Design Systems skill for Codex

A shared UI library teams actually adopt. Codex starts by counting the duplicated buttons, dialogs and selects already in the repo, then builds components on headless primitives with semantic tokens, consistent variant and size props, Storybook coverage and accessibility checks. It versions with Changesets, retires old APIs through lint rules and codemods, and tracks adoption with simple import counts.

  • Design & UX
  • $design-systems
  • 196 lines
  • 2 files
  • ~2,100 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- design-systems

Installs to ~/.agents/skills/design-systems/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $design-systems, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Builds and governs a component design system - inventory of existing UI, layered architecture from tokens to primitives to components, component API conventions (variants, slots, asChild), headless foundations such as Radix or React Aria, Storybook docs, Changesets versioning, deprecation with codemods and lint rules, and adoption metrics. Use when starting a shared UI library, consolidating duplicate buttons and modals, writing component guidelines, planning a breaking change to a shared component, or setting up contribution rules.

Use it when

  • Starting a shared component package for several apps or teams
  • Consolidating duplicated UI components across a codebase
  • Defining component API standards and a contribution or RFC process
  • Rolling out a breaking change to a shared component

Use something else when

  • Only naming and building tokens or theme variables: use design-tokens
  • Setting up shadcn/ui itself in an app: use shadcn-ui

How to check the result

Each v1 component has stories for every variant and state, passes axe and keyboard checks, and at least one real product usage has been migrated to it.

The mistake it prevents

Building a large catalog before anyone adopts it, instead of shipping the five most-duplicated components and migrating real screens.

~/.agents/skills/design-systems/SKILL.md

--- name: design-systems description: Builds and governs a component design system - inventory of existing UI, layered architecture from tokens to primitives to components, component API… license: MIT ---

A design system is working when product teams reach for it first because it is faster than building their own, every component has one documented API and accessible behavior, and breaking changes arrive with a migration path. A Figma library with no code, or a package nobody imports, is not a system.

Before building

  • Read AGENTS.md, the workspace layout (pnpm-workspace.yaml, turbo.json, nx.json) and how styling is done. The system must fit the existing stack, not replace it.
  • Inventory before designing. Count duplicates; they set the priority list:
rg -l --glob '*.{tsx,jsx,vue,svelte}' -i 'function \w*(Button|Modal|Dialog|Select|Tooltip)\b' src | sort
rg -c '<button' --glob '*.{tsx,jsx}' src | sort -t: -k2 -nr | head
rg -o "from ['\"][^'\"]*(ui|components)/[A-Za-z]+['\"]" -N src | sort | uniq -c | sort -nr | head -30
  • Screenshot every variant of the top five duplicated components side by side. The inventory board is the strongest argument for the system and the spec for v1.

Architecture

LayerContainsChanges how often
TokensColor, space, type, radius, motion, elevation (see design-tokens)Rarely; themed per brand/mode
PrimitivesBox/Stack, Text, Icon, VisuallyHidden, SlotAlmost never
ComponentsButton, Input, Select, Dialog, Tabs, Toast, TableMinor releases
PatternsForm layouts, empty states, page headers, filtersOften; can live in apps

Rules: components consume only semantic tokens, never raw values; patterns compose components and never restyle their internals; apps may build patterns but not fork components.

Foundations: build vs adopt

NeedDefault
React, full styling control, accessibility handledHeadless: Radix Primitives or React Aria Components, styled with your tokens
React + Tailwind, want owned sourceshadcn/ui as a starting point, copied into the package and then governed like your own code
Multi-framework or no frameworkWeb components (Lit) wrapping a headless core, or CSS-only components where behavior is trivial
Speed over identity, internal toolsAdopt a styled library and theme it via tokens; do not build

Never hand-roll a combobox, menu, dialog focus trap or date picker when a tested headless implementation exists; that is where accessibility bugs live.

Component API conventions

Write these down once and apply them to every component:

  • variant for visual style (primary | secondary | ghost | danger), size for scale (sm | md | lg). No booleans like isPrimary, large.
  • Composition over configuration: <Dialog><Dialog.Title/></Dialog> or slots, not fifteen props such as titleText, titleIcon, titleClassName.
  • Pass through native props and refs (...props, forwarded ref or React 19 ref prop).
  • asChild (Radix Slot) or a render prop for polymorphism, rather than an as prop that breaks types.
  • Controlled and uncontrolled: value/onValueChange plus defaultValue.
  • className accepted and merged last; styling escape hatches documented, not hidden.
import { cva, type VariantProps } from "class-variance-authority";
import { Slot } from "@radix-ui/react-slot";

