Icons

Icons are inline SVGs rendered by app/helpers/icon_helper.rb, sourced from Lucide (ISC license — free to use, no attribution required). There is no icon font, no icon webfont/CDN, and no JS icon library — this replaced a FontAwesome Kit CDN script that used to load on every page (see PR #339).

Using an existing icon

<%= icon(:trash-2) %>                          <%# decorative, paired with visible text - aria-hidden %>
<%= icon(:"trash-2", label: t("Delete")) %>     <%# icon-only control - needs an accessible name %>
<%= icon(:bookmark, filled: true) %>            <%# solid variant, e.g. a toggled/active state %>
<%= icon(:bookmark, size: 32, class: "foo") %>  <%# override size (default 20) / add classes %>

Every icon renders with the class icon icon-<name> (plus icon--filled when filled: true), so CSS can target .icon generically or a specific .icon-<name> variant. Icons need a symbol/string key matching one of the names in IconHelper::ICONS — see that file for the current list.

Rules for the label:/decorative choice: if the icon sits next to visible text (a button labelled “Delete” with a trash icon beside it), leave label off — the icon is decorative and gets aria-hidden="true" automatically. If the icon is the only content of a link/button (a bare close “x”, a bare bookmark toggle), always pass label: with a real accessible name — icon-only controls with no label are invisible to screen readers.

Adding a new icon

  1. Find the icon at lucide.dev/icons and note its kebab-case name (e.g. circle-check).
  2. Fetch the raw SVG source directly (don’t guess the path data from memory — copy it exactly):
    curl -s https://raw.githubusercontent.com/lucide-icons/lucide/main/icons/circle-check.svg
    
  3. Copy everything inside the <svg> tag (the <path>/<circle>/etc. elements only — not the outer <svg ...> wrapper, since icon_helper.rb supplies that itself with the project’s standard attributes).
  4. Add an entry to the ICONS hash in app/helpers/icon_helper.rb, keyed by the same kebab-case name as a symbol:
    "circle-check": '<circle cx="12" cy="12" r="10" /><path d="m9 12 2 2 4-4" />',
    
  5. Use it: <%= icon(:"circle-check") %>.

All Lucide icons share the same 24x24 viewBox and stroke-based style (stroke="currentColor", stroke-width="2", round caps/joins), so a new icon’s paths drop in without needing per-icon attribute tweaking. If an icon needs to support a filled/solid state (like bookmark’s toggle between saved and not-saved), check that its shape is a single closed path first — Lucide icons made of multiple thin stroke paths (e.g. bell’s clapper, circle-x’s X) can look wrong with fill: currentColor applied, since the fill and stroke share the same color and thin strokes can visually disappear into a solid fill. icon(name, filled: true) sets fill="currentColor" on the whole <svg>, so test the rendered output before relying on it for multi-path icons.


This site uses Just the Docs, a documentation theme for Jekyll.