Icons
Overview
Icons are Lucide. This is the last foundation, and it's a hybrid of two
patterns you've already seen: icon sizes borrow the master scale directly, the same
move Spacing made — no new size tokens. Stroke width is a genuinely new value the
scale doesn't cover, so — like Shadows — it gets exactly one new token. Color ships
nothing new at all: icons use currentColor.
Source of truth: tokens/primitives.css in the Monogem codebase.
Principles
- Borrow the scale for size. An icon's width and height are just scale steps — no parallel sizing system.
- One new value, one new token. Stroke width isn't on the scale, so it's the one thing this foundation actually ships.
- Icons inherit meaning from text.
currentColorties an icon's color to whatever semantic decision was already made for the text next to it. - Small icon, full-size target. An icon-only control can look small and still be easy to tap.
Tokens
| Token | Value | Note |
|---|---|---|
--icon-stroke-width | 1.5 | Lucide's own default is 2; lighter reads more refined here |
Sizes and tap target are recipes — they reference tokens that already exist, nothing new:
| Building this | Use | px |
|---|---|---|
| Icon size — inline with text | --scale-4 | 16 |
| Icon size — default UI (buttons, inputs, nav) | --scale-5 | 20 |
| Icon size — standalone (empty states, features) | --scale-6 | 24 |
| Icon-only button tap target | --scale-10 | 40 |
Rationale
Why does Icons ship a token when Spacing didn't, but not as many as Radius? Stroke width is a
genuinely new value — not a length on the scale, not spacing, not sizing. That's exactly the
situation Shadows was in, so it gets the same treatment: one new value, one new token. Size,
though, is exactly what --scale-4/5/6 already are — an --icon-size-md alias would just be a
second name for the same number, so it ships as a recipe instead, the same restraint Spacing used.
Why 1.5 instead of Lucide's default of 2? A lighter stroke sits better against this system's neutral, onyx-leaning palette — 2 reads slightly heavier than the type and color foundations around it.
Why currentColor, not --icon-color-* tokens? An icon inside a destructive button should
turn red because the button's text is red — not because a second, parallel token was also set.
currentColor makes that automatic, so an icon never disagrees with the label next to it.
Why note a 40px tap target instead of shipping a token for it? --scale-10 already exists
(Scale lists it as "small button height"). This is a usage rule — pad a small icon out to a full
target — not a new value.
Do / Don’t
Do
- Set
--icon-stroke-widthon every icon; don't vary it per instance. - Pick size by context:
sminline with text,mdfor default UI,lgfor standalone icons. - Let icons inherit
currentColorfrom their surrounding text. - Give icon-only buttons a 40px (
--scale-10) minimum tap target via padding, even when the icon itself issmormd.
Don’t
- Hand-set a one-off stroke width on a single icon.
- Reach for
lgon an icon sitting inline with body text — it'll overpower the line. - Add a dedicated icon fill/stroke color — icons take color from their text context, not their own token.
- Ship an icon-only control smaller than 40px of clickable area.