Skip to content

docs: make Icon the canonical icon entrypoint (actions B17) - #2627

Merged
camielvs merged 2 commits into
masterfrom
canon-registry/icon-usage
Aug 17, 2026
Merged

docs: make Icon the canonical icon entrypoint (actions B17)#2627
camielvs merged 2 commits into
masterfrom
canon-registry/icon-usage

Conversation

@camielvs

Copy link
Copy Markdown
Collaborator

Actions B17 in the gardening queue (#2626): blesses Icon as the canonical way to render an icon, so
the consistency pillar is allowed to migrate the stragglers instead of only proposing.

Two files, docs only. No src changes — the migration itself is a later, reviewable PR.

Why it needed a human

The empirical bar was already met — 222 files import Icon, 30 still import lucide-react
directly (88.1%, over supermajorityRatio: 0.75), and 10 of the 30 do both, which reads as drift
rather than intent. But pillars/consistency.md forbids migrating on measurement alone: without a
registry entry that would be the bot inventing a convention and enforcing it repo-wide. Hence the entry.

Non-Lucide icons are exceptions, not stragglers

Icon's name is keyof typeof icons from lucide-react, so it structurally cannot render anything
else. Recorded as KEEP so no future run "fixes" them:

  • react-icons brand/language logosFaPython ×2, SiGnubash, SiRuby, TbBrandJavascript,
    FaGoogleDrive across 3 files. Lucide dropped brand logos; there is no equivalent to migrate to.
  • src/components/ui/** — 9 files. shadcn primitives (dialog, select, sheet, command,
    calendar, checkbox, breadcrumb, date-picker) import their own glyphs and are CLI-regenerable, so
    a hand edit is overwritten by the next shadcn add. icon.tsx is the primitive.
  • Raw <svg> that isn't an icon<defs>/<marker> arrowheads in FlowCanvas/Edges/*, the
    Spinner primitive's own markup, and the bespoke resize grip in FloatingWindow.tsx.

The swap is not size-neutral, so the entry is not blanket apply: yes

Worth reading before approving, because it contradicts the "mechanical swap" framing in #2623:

iconVariants emits !w-4 !h-4. Leading-! still compiles to !important in Tailwind v4.3.3
(verified by compiling !w-4 with the project's own Tailwind: .\!w-4 { width: …; !important }), and
twMerge@3.6.0 does not dedupe !w-4 against a later size-5 — it emits both, so !important wins.
A blind <ChevronRight className="size-5" /><Icon name="ChevronRight" className="size-5" /> renders
16px instead of 20px.

Across the 30 files there are 59 Lucide JSX usages:

Bucket Count Disposition
Size class on the scale (size-4, w-5 h-5, …) 37 apply: yes — size moves to the size prop
No size class 9 apply: yes as size="xl" — Lucide's own default is width: 24 (lucide-react.js:39), so size="md" would shrink them
Off-scale (w-2 h-2 ×6, h-8 w-8, w-12 h-12) 8 apply: flag — no variant matches and className can't beat !important
Computed className={cn(…)} 5 apply: flag — size not statically readable

So ~46 of 59 usages are provably size-identical; the other 13 need a human. The off-scale ones are mostly
StatusIndicator.tsx at w-2 h-2 (8px, below xs) — if you'd rather they migrate too, the Icon scale
needs a step, which is a primitive change and deliberately not in this PR.

Also fixes a claim I got wrong

#2623 said there was "no canon-registry.md entry and no convention-skill rule naming Icon as
canonical." The registry entry was indeed missing; the convention rule was not — ui-primitives#icons
already said "Use Icon from @/components/ui/icon". That line was one sentence with no size guidance,
which is how the !important interaction went unnoticed, so this PR fills it in: the size scale, the
warning that a size className loses to the variant, and the react-icons escape hatch.

Checks

prettier --check passes on both files. No src, test, or config changes, so lint/typecheck/test
are unaffected — I did not run the full validate:test gate because its fix step rewrites the working
tree, and there is no code here for it to validate.

@camielvs
camielvs requested a review from a team as a code owner August 13, 2026 21:13
@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown

🎩 Preview

A preview build has been created at: canon-registry/icon-usage/29788cc

Comment thread .claude/skills/gardening/canon-registry.md Outdated

@morgan-wowk morgan-wowk left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Agent review. Agent-instruction markdown only (canon-registry.md, ui-primitives SKILL.md); no runtime/build surface. Spot-checked docs against icon.tsx — size scale and the !important caveat are accurate. LGTM.

camielvs commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

Merge activity

  • Aug 17, 10:44 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Aug 17, 10:45 PM UTC: Graphite rebased this pull request as part of a merge.
  • Aug 17, 10:49 PM UTC: @camielvs merged this pull request with Graphite.

camielvs and others added 2 commits August 17, 2026 22:44
Adds an `icon-usage` entry to the gardening canon-registry so the
`consistency` pillar can migrate the 30 files that still import
`lucide-react` directly, and fills in the one-line `ui-primitives#icons`
rule the entry cites.

`Icon` only accepts Lucide names, so brand and language logos from
`react-icons`, the shadcn primitives under `src/components/ui`, and raw
`<svg>` that is not an icon are recorded as exceptions to keep rather
than deviations to migrate.

Migration is size-sensitive: `iconVariants` emits `!w-* !h-*` with
`!important` and `twMerge` does not dedupe that against a later
`size-*`, so a bare swap silently resizes the glyph. The entry marks
only on-scale sizes as auto-appliable and flags off-scale and computed
classNames for a human.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
"or the w-*/h-* pair" read as satisfied by either one alone, which would
let an automated run swap <ChevronRight className="w-4" /> — that renders
16x24 today (width from the class, height from Lucide's own attribute), so
any size prop changes it. Splits it into its own flag bucket.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@camielvs
camielvs force-pushed the canon-registry/icon-usage branch from c1b555b to 29788cc Compare August 17, 2026 22:44
@camielvs
camielvs merged commit 6e294a7 into master Aug 17, 2026
16 checks passed
@camielvs
camielvs deleted the canon-registry/icon-usage branch August 17, 2026 22:49
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.

2 participants