Skip to main content

design-system

Build a new design system (tokens, variants, theming). Use when "create a design system" or "component library from scratch". Drifted existing system → housekeep-design. Plan-only unification → plan-uiux-unification.

Source facts

Repository
kensaurus/cursor-kenji
Last source activity
September 12, 2026 at 14:20
Detected SKILL.md language
English
Stars
9
Forks
0

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.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
design-system
description
Build a new design system (tokens, variants, theming). Use when "create a design system" or "component library from scratch". Drifted existing system → housekeep-design. Plan-only unification → plan-uiux-unification.
license
MIT
# Design System Skill **Degree of freedom: MIXED.** Token taxonomy and variants `[HIGH freedom]`; existing `ui/` / token inventory `[LOW freedom — run exactly]`. Build scalable, maintainable design systems with consistent tokens, variants, and documentation. ## How to reason 1. **Inventory** — `components/ui`, tokens, `cva`, shadcn/Radix 2. **Tokenize** — semantic tokens, not raw hex in components 3. **Variant** — `cva` covers real states (default/destructive/disabled) 4. **Document** — JSDoc, keyboard/focus, dark mode ## Worked example > **Inventory:** shadcn `Button` exists; colors are raw `#3B82F6` in three pages; no `--primary`. > **Tokenize:** `--primary` / `--primary-foreground` in `globals.css`; Tailwind maps `hsl(var(--primary))`. > **Variant:** extend existing `buttonVariants` — do not add `components/Button2.tsx`. > **Document:** JSDoc lists variants/sizes; focus ring + dark `.dark` overrides. ## Self-critique before reporting - **Did not fork** — existing primitives were extended, not recreated - **Semantic tokens** — components reference tokens, not primitives or hex - **A11y + dark** — focus, disabled, and dark variants exist - **Right owner** — drifted existing system → `housekeep-design`; plan-only unification → `plan-uiux-unification` ## Check existing first [LOW freedom — run exactly] **Before creating ANY design system components, verify:** 1. **Check for existing design system:** ```bash ls -la src/components/ui/ cat package.json | grep -i "shadcn\|radix\|headless" cat components.json 2>/dev/null # shadcn config ``` 2. **Check for existing tokens:** ```bash cat tailwind.config.* | head -100 cat src/styles/globals.css | head -50 rg "var\(--" --type css | head -20 ``` 3. **Check for existing patterns:** ```bash rg "cva\(|variants:" --type ts --type tsx | head -10 rg "cn\(|clsx\(|twMerge" --type tsx | head -5 ``` **Why:** Don't recreate existing primitives. Extend and enhance what exists. ## Design Tokens [HIGH freedom] ### CSS Custom Properties ```css /* globals.css */ :root { /* Colors - Semantic */ --background: 0 0% 100%; --foreground: 222.2 84% 4.9%; --card: 0 0% 100%; --card-foreground: 222.2 84% 4.9%; --popover: 0 0% 100%; --popover-foreground: 222.2 84% 4.9%; --primary: 221.2 83.2% 53.3%; --primary-foreground: 210 40% 98%; --secondary: 210 40% 96.1%; --secondary-foreground: 222.2 47.4% 11.2%; --muted: 210 40% 96.1%; --muted-foreground: 215.4 16.3% 46.9%; --accent: 210 40% 96.1%; --accent-foreground: 222.2 47.4% 11.2%; --destructive: 0 84.2% 60.2%; --destructive-foreground: 210 40% 98%; --border: 214.3 31.8% 91.4%; --input: 214.3 31.8% 91.4%; --ring: 221.2 83.2% 53.3%; /* Spacing */ --spacing-xs: 0.25rem; --spacing-sm: 0.5rem; --spacing-md: 1rem; --spacing-lg: 1.5rem; --spacing-xl: 2rem; /* Border Radius */ --radius: 0.5rem; --radius-sm: calc(var(--radius) - 4px); --radius-lg: calc(var(--radius) + 4px); /* Shadows */ --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05); --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); /* Animation */ --duration-fast: 150ms; --duration-normal: 200ms; --duration-slow: 300ms; --ease-default: cubic-bezier(0.4, 0, 0.2, 1); --ease-in: cubic-bezier(0.4, 0, 1, 1); --ease-out: cubic-bezier(0, 0, 0.2, 1); --ease-bounce: cubic-bezier(0.34, 1.56, 0.64, 1); } .dark { --background: 222.2 84% 4.9%; --foreground: 210 40% 98%; /* ... dark mode overrides */ } ``` ### Tailwind Config Integration ```ts // tailwind.config.ts import type { Config } from 'tailwindcss' const config: Config = { content: ['./src/**/*.{ts,tsx}'], darkMode: 'class', theme: { extend: { colors: { border: 'hsl(var(--border))', input: 'hsl(var(--input))', ring: 'hsl(var(--ring))', background: 'hsl(var(--background))', foreground: 'hsl(var(--foreground))', primary: { DEFAULT: 'hsl(var(--primary))', foreground: 'hsl(var(--primary-foreground))', }, secondary: { DEFAULT: 'hsl(var(--secondary))', foreground: 'hsl(var(--secondary-foreground))', }, destructive: { DEFAULT: 'hsl(var(--destructive))', foreground: 'hsl(var(--destructive-foreground))', }, muted: { DEFAULT: 'hsl(var(--muted))', foreground: 'hsl(var(--muted-foreground))', }, accent: { DEFAULT: 'hsl(var(--accent))', foreground: 'hsl(var(--accent-foreground))', }, }, borderRadius: { lg: 'var(--radius-lg)', md: 'var(--radius)', sm: 'var(--radius-sm)', }, boxShadow: { sm: 'var(--shadow-sm)', DEFAULT: 'var(--shadow)', md: 'var(--shadow-md)', lg: 'var(--shadow-lg)', }, transitionDuration: { fast: 'var(--duration-fast)', normal: 'var(--duration-normal)', slow: 'var(--duration-slow)', }, }, }, } export default config ``` ## Component Variants with CVA ```tsx // lib/utils.ts import { type ClassValue, clsx } from 'clsx' import { twMerge } from 'tailwind-merge' export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) } ``` ```tsx // components/ui/button.tsx import { cva, type VariantProps } from 'class-variance-authority' import { forwardRef } from 'react' import { cn } from '@/lib/utils' const buttonVariants = cva( // Base styles 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50', { variants: { variant: { default: 'bg-primary text-primary-foreground shadow hover:bg-primary/90', destructive: 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90', outline: 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground', secondary: 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80', ghost: 'hover:bg-accent hover:text-accent-foreground', link: 'text-primary underline-offset-4 hover:underline', }, size: { default: 'h-9 px-4 py-2', sm: 'h-8 rounded-md px-3 text-xs', lg: 'h-10 rounded-md px-8', icon: 'h-9 w-9', }, }, defaultVariants: { variant: 'default', size: 'default', }, } ) export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> { asChild?: boolean } const Button = forwardRef<HTMLButtonElement, ButtonProps>( ({ className, variant, size, asChild = false, ...props }, ref) => { const Comp = asChild ? Slot : 'button' return ( <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} /> ) } ) Button.displayName = 'Button' export { Button, buttonVariants } ``` ## Compound Components Pattern ```tsx // components/ui/card.tsx import { cn } from '@/lib/utils' import { forwardRef } from 'react' const Card = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>( ({ className, ...props }, ref) => ( <div ref={ref} className={cn( 'rounded-xl border bg-card text-card-foreground shadow', className )} {...props} /> ) ) Card.displayName = 'Card' const CardHeader = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>( ({ className, ...props }, ref) => ( <div ref={ref} className={cn('flex flex-col space-y-1.5 p-6', className)} {...props} /> ) ) CardHeader.displayName = 'CardHeader' const CardTitle = forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>( ({ className, ...props }, ref) => ( <h3 ref={ref} className={cn('font-semibold leading-none tracking-tight', className)} {...props} /> ) ) CardTitle.displayName = 'CardTitle' const CardDescription = forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>( ({ className, ...props }, ref) => ( <p ref={ref} className={cn('text-sm text-muted-foreground', className)} {...props} /> ) ) CardDescription.displayName = 'CardDescription' const CardContent = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>( ({ className, ...props }, ref) => ( <div ref={ref} className={cn('p-6 pt-0', className)} {...props} /> ) ) CardContent.displayName = 'CardContent' const CardFooter = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>( ({ className, ...props }, ref) => ( <div ref={ref} className={cn('flex items-center p-6 pt-0', className)} {...props} /> ) ) CardFooter.displayName = 'CardFooter' export { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } ``` ## Accessible Components with Radix ```tsx // components/ui/dialog.tsx 'use client' import * as DialogPrimitive from '@radix-ui/react-dialog' import { X } from 'lucide-react' import { cn } from '@/lib/utils' const Dialog = DialogPrimitive.Root const DialogTrigger = DialogPrimitive.Trigger const DialogPortal = DialogPrimitive.Portal const DialogClose = DialogPrimitive.Close const DialogOverlay = forwardRef<
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub