.agents/skills/shade-new-component/SKILL.md
Before marking a Shade component or pattern done, this checklist must pass.
dropdown-menu.tsx) — matches ShadCN CLI output. Don't rename for casing.DropdownMenu).<name>.stories.tsx is required (same folder).@/ alias (@/lib/utils, @/components/ui/input-surface).| Layer | Title |
|---|---|
| Primitive | Primitives / <Name> |
| Component | Components / <Name> |
| Recipe | Recipes / <Name> |
| Pattern | Patterns / <Name> |
| Posts–Stats interim | Posts–Stats / <Name> |
| Token gallery | Tokens / <Topic> |
Required on every story file:
tags: ['autodocs']parameters.docs.description.component — short one-line summaryparameters.docs.description.story — one-liner explaining when to use that variantimport * as React from 'react';
import {cva, type VariantProps} from 'class-variance-authority';
import {cn} from '@/lib/utils';
const thingVariants = cva('base-classes-here', {
variants: {
variant: {default: '...', destructive: '...'},
size: {default: '...', sm: '...'}
},
defaultVariants: {variant: 'default', size: 'default'}
});
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, thingVariants};
Hard requirements:
className forwarded and merged via cn() — never overwritten, never dropped. Applies to every component that renders DOM.variant, size, loading). No workflow props (isMembersPage, layoutMode) — those mean you actually need a Pattern wrapper..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.<name>.ts, no JSX): no forwardRef, no cva(), no React. They return class strings.Every interactive component must work in:
focus-visible:, never focus:)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.
bg-gray-200-style raw palette utilities for UI chrome.dark: variants for colour — semantic tokens handle dark mode.See shade-tokens-not-hex and shade-no-dark-variants.
shade-component-decision)<name>.stories.tsxclassName forwarded and merged with cn() (always)forwardRef if the component renders DOM consumers might refcva() if the component has variants; defaultVariants setdark: colour variantstags: ['autodocs'], component description, and one-line per-story descriptionspnpm lint, pnpm test, and Storybook all cleanapps/shade/AGENTS.md. Human docs: Storybook → Overview / Contributing.