const button = cva("inline-flex items-center justify-center gap-2 font-medium rounded-[var(--radius-control)] focus-visible:outline-2 focus-visible:outline-offset-2 disabled:opacity-50 disabled:pointer-events-none", {
  variants: {
    variant: {
      primary: "bg-[var(--color-action)] text-[color:var(--color-on-action)] hover:bg-[var(--color-action-hover)]",
      secondary: "border border-[var(--color-border)] bg-transparent hover:bg-[var(--color-surface-hover)]",
      danger: "bg-[var(--color-danger)] text-[color:var(--color-on-danger)]",
    },
    size: { sm: "h-8 px-3 text-sm", md: "h-10 px-4", lg: "h-12 px-6 text-lg" },
  },
  defaultVariants: { variant: "primary", size: "md" },
});

type ButtonProps = React.ComponentProps<"button"> &
  VariantProps<typeof button> & { asChild?: boolean };

export function Button({ asChild, variant, size, className, ...props }: ButtonProps) {
  const Comp = asChild ? Slot : "button";
  return <Comp className={button({ variant, size, className })} {...props} />;
}

Workflow for a new or changed component

- [ ] 1. Inventory existing usages and variants in the codebase
- [ ] 2. Write the component spec (template below) and get it reviewed
- [ ] 3. Build on a headless primitive with semantic tokens only
- [ ] 4. Stories for every variant, size and state; interaction tests for behavior
- [ ] 5. Accessibility check: keyboard path, axe, screen reader spot-check
- [ ] 6. Visual regression baseline
- [ ] 7. Changeset with the right semver bump
- [ ] 8. Migrate at least one real product usage before calling it done

Component spec template

# <Component>
**Purpose:** <one sentence; when to use, when NOT to use and what to use instead>
**Anatomy:** <parts and slots>
**API:** <props table: name, type, default, notes>
**Variants / sizes:** <list with intended use>
**States:** default, hover, focus-visible, active, disabled, loading, invalid
**Keyboard:** <keys and behavior, following the ARIA APG pattern>
**Content rules:** <label length, truncation, icon usage>
**Replaces:** <file paths of local duplicates to migrate>

Versioning and release

Use Changesets in a monorepo package:

npx changeset          # describe the change, pick patch/minor/major per package
npx changeset version  # bump versions, write CHANGELOG.md
npx changeset publish  # publish to the registry (ask the user before publishing)
ChangeBump
New component, new optional prop, new variantminor
Bug fix, visual fix within specpatch
Removed or renamed prop, changed default, changed DOM structure apps may targetmajor
Token value change that shifts layout or contrast noticeablyminor with release note; major if contrast or sizing contracts break

Publishing to a registry needs network and credentials; never run changeset publish or npm publish without explicit user confirmation.

Deprecation path

  1. Mark the old API with /** @deprecated Use <Button variant="ghost"> instead. */ so IDEs strike it through.
  2. Add a lint rule so new usages fail CI:
// eslint.config.js
rules: {
  "no-restricted-imports": ["error", { paths: [{
    name: "@/components/OldButton",
    message: "Use Button from @acme/ui (see docs/migrations/button.md).",
  }]}],
}
  1. Ship a codemod for mechanical renames and run it in a dry run first: npx jscodeshift -t codemods/button-v2.ts src --parser=tsx --dry --print.
  2. Remove in the next major only after usage reaches zero.

Governance

  • Contribution model: anyone can propose (RFC issue with the spec template), the system team reviews API and accessibility, contributors can build with pairing.
  • A component enters the system when two or more products need it; one-offs stay local.
  • Weekly or per-release adoption metric:
rg -l "from ['\"]@acme/ui['\"]" apps | wc -l              # files using the system
rg -l "<button|styled\.button|className=.*btn" apps | wc -l  # files still hand-rolling
  • Track in a table: component, adoption count, open bugs, local duplicates remaining.

Gotchas

  • Shipping components that import app code or global CSS; the package must be leaf-level.
  • className overrides that depend on specificity order; use cascade layers or a merge helper (tailwind-merge) so app overrides win predictably.
  • Changing DOM structure in a minor: apps target internals in tests and CSS. Treat as major.
  • Documentation that drifts: generate props tables from types (Storybook autodocs) instead of hand-writing them.
  • Building every component before anyone uses one. Ship the top five by duplication count.

Output contract

When asked to set up or audit a system, return:

**Inventory:** <component>: <n local implementations> (files)
**Proposed layers and package:** <path, build tool, styling approach>
**v1 components (by duplication):** 1. ... 5.
**Conventions doc:** <path>
**Release/versioning:** <Changesets config path>
**Governance:** <RFC template path, review owners>
**Next migrations:** <files to migrate first>

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Design Systems"
  short_description: "A shared UI library teams actually adopt"
  default_prompt: "$design-systems We have six different modal components, consolidate them into one in packages/ui"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem