Redraw every icon on the pixel grid it renders at

On a 1x screen TDesign's glyphs smeared at 14 and 16px: their 24-unit
strokes land between pixels, so every ~1px line covered two at half ink.
All 37 icon/size pairs are now TDesign's shapes redrawn on their own grid
(viewBox 0 0 N N, so one unit is one CSS pixel):

- Straight lines are runs of whole pixels that end on pixel edges, with
  no caps or joins. WebKit drops square caps on a path of several
  subpaths, and Skia strokes 1px lines as hairlines that leave a joined
  corner pixel three-quarters inked.
- Slants and curves are 1.25px so they read as heavy as the crisp runs.
  Their ends, vertices and circle extremes sit on pixel centres.
- server24 keeps TDesign's 24px geometry; only its dots move onto whole
  pixels.

Only closeCircle24 (TDesign's own 24px grid) and the spinner still come
from @ng-icons/tdesign-icons. This also fixes add14 from 5d23809, whose
square-capped ends rendered half-inked in WebKit. icons.spec.ts now also
checks that every grid name's glyph uses that grid's viewBox.

Checked at 1x and 2x in WebKit (the app's macOS engine) and in Chrome
(Skia, as in WebView2 and WebKitGTK); straight runs fill whole pixels in
both. In WebKit at 1x, a copy of the tab bar, toolbar, file rows, menu
and shortcut-row markup rendered every straight-line glyph with zero
blur, including icons laid out on fractional pixels.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-10-09 13:49:42 +02:00
co-authored by Claude
parent 5d23809d48
commit 5be41db032
6 changed files with 123 additions and 90 deletions
+19 -8
View File
@@ -82,14 +82,25 @@ touching the adjacent code.
it. Per-template picks had drifted to 12 sizes, and neighbours stopped
matching: a 14.4px cog beside a 12.8px +, and a 12px + beside 9.6px × in
the shortcut chips.
- **On a 1x screen, a TDesign line through the icon's centre smears at any
small size.** At 12 or 14px the centre is a pixel boundary, so a ~1px bar
covers two pixels at half ink. A 1px stroke alone doesn't help, because the
bar stays centred on the boundary. This is geometry, not Electrobun:
headless Chrome renders the same. So the + is `add14` (`pixel-icons.ts`),
redrawn on the 14px grid with 1px bars at 7.5, which fill whole pixels at 1x
and at 2x. It's tuned for `--icon-size-sm` only. Arrow shafts (↑ →) have
the same issue if it ever shows.
- **On a 1x screen, TDesign's glyphs smear at 14 and 16px, so every icon is
redrawn on its pixel grid (`pixel-icons.ts`).** Their 24-unit strokes land
between pixels, and a ~1px line covers two pixels at half ink. This is
geometry, not Electrobun: headless Chrome renders the same. The drawing rules
each cost a debugging round:
- **Straight lines are runs of whole pixels** (`h`, `v`, `box`) that end on
pixel edges. Never rely on caps or joins:
- WebKit drops square caps on a `<path>` with several subpaths.
- Skia (WebView2, WebKitGTK) strokes 1px lines as hairlines at 1x, which
leave a joined corner pixel three-quarters inked.
- **Slants and curves (`slant`) are 1.25px**, so they read as heavy as the
crisp 1px runs. Put their ends, vertices and a circle's extremes on pixel
centres; a 45° line is cleanest where x−y or x+y is whole.
- **A glyph is exact only on its own grid**: a 14px glyph blurs at 16px, so
the name's suffix must match the size token (`icons.spec.ts`). It is also
exact only at UI scale 100/200/300%.
- **Check new glyphs at 1x in WebKit and Chrome, not resvg.** resvg skews a
shape's closing edge and leaves path corners partly inked, so its pixels
don't predict either browser.
## The interface scale (root font-size lever)