skills/design-system-create/SKILL.md
--- name: design-system-create description: Scaffold a design system in a React/Tailwind project — DESIGN_SYSTEM.md spec + typed tokens module + cva variants for core primitives (Button, Input, Card). Emits a `/styleguide` showcase route (role-gated to superadmin + staff if Clerk is wired, dev-only otherwise) so every theme/component/token is auditable in-browser. Use when a project has hand-rolled Tailwind for buttons/inputs/cards and needs a single source of truth, or just to add the styleguid
npx skillsauth add RonanCodes/ronan-skills skills/design-system-createInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Lay down the four-layer contract that makes UI predictable:
@theme inline bridge — maps vars to utilities (bg-background etc.)src/design-system/tokens.ts) — TYPOGRAPHY, SPACING, RADIUS, ELEVATION, Zcva-based component variants — Button, Input, Card with state tables baked inDESIGN_SYSTEM.md at repo root — rules, state tables, review checklistThe skill generates these in that order, and leaves the project with zero hex values in component files.
/ro:design-system-create # interactive — inspect repo, propose layout (showcase on by default)
/ro:design-system-create --primitives=button,input,card
/ro:design-system-create --showcase # explicit opt-in (default anyway)
/ro:design-system-create --no-showcase # skip the /styleguide route
/ro:design-system-create --showcase-only # ONLY add /styleguide to an existing DS
/ro:design-system-create --dry-run # show what would be written, don't write
Run in parallel:
ls src/components/ui/ (shadcn layout) or ls src/components/ (alternative)cat tailwind.config.* 2>/dev/null || cat src/styles.css | head -40grep -rn '#[0-9a-fA-F]\{3,8\}' src/components/ 2>/dev/null | head -20 (hex leakage)grep -rn 'scale-\|rounded-\[' src/components/ 2>/dev/null | head -20 (likely DS violations)Determine:
If the project already has some of this (e.g. shadcn-ui scaffolded), the skill augments rather than overwriting. Never clobber an existing DESIGN_SYSTEM.md without asking.
Via AskUserQuestion, confirm:
translate-y-px + darker bg (recommended), or fade only?/styleguide page that renders every token + primitive? (default: yes — it's the cheapest way to keep the spec honest). If Clerk is detected, the route is gated to superadmin + staff; otherwise it's dev-only (404 when import.meta.env.PROD)If Tailwind v4, ensure src/styles.css has:
@import 'tailwindcss';
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
}
The vars themselves live per-theme in src/lib/themes/css/*.css (or wherever the project keeps them). Do not inline concrete colours here.
src/design-system/tokens.tsTyped, importable, autocomplete-friendly. Template:
export const TYPOGRAPHY = {
display: 'text-3xl md:text-4xl font-bold tracking-tight',
h1: 'text-2xl md:text-3xl font-bold tracking-tight',
h2: 'text-xl font-semibold',
h3: 'text-base font-semibold',
body: 'text-sm leading-relaxed',
bodyLg: 'text-base leading-relaxed',
caption: 'text-xs text-muted-foreground',
label: 'text-sm font-medium',
mono: 'font-mono text-xs',
} as const
export type TypographyToken = keyof typeof TYPOGRAPHY
export const SPACING = {
xs: '1', sm: '2', md: '3', lg: '4', xl: '6', xxl: '8',
} as const
export type SpacingToken = keyof typeof SPACING
export const RADIUS = {
none: 'rounded-none',
sm: 'rounded-sm',
md: 'rounded-md',
lg: 'rounded-lg',
xl: 'rounded-xl',
pill: 'rounded-full',
} as const
export const ELEVATION = {
flat: 'border border-border',
raised: 'border border-border shadow-sm',
floating: 'border border-border shadow-md',
overlay: 'border border-border shadow-xl',
} as const
export const Z = {
base: 0, dropdown: 40, overlay: 50, dialog: 60, toast: 70, tooltip: 80,
} as const
Each primitive follows the same state-table contract. Example for Button:
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium " +
"transition-all duration-150 disabled:pointer-events-none disabled:opacity-50 " +
"outline-none focus-visible:ring-ring/50 focus-visible:ring-[3px] active:translate-y-px",
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/80',
destructive: 'bg-destructive text-white hover:bg-destructive/90 active:bg-destructive/80',
outline: 'border bg-background hover:bg-accent hover:text-accent-foreground active:bg-accent/80',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80 active:bg-secondary/70',
ghost: 'hover:bg-accent hover:text-accent-foreground active:bg-accent/80',
link: 'text-primary underline-offset-4 hover:underline active:text-primary/80',
},
size: {
default: 'h-9 px-4 py-2',
xs: 'h-6 gap-1 px-2 text-xs',
sm: 'h-8 gap-1.5 px-3',
lg: 'h-10 px-6',
icon: 'size-9',
},
},
defaultVariants: { variant: 'default', size: 'default' },
},
)
Invariants the generated variants enforce:
translate-y-px — never scales upMirror this for Input (hover border-foreground/30, focus-visible border-ring + ring, disabled opacity-50) and Card (Flat/Raised/Floating/Overlay elevation tiers).
DESIGN_SYSTEM.mdRoot of the repo. Must include:
The six core rules (lift verbatim unless the project has a well-reasoned deviation):
rounded-md must be identical in rest, hover, active, focus, disabled.scale-110 or a larger ring for active — that says "emphasised", which is a different concept (toggle).ring-ring/50, outline-none. Don't customise per variant.rounded-sm (badges), rounded-md (buttons/inputs), rounded-lg/xl (cards), rounded-full (pills). No rounded-[7px] one-offs.Emit a /styleguide page that renders every token and primitive so the system is auditable in-browser across themes. Default-on; use --no-showcase to skip.
Route path: /styleguide. Picked over /design-system and /showcase because it's the term frontend devs already search for, reads cleanly as a sidebar nav entry the day a real admin panel exists, and doesn't suggest the route is dev-only (it's role-gated in production by design, see below).
Where to write it:
src/routes/styleguide.tsx (route = /styleguide)app/styleguide/page.tsxsrc/pages/Styleguide.tsx + register in the router manuallyGating — role-based if Clerk is wired, dev-only otherwise:
Detect Clerk by looking for @clerk/tanstack-react-start in package.json AND a src/lib/auth/roles.ts file (the requireRole() helper emitted by /ro:clerk add-roles).
If both are present (preferred): wire the route to requireRole('superadmin', 'staff'). Production-safe — only the superadmin email + Clerk org:staff members can view. Members and signed-out visitors get a 404 (not a redirect — don't leak the existence of admin routes).
// src/routes/styleguide.tsx
import { createFileRoute } from '@tanstack/react-router';
import { createServerFn } from '@tanstack/react-start';
import { requireRole } from '@/lib/auth/roles';
const guardFn = createServerFn({ method: 'GET' }).handler(async () => {
return await requireRole('superadmin', 'staff');
});
export const Route = createFileRoute('/styleguide')({
beforeLoad: () => guardFn(),
component: StyleguidePage,
});
If Clerk is wired but src/lib/auth/roles.ts is missing: prompt the user to run /ro:clerk add-roles first (one command, takes <30s), then emit the gated route. Do NOT silently skip the gate — that's the failure mode where an unguarded admin surface ships to production.
If Clerk is not wired: fall back to a dev-only gate. The route 404s in production builds, renders in dev. This is the "scaffold without auth" path; the moment auth lands, swap to the role-gated form.
// src/routes/styleguide.tsx — no-auth fallback
import { createFileRoute } from '@tanstack/react-router';
export const Route = createFileRoute('/styleguide')({
beforeLoad: () => {
if (import.meta.env.PROD) {
throw new Response('Not Found', { status: 404 });
}
},
component: StyleguidePage,
});
Rules for the showcase file:
src/lib/themes/index.ts exists. The toggle lives on-page so you can A/B both modes side-by-side without leaving the route.Required sections (in order):
superadmin / staff / dev-mode), light/dark toggle (mandatory), multi-theme picker (if themes exist)bg-background/text-foreground, bg-card/text-card-foreground, etc.) as labelled swatches. Names match the @theme inline bridge.TYPOGRAPHY.* key, token name on the left, rendered sample on the right (use "The quick brown fox…")RADIUS.* value with the key as captionELEVATION.* tieraria-pressed state toggled via local state, showing the recommended filled+bordered on-statearia-invalid, with-value (in a 2-col grid)SPACING.* value with px readoutDESIGN_SYSTEM.md (relative to repo host if known)Why it earns its keep:
/styleguide and eyeball every primitive in 30 secondsorg:staff in Clerk, share the /styleguide link, no extra access neededSee docs/showcase-template.tsx in the repo where this skill was developed (connections-helper src/routes/styleguide.tsx) for a canonical reference implementation.
Print what was written:
✅ Design system scaffolded
Created:
DESIGN_SYSTEM.md — spec + review checklist
src/design-system/tokens.ts — typed tokens
src/styles.css — @theme inline bridge (augmented)
src/components/ui/button.tsx — cva variants with state table
src/components/ui/input.tsx — matches button state treatment
src/components/ui/card.tsx — elevation tiers
src/routes/styleguide.tsx — /styleguide showcase (role-gated to superadmin+staff if Clerk wired, dev-only otherwise)
Next:
1. Visit /styleguide in dev to verify the scaffold renders cleanly across themes (sign in as superadmin or an org:staff member if Clerk is wired)
2. Run /ro:design-system-audit to find callers that need migrating
3. Review DESIGN_SYSTEM.md — deviate where your brand demands it
4. Commit the scaffold before starting migrations
@theme inline bridge for Tailwind v4 — maps a small number of semantic vars to a much larger utility surface for free.DESIGN_SYSTEM.md, tokens.ts, or primitives without explicit confirmation/ro:commit for the "just scaffolded" commit/ro:design-system-audit — run after scaffolding to find callers that still violate the rules/ro:frontend-design — broader UI review skill; this one is narrower and more actionable/ro:coding-principles — simplicity principles that informed the "rules not laws" choice/ro:clerk add-roles — emits the requireRole() helper that /styleguide consumes when Clerk is wireddevelopment
--- name: worktree description: Coordinate multiple agents on one repo via a worktree-lock pool, so two agents never clobber each other's working tree. Acquire the first free slot (main, then beta/gamma… worktrees, created on demand), work there on your own branch, release when you've pushed. Use before modifying any repo that might be in use by another agent (factory, dataforce, etc.), or whenever you're told a repo is being worked on. Backed by `ro worktree`. category: development argument-hin
testing
--- name: ship description: Ship a feature branch the local-CI-first way — run the full local gate, push, open a PR, squash-merge, then deploy, without waiting on GitHub Actions. Use when a branch is ready for main and you want it merged and deployed now. Reads CI policy from `ro ci` (default skips remote CI because GitHub Actions billing keeps hitting limits). Sibling to /ro:gh-ship (waits on GitHub checks) and /ro:cf-ship (the deploy half). Triggers on "ship it", "ship this", "merge and deploy
testing
--- name: setup-logging description: Set up (or audit) the observability stack in a TanStack Start + Cloudflare Workers app so it is "diagnosable by default" — structured logging (logtape) with a request context carrying trace_id + userId + tenant/orgId, a trace_id propagated FE→BE→logs→Sentry→PostHog, Cloudflare Workers observability enabled, and Sentry + PostHog wired. Two modes: `setup` (wire it into an app) and `audit` (check an existing app + report gaps). Use when scaffolding a new app, wh
development
Manage credentials INSIDE the active ~/.claude/.env file — read which token/account to use for a given app (Simplicity vs Dataforce vs Ronan-personal), add or update a secret WITHOUT it passing through the chat (an interactive Terminal window prompts for it), and track secrets that were exposed in a transcript so they get rotated. Sibling to /ro:context (which switches WHICH env file is active). Use when the user wants to add an API key/token/secret, asks "which credential do I use for X", needs the env organized/labelled, or a secret was pasted into the chat and should be rotated.