Standardizes icon work in this Trainm8 repo (Epic Stack + shadcn). Covers Tabler-first and Hugeicons-fallback via Sly CLI, SVG sprites with the shared Icon component, and cleanup after shadcn generates icon imports (`lucide-react` or `@tabler/icons-react`). Use whenever the user adds or changes icons, runs `npx shadcn add`, runs `npx shadcn init` (including `--preset`), edits `components.json`, mentions lucide/tabler icon imports, sprites, Sly, Tabler, Hugeicons, Radix icons, or `vite-plugin-icons-spritesheet`—even if they only ask to "add a component".
If the request is unrelated to icons, shadcn-generated icon imports
(lucide-react or @tabler/icons-react), the SVG sprite, or Sly, do not
drag in this workflow—handle the actual task (data, routing, tests, etc.)
instead.
Use this skill when the task involves:
lucide-react or @tabler/icons-react)other/svg-iconsother/sly/sly.jsonother/sly/transform-icon.tsapp/components/ui/icons/sprite.svgapp/components/ui/icon.tsxtypes/icon-name.d.tscomponents.json (currently iconLibrary: "lucide")Icon component in app code.components.json may not fully control preset output. In practice, shadcn flows
can generate imports from either lucide-react or @tabler/icons-react
(especially with npx shadcn init --preset ...). This repo’s standard is the
sprite Icon for app-level consistency.
After adding a shadcn component:
npx shadcn@latest add <name> or
npx shadcn@latest init --preset <id> --base <base> --template react-router.lucide-react@tabler/icons-reactother/svg-icons
(same basename you will use in Icon name).<Icon name="kebab-name" /> (and aria-hidden /
labels as needed).npm run build so the sprite and IconName types stay correct.Icon for consistency.components.json maps ui → app/components/ui,
@/… for utils as configured.iconLibrary in components.json unless the team explicitly
standardizes on something else; post-process generated files instead so
CLI keeps working predictably.icon: ChevronRight),
refactor to icon: () => <Icon name="chevron-right" /> or pass a small
wrapper—match the consuming API.@tabler/icons-react, treat it as generated code to
normalize: keep the glyph choice, but migrate rendering to sprite Icon
unless there is a clear reason not to.Recommended interactive flow:
npx sly add
Direct flow:
npx sly add <library> <icon-name>
npx sly add <library> <icon-a> <icon-b>
npx sly add <library> <icon-name> --yes --overwrite
If you are unsure about the exact registry library name for Tabler/Hugeicons, run interactive mode and select the correct library from the list.
Keep other/sly/sly.json aligned to this policy:
./other/svg-iconstransform-icon.ts transformer enabledUse Tabler as the default selected library during installs.
After adding SVGs:
npm run build
This regenerates the sprite/types used by Icon.
Always render icons with:
import { Icon } from '#app/components/ui/icon.tsx'
Examples:
<Icon name="trash" />
<Icon name="plus">Add note</Icon>
<Icon name="check" aria-hidden="true" />
kebab-case SVG file names.Icon name equals file name without .svg.aria-hidden="true".aria-label),
optionally add title on Icon.npm run buildother/svg-iconsIcon name matches file namelucide-react or @tabler/icons-react is
installedIcon and remove unused importsUse EVALS.md and evals/evals.json for trigger smoke tests. Eval artifacts
and the static review HTML live under icon-workflow-workspace/.