Skip to content
Monogem

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. currentColor ties 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

TokenValueNote
--icon-stroke-width1.5Lucide'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 thisUsepx
Icon size — inline with text--scale-416
Icon size — default UI (buttons, inputs, nav)--scale-520
Icon size — standalone (empty states, features)--scale-624
Icon-only button tap target--scale-1040

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-width on every icon; don't vary it per instance.
  • Pick size by context: sm inline with text, md for default UI, lg for standalone icons.
  • Let icons inherit currentColor from their surrounding text.
  • Give icon-only buttons a 40px (--scale-10) minimum tap target via padding, even when the icon itself is sm or md.

Don’t

  • Hand-set a one-off stroke width on a single icon.
  • Reach for lg on 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.