Skip to main content

sfnext-components

Build and modify UI components in a Storefront Next storefront: page components with Suspense and use()/Await, createPage and withSuspense, Tailwind v4 token classes, shape tokens (rounded-ui, shadow-ui, border-ui), shadcn/ui primitives in src/components/ui (sync-shadcn), CVA variants, data-slot, DynamicImage, lazy-loaded overlays, and Storybook stories with snapshot tests. Use when creating or editing a component in src/components, adding a shadcn primitive, fixing styling that ignores the theme, writing a *.stories.tsx file, or running pnpm storybook:test. Do not use for re-branding colors, fonts, or logos (use `storefront-next:sfnext-theming`), route loaders (use `storefront-next:sfnext-data-fetching`), or Page Designer registration (use `storefront-next:sfnext-page-designer`).

소스 정보

저장소
SalesforceCommerceCloud/b2c-developer-tooling
최근 소스 활동
2026년 10월 4일 16:40
감지된 SKILL.md 언어
영어
스타
56
포크
22

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

파일 탐색기
7 개 파일

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
sfnext-components
description
Build and modify UI components in a Storefront Next storefront: page components with Suspense and use()/Await, createPage and withSuspense, Tailwind v4 token classes, shape tokens (rounded-ui, shadow-ui, border-ui), shadcn/ui primitives in src/components/ui (sync-shadcn), CVA variants, data-slot, DynamicImage, lazy-loaded overlays, and Storybook stories with snapshot tests. Use when creating or editing a component in src/components, adding a shadcn primitive, fixing styling that ignores the theme, writing a *.stories.tsx file, or running pnpm storybook:test. Do not use for re-branding colors, fonts, or logos (use `storefront-next:sfnext-theming`), route loaders (use `storefront-next:sfnext-data-fetching`), or Page Designer registration (use `storefront-next:sfnext-page-designer`).
# Storefront Next Components How to write, style, and verify components in your storefront. Your project already ships the rules in `AGENTS.md`, `docs/README-UI-STYLING.md`, `docs/README-SHAPE-TOKENS.md`, `docs/README-SUSPENSE.md`, and `docs/README-STORYBOOK.md`; this skill is the task-oriented path through them. ## Before you create anything 1. Search `src/components/` for an existing component (`ls src/components`). Extend or compose it before adding a new one. 2. Decide the layer: - **Primitive** (Button, Card, Dialog): a shadcn fork in `src/components/ui/`. Only shadcn forks live there. - **Composite / domain component**: everything else, in `src/components/<kebab-name>/`. - **Optional feature**: a folder under `src/extensions/` (see `storefront-next:sfnext-extensions`). 3. Match the folder shape already used by neighbors: ``` src/components/my-widget/ ├── index.tsx # implementation (default or named export) ├── index.test.tsx # Vitest + Testing Library └── stories/ ├── index.stories.tsx # Storybook (stories/ subfolder is required) ├── my-widget-snapshot.tsx # optional snapshot fixture └── __snapshots__/ # generated baselines, commit them ``` Every `.ts`/`.tsx` file needs the Apache header enforced by the `custom/header-format` lint rule. Copy it from any existing file (or `AGENTS.md`). ## Styling rules - Tailwind utility classes only; merge with `cn()` from `@/lib/utils`. No CSS modules, no per-component CSS files, no `style={{}}` (a truly dynamic API-driven value is the only exception). - Global/theme CSS lives in `src/theme/` only (entry `src/theme/index.css`). There is no `src/app.css`. - Use semantic token classes, never hard-coded colors. The `custom/color-linter` rule fails `pnpm lint` on raw palette classes such as `text-white` or `bg-red-500`; use `text-primary-foreground`, `text-destructive-foreground`, `bg-card`, `text-muted-foreground`, `border-border`. - Header/footer chrome has its own classes: `bg-header-background`, `text-header-foreground`, `bg-header-menu-background`, `bg-footer-background`. Do not write `bg-[--header-background]`. - A new token needs two edits: the value in `src/theme/tokens/*.css` and a `--color-x: var(--x)` line inside `@theme inline` in `src/theme/tailwind.css`. See [TOKEN-SYSTEM.md](references/TOKEN-SYSTEM.md). - The shipped theme defines one (light) palette; there is no `.dark` token block. Do not promise dark mode in component work. - Breakpoints are mobile-first (`sm`, `md`, `lg`, `xl`, `2xl`). ### Shape tokens (read before touching Card, Button, Input, Dialog) Radius, shadow, and card border width come from three variables in `src/theme/tokens/core.css`: `--ui-radius`, `--ui-shadow`, `--ui-border-width`, consumed through `rounded-ui`, `shadow-ui`, `border-ui`. - Never add `rounded-xl`, `rounded-none`, `shadow-md`, `shadow-none`, or `border-0` to a primitive or a Card to force a shape. Change the token, or scope it: `<Card className="[--ui-radius:var(--radius-xl)]">`. - Always write the source names (`--ui-radius`). Writing the bridge names `--radius-ui` / `--shadow-ui` is a silent no-op. - `border-ui` is Card-only. To show a Card border use `[--ui-border-width:1px]`, not `border`. Full mechanism and scoped-override recipes: [SHAPE-TOKENS.md](references/SHAPE-TOKENS.md). ## Page components and Suspense Route components receive `loaderData` with deferred fields as unresolved promises. Rules from `AGENTS.md`: one `<Suspense>` per async operation, stable promise identity (never `Promise.all`/`.then` in render; compose in the loader), skeletons where layout is known. ```tsx import { Suspense, use } from 'react'; import { Await } from 'react-router'; function Reviews({ promise }: { promise: Promise<ReviewsData> }) { const reviews = use(promise); return <ReviewList reviews={reviews} />; } export default function ProductPage({ loaderData }: { loaderData: PageData }) { return ( <> {/* use() needs a small child component */} <Suspense fallback={<ReviewsSkeleton />}> <Reviews promise={loaderData.reviews} /> </Suspense> {/* Await resolves inline and supports a local errorElement */} <Suspense fallback={<RecsSkeleton />}> <Await resolve={loaderData.recommendations}>{(recs) => <Recommendations items={recs} />}</Await> </Suspense> </> ); } ``` `createPage` (`@/components/create-page`) wraps a component with the standard Suspense fallback and keys the page so navigations remount it: ```tsx import { createPage } from '@/components/create-page'; export default createPage({ component: CategoryView, fallback: <CategorySkeleton />, // optional; default is a generic skeleton getPageKey: (data) => data?.categoryId, // optional; default is pathname+search+hash }); ``` `withSuspense` (`@/components/with-suspense`) is the lower-level HOC for a single async child. Details: `docs/README-SUSPENSE.md`. ## Links, navigation, text - Import `Link`/`NavLink` from `@/components/link` and `useNavigate` from `@/hooks/use-navigate`, never from `react-router`. The wrappers add the site/locale URL prefix; the originals silently drop it. Combine with `href()` for typed params. - Strings go through `react-i18next` (`useTranslation`) and prices through `formatCurrency` in `@/lib/currency`. Do not hard-code `'en-US'` or `$`. See `storefront-next:sfnext-i18n`. ## Images Use `<DynamicImage>` (`@/components/dynamic-image`) with `widths` (and `heights` for cropping) for product and content images; add `priority="high"` to the LCP image. Behavior is configured under the `images` key of `config.server.ts` (`quality`, `formats`, `fallbackFormat`, `host`, `enableDis`, `realmHostMappings`); see `docs/README-IMAGES.md`. Wrap grids in `DynamicImageProvider` (`@/providers/dynamic-image`) instead of prop-drilling sizes. ## Overlays Modals, drawers, and dialogs hidden on first render must be `React.lazy()` and mounted only after first interaction, gated on `open` with `useDeferredUnmount` from `@/hooks/use-deferred-unmount`. Do not keep a sticky "loaded" latch. Pattern: `docs/README-PERFORMANCE.md` ("Lazy Loading for Overlays"). ## shadcn primitives `src/components/ui/` files are forks of shadcn/ui that you own and may edit in place (shape tokens, `data-slot`, extra props). Do not put non-shadcn components there, and do not hand-copy from the shadcn docs. Use the in-project `sync-shadcn` skill (`.claude/skills/sync-shadcn/`): ```bash S=.claude/skills/sync-shadcn/sync.mjs node $S add slider # fetch from shadcn, apply shape tokens + import convention, seed baseline node $S restyle --all --check # gate: exit 1 if any fork has drifted off the shape-token rules node $S restyle src/components/ui/toggle.tsx # normalize one file ``` Prefer `add` over `npx shadcn@latest add`: `npx` pulls the older `new-york` registry style and leaves raw `rounded-md`/`shadow-sm` classes you then have to restyle. To pull upstream fixes into an existing fork, use `status` / `sync <name>` / `diff <name>` / `advance <name>` (a 3-way merge that keeps your edits); these need a merge baseline in `.shadcn-baseline/`, which `add` and `sync <name> --bootstrap` create. Read `.claude/skills/sync-shadcn/SKILL.md` for conflict handling. If it mentions a path that does not exist in your project, use the `ui` directory configured in `components.json`. ## Stories and verification Each reusable component needs `stories/*.stories.tsx` using `@storybook/react-vite` and a title from the fixed taxonomy (`Products/Product Tile/Product Tile`). `src/components/ui/` is excluded from story coverage. Details and the snapshot fixture pattern: [STORYBOOK.md](references/STORYBOOK.md). ```bash pnpm typecheck pnpm lint # oxlint (color-linter, header, a11y rules) + biome format check pnpm test # Vitest pnpm storybook:test --type=snapshot # add --update only for intended DOM changes pnpm storybook:test --type=a11y # axe-core; new stories must pass ``` Interaction and a11y runs need Chromium once (`pnpm exec playwright install chromium`). ## More detail - [COMPONENT-AUTHORING.md](references/COMPONENT-AUTHORING.md): worked example, CVA variants, accessibility checklist, CSS component classes vs React components, mask icons - [TOKEN-SYSTEM.md](references/TOKEN-SYSTEM.md): token files, bridge, class names - [SHAPE-TOKENS.md](references/SHAPE-TOKENS.md): shape token mechanism and traps - [STORYBOOK.md](references/STORYBOOK.md): story layout, titles, snapshots - [TROUBLESHOOTING.md](references/TROUBLESHOOTING.md): symptom to fix ## Related Skills - `storefront-next:sfnext-theming` - Change palette, fonts, logo, and shape for your brand - `storefront-next:sfnext-data-fetching` - Loaders that feed data to components - `storefront-next:sfnext-page-designer` - Make a component Page Designer-editable - `storefront-next:sfnext-extensions` - Extension folders and UI targets - `storefront-next:sfnext-testing` - Vitest, Storybook, and e2e testing - `storefront-next:sfnext-accessibility` - Accessibility requirements and scans - `storefront-next:sfnext-i18n` - Translating component text - `storefront-next:sfnext-performance` - Lazy loading, images, bundle size
GitHub에서 보기