07 · Icons

One family, hairline weight

Fifty-six icons drawn to the frame's own line weight. Click any of them to copy its SVG.

01

Rules

Box
viewBox="0 0 24 24", always. Artwork sits on a 24px grid with roughly 2px of clear space, so icons optically match at any size.
Stroke
fill: none; stroke: currentColor. Never a filled or duotone icon — the weight is the point, and a filled glyph is heavier than every line on the page.
Weight
1.4 at default size; 1.5 small, 1.2 large, 1 for anything above 2rem and for the notch chevrons. The .icon classes handle this — do not set stroke-width per icon.
Joins
stroke-linecap: round; stroke-linejoin: round. The only rounded thing in the system, and only because a butt cap at 1.4px reads as a chip in the line.
Colour
currentColor and nothing else. An icon inherits from its context — muted in chrome, accent when the row is active. Never a hardcoded fill.
Mixing sets
Don't. A third-party set at a heavier stroke sits half a weight above the frame and looks like a mistake even when nobody can say why.
02

The set

Click to copy. Each one comes back as a complete <svg class="icon"> element, ready to paste.

Interface
Action
Status
Data
Domain
03

Sizes

.icon .sm / default / .lg / .xl / .hair
.sm · 0.85rem default · 1.05rem .lg · 1.5rem .xl · 2.2rem .hair · notches

The stroke weight steps down as the icon grows. A 1.4px stroke that looks right at 17px looks coarse at 35px — this is the same reason headings are set at weight 300 while small labels are at 400.

In context
Verified Updated 4 min ago
04

Drawing a new one

The set will not cover everything. When you add to it, match these and it will sit down with the rest.

  • Draw on the 24px grid at whole and half pixels. A path at x=7.3 renders blurry at 17px.
  • Keep the extremities at 3 and 21 — roughly 2px of clear space inside the box. An icon that fills its box looks larger than its neighbours.
  • Use one path where the shape allows, and combine sub-paths with M rather than adding elements: d="M4 7h16M4 12h16".
  • No fill, no stroke-width, no stroke on the paths — all three come from .icon.
  • Test at 0.85rem before you commit. Detail that vanishes at small size is detail that should not be there.
  • Give it an aria-hidden="true" when a text label sits beside it, and an aria-label on the button when it does not.
<!-- decorative: the label carries the meaning -->
<button class="btn ghost">
  <svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3v12M7 11l5 5 5-5M4 20h16"/></svg>
  Export
</button>

<!-- icon only: the button carries the meaning -->
<button class="icon-btn" aria-label="Export">
  <svg class="icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3v12M7 11l5 5 5-5M4 20h16"/></svg>
</button>