Skip to main content

shade-new-component

Acceptance checklist for adding or editing a Shade component or pattern file — naming, sibling story, className forwarding, cva variants, required states, recipe usage. Trigger when editing files in apps/shade/src/components.

来源信息

仓库
TryGhost/Ghost
最近来源活动
2026年10月11日 08:53
检测到的 SKILL.md 语言
英语
星标
55,512
分支
11,988

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
Shade new component
description
Acceptance checklist for adding or editing a Shade component or pattern file — naming, sibling story, className forwarding, cva variants, required states, recipe usage. Trigger when editing files in apps/shade/src/components.
autoTrigger
[{"fileEdit":"apps/shade/src/components/**/*.{ts,tsx}"}]
# Shade — new component / pattern checklist Before marking a Shade component or pattern done, this checklist must pass. ## Files and naming - File: **kebab-case** (`dropdown-menu.tsx`) — matches ShadCN CLI output. Don't rename for casing. - Exported identifier: **PascalCase** (`DropdownMenu`). - Hooks, functions, variables: camelCase. - Sibling **`<name>.stories.tsx`** is required (same folder). - A component file exports only components and types, so Fast Refresh can hot-update it (ESLint enforces this). An exported `cva()` config goes in a sibling **`<name>-variants.ts`**; exported hooks, contexts and helpers go in their own sibling module (`sidebar-context.ts`). These siblings belong to the component; they aren't Recipes. - Inside Shade itself, import via the `@/` alias (`@/lib/utils`, `@/components/ui/input-surface`). ## Storybook title prefix | Layer | Title | | ------------- | --------------------- | | Primitive | `Primitives / <Name>` | | Component | `Components / <Name>` | | Recipe | `Recipes / <Name>` | | Pattern | `Patterns / <Name>` | | Token gallery | `Tokens / <Topic>` | Required on every story file: - `tags: ['autodocs']` - `parameters.docs.description.component` — short one-line summary - Per-story `parameters.docs.description.story` — one-liner explaining when to use that variant - One story per important variant/state. Many small focused stories > one prose-heavy story. ## Component shape ```ts // thing-variants.ts import {cva} from 'class-variance-authority'; const thingVariants = cva('base-classes-here', { variants: { variant: {default: '...', destructive: '...'}, size: {default: '...', sm: '...'} }, defaultVariants: {variant: 'default', size: 'default'} }); export {thingVariants}; ``` ```tsx // thing.tsx import * as React from 'react'; import {type VariantProps} from 'class-variance-authority'; import {thingVariants} from '@/components/ui/thing-variants'; import {cn} from '@/lib/utils'; export interface ThingProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof thingVariants> {} const Thing = React.forwardRef<HTMLDivElement, ThingProps>( ({className, variant, size, ...props}, ref) => ( <div ref={ref} className={cn(thingVariants({variant, size, className}))} {...props} /> ) ); Thing.displayName = 'Thing'; export {Thing}; ``` Hard requirements: - `className` forwarded and merged via `cn()` — never overwritten, never dropped. Applies to every component that renders DOM. - Visual/interactive props only (`variant`, `size`, `loading`). **No workflow props** (`isMembersPage`, `layoutMode`) — those mean you actually need a Pattern wrapper. - Multi-region components expose compound subcomponents (`.Title`, `.Actions`, `.Body`) — not a prop bag. When applicable: - **`forwardRef`** — use when the component renders a single DOM element consumers might need a ref to (most UI controls). Skip for: pure provider/context wrappers, Radix root re-exports that don't render DOM themselves, and components whose `ref` semantics are already handled by a child. - **`cva()`** — use when the component has variants or stateful class branches (`variant`, `size`, `tone`). Skip for simple single-style components where a `cn(...)` call is clearer. - **Recipes** (`<name>.ts`, no JSX): no `forwardRef`, no `cva()`, no React. They return class strings. ## Required states (all four) Every interactive component must work in: - **default** - **hover** - **focus-visible** (use `focus-visible:`, never `focus:`) - **disabled** Each state visible in the story. Optional states (active, loading, error, empty) only when they apply. For form controls, drive chrome through the **`inputSurface` recipe** — don't roll your own border/focus ring. See the `shade-input-surface-recipe` skill. ## Tokens - No hex values. No `bg-gray-200`-style raw palette utilities for UI chrome. - No `dark:` variants for colour — semantic tokens handle dark mode. See `shade-tokens-not-hex` and `shade-no-dark-variants`. ## Before marking done - [ ] Lives in the right layer (`shade-component-decision`) - [ ] kebab-case filename, PascalCase export, sibling `<name>.stories.tsx` - [ ] `className` forwarded and merged with `cn()` (always) - [ ] `forwardRef` if the component renders DOM consumers might ref - [ ] `cva()` if the component has variants; `defaultVariants` set - [ ] The component file exports only components and types; exported variants, hooks and contexts live in sibling modules - [ ] All four required states work and are visible in the story - [ ] Semantic tokens only — no hex, no raw greys, no `dark:` colour variants - [ ] No product-specific props on a generic Component - [ ] Story has `tags: ['autodocs']`, component description, and one-line per-story descriptions - [ ] `pnpm lint`, `pnpm test`, and Storybook all clean ## Source of truth Storybook → Overview / Contributing and the component's stories.
在 GitHub 查看