An icon set: Lucide, delivered as inline SVG through a template tag #8
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Blocks
Reference
Postulo/postulo#8
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Observation
This issue is the icon set and how it reaches a template. The switch itself is the next issue, which depends on this one.
What exists today
Postulo has no icons. The whole template tree holds exactly one
<svg>, and it is a bar chart (applications/insights.html). Navigation, buttons and status are all words — deliberate at 0.1.0, but a header switch needs a sun and a moon, and #4's channels, #7's avatar and the board will all want a glyph or two. Better to choose once than to paste SVGs one at a time.The candidates
Checked at the registries on 2026-09-05, not from memory:
currentColor. The default set of shadcn/ui and of most current Tailwind projects; pushed the day before this was written.Proposal: Lucide
"Most widespread" has two honest answers. Font Awesome is the most installed, but it is a freemium product, and a project that will never paywall a feature should not draw its interface from a set whose full catalogue sits behind one. Lucide is the most adopted of the fully open sets in current web projects, and the most actively maintained.
stroke="currentColor"means every icon takes the surrounding text colour, so the existingdark:variants colour icons for free.sun,moon,sun-moon,monitor,laptop.lucide-staticalready carries<!-- @license lucide-static v1.41.0 - ISC -->in its first line, which is all the credit the licence asks for. A line in the README's credits does not hurt.How an icon reaches a template
Three ways, in order of preference.
{% icon "sun" class="size-4" %}readsstatic/icons/sun.svg, cached at first use. Only the icons Postulo uses are committed, copied from thelucide-staticnpm package by async:iconsscript besidebuild:css— the same arrangement as the compiled stylesheet: Node is needed to change it, never to run it. Inline SVG is untouched by the CSP, inheritscurrentColor, is swapped by htmx like any markup, and isaria-hidden="true"by default witharia-labelwhen the tag is given a label, so an icon-only button is still announced. A dozen files of 400 bytes, no runtime dependency, no new Python dependency; the tag is thirty lines.heroicons(Adam Johnson; Django 6.0 in its classifiers),django-lucide(1.3.1, February 2026; Python 3.14 in its classifiers) orlucide(1.1.4, July 2026; classifiers stop at Django 5.0). Convenient, but a runtime dependency on a small third-party package to ship ten SVGs, on its author's release cadence. Not worth it while the tag is thirty lines.@iconify/tailwind4(1.2.3, MIT) with@iconify-json/lucide: classes likeicon-[lucide--sun]generated at CSS build time, the SVG embedded as amask-imagedata URI. The production CSP allows it —img-src 'self' data:inprod.py— and it needs no template code, and it opens every Iconify set. But a mask is single-colour, cannot carry an accessible name without extra markup, and grows the committed stylesheet. Overkill for a handful of icons.Proposal: the first.
Rules that come with it
title.size-4inline with text,size-5in buttons,size-6or more in empty states; the stroke width stays at Lucide's default 2.{% icon "sun" %}, not{% lucide "sun" %}, so a change of set is one directory swap and no template edit.Where it lands first
The theme switch. Then, cheaply and only where a glyph carries meaning: the alerts (info, success, warning, error), the status pills on the board, empty states, the navigation.
Classification
Enhancement. Not breaking: no schema, no setting, nothing an operator has to do.
Open questions
lucide-staticsprite — one file of about a megabyte, any icon available without a build step, and a megabyte on every first visit?lucide-staticversion somewhere a bump shows as a diff? The SVG header already does (@license lucide-static v1.41.0), so a bump touches every file; that is probably enough.