Skip to content

feat: control icon stroke width with a CSS variable - #112

Draft
korvin89 wants to merge 2 commits into
mainfrom
feat/icon-stroke-width
Draft

korvin89 wants to merge 2 commits into
mainfrom
feat/icon-stroke-width

Conversation

@korvin89

Copy link
Copy Markdown

What

Regular icon components now take their stroke width from the --g-icon-stroke-width CSS variable:

.app {
    --g-icon-stroke-width: 2;
}
<Icon data={Gear} style={{'--g-icon-stroke-width': 1}} />
  • The default is 1.5, the width icons are drawn with, so without the variable icons look as before.
  • Any value from 1 up works, on an icon or any ancestor. Since it is CSS, it passes through uikit's Icon (which doesn't forward props to the icon component), covers uikit's own icons, and can change at runtime.
  • -fill icons, accent paths (the red dot in *-dot icons) and files in svgs/ are unchanged and ignore the variable.

How it works: icons are 1.5px strokes with round joins, outlined in Figma. npm run src:icons shrinks the outline of each regular icon to a 1px stroke (Skia path ops via pathkit-wasm, a new dev dependency, see scripts/stroke-width.mjs) and adds a currentColor stroke on top whose width is variable − 1. Shrinking an outline by half of the width difference equals re-stroking its centerline, so the stroke grows it back to the requested width: by 0.25 per side at 1.5 (the original look), by 0.5 at 2.

Seven icons lose details at 1px (card-club, flame, logo-docker, logo-rustore, logo-yandex-messenger, paper-plane, pin-slash). They keep their 1.5px outline and only get bolder.

A new Icons / Weights story shows icons at 1, 1.5 and 2px side by side, and the whole set at 1–2px.

Why

A design proposal to switch icon stroke width between 1px, 1.5px (default) and 2px without redrawing icons by hand.

Notes for review

  • Default look. All regular icons were compared with the originals on a 16× raster: identical up to antialiasing noise. The largest difference, at the slash cut corners of eye-slash, is 0.13 px² at 16px.
  • Not checked automatically. At 2px some icons nearly or fully close gaps (e.g. person, calculator, circle-letter-g), see the story. A new icon that breaks at 1px has to be added to the list in scripts/stroke-width.mjs.
  • Edges. Above 1.5px, icons drawn up to the edge of the viewBox are clipped there by up to (width − 1.5) / 2.
  • Package size. Unpacked 2.49 → 3.64 MB, tarball 0.36 → 0.45 MB, as the 1px outlines take longer path data. The number of files is the same.
  • Compatibility. Components now carry stroke attributes, so consumers recoloring icons with CSS fill instead of color would get a stroke of another color. Should this be released as a breaking change?
  • feat: sync icons #111 changes CircleLetterD and CopyCheckXmark; whichever PR is merged second needs npm run src:icons again.

@gravity-ui

gravity-ui Bot commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Preview is ready.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant