Skip to main content
All icons come from one layer, @/shared/icons. A component imports an icon by its semantic name and never from lucide-react, Phosphor or Heroicons.
Import an icon

Where the glyphs come from

Icons use currentColor and draw the second tone at half opacity. Give a status icon an explicit colour such as text-success, everything else inherits the text colour. Sizes: 14px in dense chrome and tabs, 16px inline with text, 20px in dialog headers.

The map

scripts/codemods/icon-map.json maps every old icon name to a semantic name and a glyph. An entry looks like this:
icon-map.json
The solar value is a Solar name, duo:<name> for Duo Icons, ph:<name>-duotone for Phosphor or own for a drawing. Several old names may share one semantic name.

Add an icon

1

Find the glyph

Search the Solar set first, then Duo Icons, then Phosphor. Use a glyph from a later source only when an earlier one has no faithful match.
2

Add a map entry

Add the entry to icon-map.json with a semantic name that ends in Icon. For an own drawing, set "solar": "own" and add src/shared/icons/own/<Name>.tsx in the style of the existing files.
3

Generate the layer

Run node scripts/generate-icons.mjs. It writes the components and src/shared/icons/index.ts.
4

Verify

Run node scripts/generate-icons.mjs --check. It fails when a generated file differs from what it would write, so never edit the generated files by hand.
The lint check icon-imports reports any import from lucide-react, @phosphor-icons/react or @heroicons/react. Icon names stored in data keep their keys and are read through the layer.
Last modified on November 4, 2026