Files
clide/.claude/skills/ui-design/references/icons.md
T
jpmschweitzerandClaude Opus 4.8 3833a41a9e Phosphor icons: resolve by name via a generated map (T-314)
Replace the 49 hand-maintained named consts with one generated
label→codepoint map (phosphor_glyphs.g.dart, 1512 glyphs from the glyph
table via tool/gen_phosphor_glyphs.dart). Feature code now references
glyphs by their exact kebab-case name — PhosphorIcons.byName('folder') —
with no raw codepoints; this also lets a Lua extension name an icon
without crossing the FFI boundary with a codepoint.

byName is total: an unknown name degrades to the `placeholder` box so the
bug is visible (it's a real error), while phosphor_glyphs_test asserts
every byName('...') literal in lib/ resolves — recovering the typo check a
const gave. Migrated the 89 call sites. Adds EmptyIconPainter for an
intentional blank that still reserves the icon box; ClideFilterBox gains
showIcon to keep the slot aligned when blank.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 12:46:51 +02:00

3.2 KiB
Raw Blame History

Icons — Phosphor + clide-owned painters

Phosphor Icons

The app bundles Phosphor Icons (v2.0.8, MIT) as TTF fonts at assets/fonts/phosphor/ (regular, bold, fill weights).

Glyph reference: phosphor-glyphs.md — the full 1512-glyph table (codepoint · kebab name · Pascal name). Grep it for the exact kebab name; all 1512 are available — no per-icon wiring (T-314).

Using an icon

Reference a glyph by its exact kebab-case name — no codepoints in feature code; the label→codepoint table lives only in the generated lib/widgets/src/icons/phosphor_glyphs.g.dart:

ClideIcon(PhosphorIcons.byName('arrow-clockwise'), size: 13)

Or as a TabContribution icon field: icon: PhosphorIcons.byName('lightbulb').

  • An unknown name is a bug — it degrades to the placeholder error box so the mistake is visible. phosphor_glyphs_test asserts every byName('…') literal in lib/ resolves, recovering the typo check a const would give.
  • For an intentional blank that still reserves the icon's box (alignment), use const EmptyIconPainter()not a missing name.
  • Regenerate the map after a font bump: dart run tool/gen_phosphor_glyphs.dart.

Bold weight: pass family: 'Phosphor-Bold' to PhosphorIconPainter. Fill weight: family: 'Phosphor-Fill'.

Bold weight: pass family: 'Phosphor-Bold' to PhosphorIconPainter. Fill weight: family: 'Phosphor-Fill'.

clide-owned painters

Some shapes are simple enough to paint directly without an icon font. Hand-rolled ClideIconPainter subclasses live under lib/widgets/src/icons/:

  • CheckIcon, ChevronIcon, CloseIcon (x.dart)
  • DotIcon, FolderIcon, GearIcon
  • GitBranchIcon, PlugIcon, SearchIcon
  • TerminalIcon, WarningIcon

Use these for tiny, theme-aware glyphs (close ×, dropdown chevrons, status dots) where pulling in the Phosphor font weight would be overkill or where the visual needs to match the theme's stroke weight conventions.

Pattern for a new painter:

class FoobarIcon extends ClideIconPainter {
  const FoobarIcon();

  @override
  void paint(Canvas canvas, Color color) {
    final p = Paint()
      ..color = color
      ..strokeWidth = 0.10
      ..strokeCap = StrokeCap.round;
    // Coordinates are 0..1 (the painter is given a unit square).
    canvas.drawLine(const Offset(0.2, 0.2), const Offset(0.8, 0.8), p);
  }
}

Sizing

Icon sizes used in clide (subject to consolidation under ClideSpacing — see T-86):

  • 10 — micro: close × inside a tab
  • 13 — caption-row icons (sidebar, status bar)
  • 14 — standard inline icons (icon rail)
  • 16 — small icon hit-target outer container
  • 1820 — emphatic / standalone icons

Pass size: to ClideIcon; the painter receives a unit-square canvas regardless. Color defaults to globalForeground; pass explicit color: for muted/active variants.

Anti-patterns

  • Importing all of Phosphor — only declare codepoints we use.
  • Hand-painting a glyph that already exists in Phosphor at the right weight — use the font.
  • Hardcoded Color on icons — pass through the surface tokens (globalForeground, globalTextMuted, panelActiveBorder, etc.).