Builds a new reusable Chakra UI primitive under src/ui from a reference image (layout, typography, color), verifies light and dark themes with semantic tokens, and adds a colocated Storybook story. Use when the user attaches or links a design image for a new generic component, asks to match a mockup in src/ui, or wants a Card-style shared UI building block from a screenshot.
Install
npx skillscat add slashieapp/web/ui-component-from-image Install via the SkillsCat registry.
UI component from image
When this applies
The user supplies a reference image (mockup, screenshot, or design frame) and wants a new general-purpose component in src/ui/, reusable like Card, correct in light and dark theme, aligned to Slashie design, with a Storybook story.
Before coding
- Read the image with the image-capable read path so layout, hierarchy, spacing, and states are grounded in the asset.
- Read .cursor/skills/slashie-design/SKILL.md for tokens, typography, surfaces, borders, motion, and a11y expectations.
- Infer the component API: prefer composition (
children, optional named slots via props) and extending the narrowest Chakra props type (BoxProps,StackProps, etc.) so callers can forward layout props—mirror the spirit ofsrc/ui/Card/Card.tsx.
File layout (match existing src/ui primitives)
Create a folder per component:
src/ui/<ComponentName>/
<ComponentName>.tsx
<ComponentName>.stories.tsx- Use PascalCase for the folder and primary export name.
- Add
'use client'only if the implementation needs client-only APIs (hooks, events beyond static markup).
Implementation rules
Stack and styling
- Build with Chakra UI v3 primitives (
Box,Stack,Text,Button, etc.) consistent with the rest ofsrc/ui. - Colors and surfaces must come from semantic tokens defined for both themes in
src/theme/system.ts(lightConfig/darkConfig)—for examplecardBg,cardFg,formControlBg,bg,fg,intent*/badge*tokens, etc. (seesrc/theme/cardPalette.ts,src/theme/formPalette.ts, andsrc/theme/intentPalette.tsfor grouped palettes). - If the mockup needs a color that has no semantic token, add a paired semantic entry in both
lightConfiganddarkConfig(and document the token name in the component props or JSDoc if non-obvious). - Follow Slashie no-line rule by default: avoid 1px borders for sectioning; use tonal surfaces and spacing. Use borders only when required for clarity or a11y, and prefer tokenized borders (e.g. patterns used by
Card:cardBorder,cardBg). - Typography: headings →
fontFamily="heading"where appropriate; body → default body. Respect scale and rhythm from the slashie skill. - Contrast: aim for WCAG AA in both themes for text and interactive states.
Generality and reuse
- Prefer one component with props (
variant,size, optionalicon,isDisabled, etc.) over forking duplicate markup. - Expose
children(and optional render props or subcomponents) so the same shell works across product screens. - Reuse existing
src/uipieces where they already encode the pattern; do not copy-paste large chunks from feature routes intosrc/ui.
Reuse src/ui before inventing markup
When a mockup shows common UI atoms (badges, tags, buttons, icon buttons, cards, form fields):
- Search
src/ui(list folders undersrc/ui/or grep exports) for an existing component that matches the role. - If it exists, import and use it—extend via props only when the design needs a small delta (e.g.
borderRadius,fontFamilyonBadge). Do not recreate the same pill/chip with rawBox+Textunless nothing insrc/uifits and adding a primitive is intentional. - Imports: from another
src/uimodule, use a relative path to the concrete file (e.g.../Badge/Badge), not@ui, in both implementation and colocated stories.
Examples in this repo: Badge for metadata-style pills; Tag for status/chip patterns; Button / IconButton for actions; Card for elevated surfaces.
Imports and barrels
- Inside
src/ui/**, use relative imports to sibling or nested files. Do not import from@uiin component or colocated story files (avoids circular barrel issues). See repo rule dual-theme-storybook-ui.
Storybook
- Follow .cursor/skills/storybook-story-conventions/SKILL.md for skeleton,
Meta/StoryObjimport from@storybook/nextjs-vite,tags: ['autodocs'], andparameters.layout(paddedfor widgets,fullscreenfor page-like blocks). - Title:
ui/<ComponentName>(e.g.ui/Card). - Stories should use the same semantic tokens as production so the Storybook theme toolbar shows correct light and dark without extra wrappers.
- Do not wrap the story in a hardcoded hex background to simulate dark mode; rely on preview + tokens.
After files exist
- Run
bun run exports-gensosrc/ui/index.tspicks up the new module (registered inscripts/generate-exports.ts). - Run
bun run lintand fix any issues.
Verification checklist
- Component lives under
src/ui/<ComponentName>/with a clear, typed public API. - Common atoms (badge, tag, button, etc.) reuse existing
src/uicomponents where applicable—not reimplemented ad hoc in stories or implementation. - No hardcoded theme-specific hex in component or story (except rare documented exceptions).
- Light and dark both look intentional; interactive states visible.
- Colocated
*.stories.tsxwithui/<Name>title and autodocs. - Exports regenerated; lint clean.
Reference implementation
Use src/ui/Card/Card.tsx and src/ui/Card/Card.stories.tsx as the minimal shape reference (thin wrapper, Chakra props spread, colocated story)—not as a mandate to copy card tokens for every new component.