Skip to main content

sfnext-create-component

Author a new Storefront Next design-system component the right way — pick the correct layer (primitive vs composite), reuse-or-extend before creating, define CVA variants bound to semantic tokens, add data-slot attributes, satisfy the accessibility checklist, and ship a Storybook story. Use when asked to create, add, or build a new UI component, button/badge/tile/card variant, or design-system primitive. This is the design-system authoring discipline; for createPage/Suspense/shadcn page-rendering patterns see sfnext-components.

Source facts

Repository
SalesforceCommerceCloud/b2c-developer-tooling
Last source activity
June 11, 2026 at 14:32
Detected SKILL.md language
English
Stars
54
Forks
21

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
6 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
sfnext-create-component
description
Author a new Storefront Next design-system component the right way — pick the correct layer (primitive vs composite), reuse-or-extend before creating, define CVA variants bound to semantic tokens, add data-slot attributes, satisfy the accessibility checklist, and ship a Storybook story. Use when asked to create, add, or build a new UI component, button/badge/tile/card variant, or design-system primitive. This is the design-system authoring discipline; for createPage/Suspense/shadcn page-rendering patterns see sfnext-components.
# Create a Component Skill This skill is the authoring discipline for adding a new component to a Storefront Next storefront: choosing the right layer, reusing before creating, and meeting the token / a11y / Storybook requirements that keep the design system consistent. > **It complements `sfnext-components`.** That skill covers engineering patterns — `createPage`, Suspense/`Await` streaming, shadcn/ui setup, the `cn()` utility. **This** skill covers design-system authoring — the layer model, the extend-before-create gate, CVA variants bound to semantic tokens, `data-slot`, accessibility, and story coverage. Use both together when building UI. ## Before you start — scan first (extend before create) Do not create a new component file until you have run this scan: 1. Search `src/components/` for components with a similar name or behaviour. 2. Search `src/components/ui/` for Shadcn primitives — run `npx shadcn@latest add <name>` to check whether it exists before hand-rolling one. 3. Apply the REUSE / EXTEND / CREATE decision: - **REUSE** — an existing component already does this → use it as-is. - **EXTEND** — an existing component is close → add a new CVA variant (cheaper than a new file). - **CREATE** — nothing exists → proceed with this skill. See [Component Authoring Reference](references/COMPONENT-AUTHORING.md) for the full layer model and the extend-before-create rule. ## Step 1 — Decide the layer | Question | Layer | Location | |----------|-------|----------| | Pure UI element, no data or business logic? | **Primitive** | `src/components/ui/` (check shadcn first) | | Assembles primitives or takes data props? | **Composite** | `src/components/` | For primitives, always check shadcn before hand-rolling: ```bash # from packages/template-retail-rsc-app npx shadcn@latest add <component-name> ``` Shadcn components land in `src/components/ui/` already wired to the token system. If it exists in shadcn, use it — do not hand-roll a replacement. ## Step 2 — Create the file with the copyright header ``` src/components/<feature>/<component-name>.tsx ← preferred (feature-grouped) src/components/<component-name>.tsx ← acceptable for standalone components ``` Start every `.ts`/`.tsx` file with the Apache 2.0 copyright header (ESLint enforces it), then imports: ```tsx import * as React from 'react' import { cva, type VariantProps } from 'class-variance-authority' import { cn } from '@/lib/utils' // Import primitives from src/components/ui/ — not from @radix-ui directly import { Button } from '@/components/ui/button' ``` ## Step 3 — Type the props explicitly ```tsx interface ProductTileProps { productId: string name: string price: number imageUrl: string currency?: string className?: string } ``` No `any`. Extend `React.ComponentProps<'element'>` for native passthrough. Use discriminated unions for mutually exclusive prop shapes. ## Step 4 — Define CVA variants (if the component has visual states) ```tsx const productTileVariants = cva( 'relative flex flex-col overflow-hidden rounded-md bg-card text-card-foreground', { variants: { density: { comfortable: 'gap-4 p-4', compact: 'gap-2 p-2', }, }, defaultVariants: { density: 'comfortable' }, } ) ``` **Token rules:** every class that affects color MUST use a semantic token utility (`bg-primary`, `text-foreground`, `border-border`, …). No raw hex, no `bg-black`, no `text-gray-500`. See [Token System Reference](references/TOKEN-SYSTEM.md). ## Step 5 — Write the component ```tsx export function ProductTile({ productId, name, price, imageUrl, currency = 'USD', className, density, ...props }: ProductTileProps & VariantProps<typeof productTileVariants>) { return ( <article data-slot="product-tile" data-product-id={productId} className={cn(productTileVariants({ density }), className)} {...props} > <div data-slot="product-tile-image">{/* image */}</div> <div data-slot="product-tile-body" className="flex flex-col gap-1"> <span data-slot="product-tile-name" className="text-sm font-medium text-foreground"> {name} </span> <span data-slot="product-tile-price" className="text-sm text-muted-foreground"> {new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(price)} </span> </div> </article> ) } ``` Required on every component: - `data-slot` on the root and each named region. - `className` accepted and merged via `cn()` **last** so callers can override. - Native props spread via `{...props}`. ## Step 6 — Create the Storybook story Every new component in `src/components/` needs a `.stories.tsx` — one story per CVA variant plus a `Snapshot` story for CI diffing. ```tsx import type { Meta, StoryObj } from '@storybook/react' import { ProductTile } from './product-tile' const meta: Meta<typeof ProductTile> = { title: 'Components/Product/ProductTile', component: ProductTile, } export default meta type Story = StoryObj<typeof ProductTile> const defaultProps = { productId: 'P001', name: 'Classic Shirt', price: 89.99, imageUrl: '/placeholder.jpg' } export const Default: Story = { args: defaultProps } export const Compact: Story = { args: { ...defaultProps, density: 'compact' } } export const Snapshot: Story = { name: 'Snapshot', render: () => ( <div className="grid grid-cols-2 gap-4"> <ProductTile {...defaultProps} density="comfortable" /> <ProductTile {...defaultProps} density="compact" /> </div> ), } ``` ## Step 7 — Accessibility check - [ ] Interactive elements are `<button>` or `<a>`, never `<div onClick>`. - [ ] All form inputs have `<Label htmlFor>`. - [ ] Focus ring: `focus-visible:ring-ring focus-visible:ring-[3px]`. - [ ] No color-only information (pair with icon or text). - [ ] `aria-label` on icon-only buttons. ## Verification Before marking the task complete, run from the monorepo root: ```bash pnpm typecheck pnpm lint pnpm test-storybook:snapshot:agent ``` All three must pass with zero errors and zero warnings. Lint runs with `--max-warnings 0`. ## Related Skills - `storefront-next:sfnext-components` - createPage HOC, Suspense/Await, shadcn/ui, Tailwind page-rendering patterns - `storefront-next:sfnext-testing` - Vitest unit tests and Storybook interaction/a11y tests - `storefront-next:sfnext-create-vertical` - Brand theming via the token layer - `storefront-next:sfnext-extensions` - Injecting a component into a page via UITarget ## Reference Documentation - [Component Authoring Reference](references/COMPONENT-AUTHORING.md) - Layer model, CVA, data-slot, extend-before-create - [Token System Reference](references/TOKEN-SYSTEM.md) - Semantic token classes and dark mode rules - [Project Structure Reference](references/PROJECT-STRUCTURE.md) - Where components live in the monorepo - [Troubleshooting Reference](references/TROUBLESHOOTING.md) - Token, lint, and snapshot pitfalls
View on GitHub