Icons
The icon set is a brand decision, not a dependency. The system declares which roles exist, the brand draws them, and no component ever names an icon library.
import from ~/system/icons (contract) · ~/brand/icons (glyphs)
The icon set is a brand decision
This app draws its own icons and depends on no icon library. That is possible because the design system does not name one. The system declares which icon ROLES exist, in src/system/icons.ts, and the brand supplies the glyphs, in src/brand/icons.tsx. It is the same seam as color: the contract says which, the brand says what.
The design-system components import icons.next, never ChevronRight. So re-iconing this site means editing one file, and no component knows it happened.
Roles are named for their job, never for their picture. A brand that expresses "expand" with a plus sign is still a legal brand, and nothing downstream has to care.
House style
square caps, mitred joins, 2px
Cut from the same geometry as the hero's 4px rules and its X/lines grid. Lucide's rounded terminals fight that, which is the other reason these are drawn here.
The contract — 14 roles
expand
an accordion or select, opening
next
forward: breadcrumb separator, pagination
previous
back
ellipsis
a truncated range
check
a chosen item
selected
the radio's filled marker
close
dismiss a dialog, sheet, toast
trendUp
a metric rose
trendDown
a metric fell
trendFlat
a metric did not move
success
it worked
error
it failed
warning
proceed carefully
info
context, not a problem
icons.*
Every role the design system consumes. Miss one and the build fails: the registry is typed, so a missing icon is a compile error rather than a blank space in production.
Brand glyphs — not the contract
glyphs.*
The extra pictures this project needs for its own UI and documentation. The design-system components never reach for these, which is why they are a separate export.
Sizes
<Icon icon={glyphs.search} size='xs|sm|md|lg'>
A closed set of four. Color is inherited through currentColor, so an icon follows the text token around it and never carries a color class of its own.
Color is inherited
currentColor
The same icon on four text tokens. No icon ever carries its own color class.
Inside a component
Button sizes its own icons
Button already sets the glyph size. The call site passes the icon and nothing else.
An icon-only control has no visible label, so it needs an aria-label. The glyph is not a name.
Rules
Do
Don't