shadcn/ui skill for Codex

Own your components, theme them with tokens. Codex treats shadcn/ui as source the project owns: it reads components.json first, adds components through the CLI, themes through paired CSS variables in OKLCH instead of hard-coded classes, and extends components with cva variants. It checks for local edits before overwriting, previews upstream changes with add --diff before applying them, and can publish a private registry for other repos.

  • Frontend
  • $shadcn-ui
  • 180 lines
  • 2 files
  • ~1,900 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- shadcn-ui

Installs to ~/.agents/skills/shadcn-ui/ 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 $shadcn-ui, 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

Sets up and works with shadcn/ui in React projects - the shadcn CLI, components.json, Tailwind v4 CSS variable theming in OKLCH, adding and updating copied components, composing variants with cva, building forms, data tables and custom registries, and choosing Radix or Base UI primitives. Use when a user runs or asks about npx shadcn, wants to add a Button, Dialog, Form or DataTable, retheme components or add dark mode, update local components after an upstream change, or publish a private component registry.

Use it when

  • A React project uses or is adopting shadcn/ui
  • Components need a new variant, brand theme or dark mode
  • Local shadcn components drifted and need upstream fixes
  • Several apps should share one set of shadcn-style components

Use something else when

  • Building an accessible primitive from scratch without shadcn styling: use radix-ui
  • Designing the token architecture itself: use design-tokens
  • Migrating Tailwind v3 config to v4 first: use tailwind-v4

How to check the result

Type check and lint pass, dark mode is correct inside portaled dialogs and menus, keyboard interaction works in each added component, and git diff on components/ui shows only intended edits.

The mistake it prevents

Running add with overwrite on components that carry local edits, or hard-coding colors in components instead of changing the CSS variable tokens.

~/.agents/skills/shadcn-ui/SKILL.md

--- name: shadcn-ui description: Sets up and works with shadcn/ui in React projects - the shadcn CLI, components.json, Tailwind v4 CSS variable theming in OKLCH, adding and updating… license: MIT ---

Done well means: components live in the repo as owned source that matches the project’s conventions, theming is driven by CSS variables (not hard-coded colors in components), customizations happen through variants rather than one-off class soup, and the CLI can still add and diff components afterwards.

Facts to keep straight

  • shadcn/ui is not a dependency you import; the CLI copies component source into your repo (default components/ui). You own and edit that code.
  • components.json drives the CLI: style, Tailwind CSS file, aliases, icon library, and registries. Read it before running any command.
  • Current projects use Tailwind v4: theme tokens are CSS variables declared in the main CSS file and mapped with @theme inline; colors are OKLCH. Animations come from tw-animate-css (the old tailwindcss-animate plugin is for Tailwind v3 projects).
  • The primitive layer can be Radix (radix-ui unified package) or Base UI; the style in components.json records which. Do not mix primitives inside one component.
  • CLI commands need network (they fetch from the registry). In a Codex sandbox, ask for approval before running them.

Before changing anything

  1. Read AGENTS.md, components.json, the global CSS file it points to, and package.json.
  2. List what is installed: ls components/ui (or the alias path). Check for local edits before overwriting anything: git diff --stat -- components/ui.
  3. Note the package manager; the CLI detects it from the lockfile.

Setup

# Existing app (Next.js, Vite, React Router, Astro, TanStack Start...)
npx shadcn@latest init

# Pick the primitive layer explicitly (create is an alias of init)
npx shadcn@latest init --base radix      # or: --base base   (Base UI)

Then add components by name; dependencies (other components, npm packages) come along:

npx shadcn@latest add button dialog dropdown-menu form
npx shadcn@latest add sidebar          # blocks can pull many files: review the diff

add prompts before overwriting an existing file; never pass --overwrite on components with local edits unless the user confirms.

Theming

/* app/globals.css (excerpt) */
@import "tailwindcss";
@import "tw-animate-css";

@custom-variant dark (&:is(.dark *));

:root {
  --radius: 0.625rem;
  --background: oklch(1 0 0);
  --foreground: oklch(0.145 0 0);
  --primary: oklch(0.55 0.2 264);
  --primary-foreground: oklch(0.985 0 0);
  --ring: oklch(0.55 0.2 264 / 50%);
}
.dark {
  --background: oklch(0.145 0 0);
  --foreground: oklch(0.985 0 0);
  --primary: oklch(0.7 0.16 264);
  --primary-foreground: oklch(0.145 0 0);
}
@theme inline {
  --color-background: var(--background);
  --color-foreground: var(--foreground);
  --color-primary: var(--primary);
  --color-primary-foreground: var(--primary-foreground);
  --radius-lg: var(--radius);
}
  • Every surface token comes in a pair (--card / --card-foreground). When adding a brand token, add both, define both modes, and map it in @theme inline so bg-brand works.
  • Check contrast of each foreground/background pair in both modes (4.5:1 for text).
  • Dark mode: toggle the .dark class on <html> (for Next.js, next-themes with attribute="class" and suppressHydrationWarning on <html>).
  • Change the theme in CSS variables, not by editing bg-primary classes across components.

Customizing components

ChangeWhere
New look for a component everywhereEdit components/ui/<name>.tsx (it is yours)
A new visual optionAdd a variant to the component’s cva config
A one-off tweak at a call siteclassName prop; components merge with cn()
Domain component (e.g. UserMenu)New file outside components/ui that composes ui parts
const buttonVariants = cva("inline-flex items-center gap-2 rounded-md text-sm font-medium ...", {
  variants: {
    variant: {
      default: "bg-primary text-primary-foreground hover:bg-primary/90",
      outline: "border bg-background hover:bg-accent",
      success: "bg-emerald-600 text-white hover:bg-emerald-600/90", // added
    },
    size: { default: "h-9 px-4", sm: "h-8 px-3", lg: "h-10 px-6", icon: "size-9" },
  },
  defaultVariants: { variant: "default", size: "default" },
});
  • Keep components/ui generic. Business logic in a primitive file makes upstream updates painful.
  • Use asChild (Radix) to render a Button as a Link instead of nesting interactive elements.
  • Components carry data-slot attributes; style descendants with them ([&_[data-slot=icon]]:size-4) rather than brittle child selectors.

Forms and tables

  • Forms: the shadcn form pieces wrap React Hook Form with a schema resolver (Zod is the common default). Keep the schema next to the form, reuse it on the server. Deep form work belongs to $react-hook-form-zod.
  • Data tables: npx shadcn@latest add table and build on TanStack Table (@tanstack/react-table); the shadcn data-table guide is a pattern to copy, not a component. Keep column definitions in their own file and memoize data.

Updating components

npx shadcn@latest view button                 # inspect the current registry version
npx shadcn@latest add button --dry-run        # what would be written
npx shadcn@latest add button --diff           # diff registry version against the local file

Apply upstream fixes by hand when the local file has intentional edits. Commit before running add --overwrite, so the change is reviewable as a git diff.

Private registry

For sharing components across repos:

  1. Create registry.json listing items with name, type (registry:ui, registry:block, registry:lib, registry:hook), files, dependencies and registryDependencies.
  2. npx shadcn@latest build writes JSON item files to public/r/.
  3. Host them; consumers add a namespace under registries in components.json (e.g. "@acme": "https://ui.acme.dev/r/{name}.json", with headers for auth tokens read from env vars) and run npx shadcn@latest add @acme/data-grid.

Never commit registry auth tokens into components.json; reference environment variables.

Gotchas

  • Tailwind v3 projects need the v3 instructions (tailwind.config with hsl(var(--x)) colors, tailwindcss-animate). Do not paste v4 CSS into a v3 project; migrate first with $tailwind-v4.
  • Path aliases in components.json must match tsconfig.json paths, or added files import from paths that do not resolve.
  • Mixing @radix-ui/react-* packages and the unified radix-ui package duplicates code; npx shadcn@latest migrate radix moves imports to the unified package.
  • Dialog, Sheet and Popover portal to body; theme classes must be on <html> or <body>, or portaled content renders unthemed.
  • Toast was replaced by Sonner in current registries; do not add both.

Verification

  • tsc --noEmit and the project’s lint pass; no unresolved @/components/ui/... imports.
  • Toggle dark mode on a page with dialog, dropdown and form; portaled content is themed.
  • Keyboard pass: Tab, Escape, arrow keys work in every added interactive component.
  • git diff -- components/ui shows only the edits you intended.
  • Run the axe check from $accessibility-testing if the project has one.

Output

List components added or changed, new tokens/variants (with both mode values), files touched outside components/ui, and any registry configuration.

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: "shadcn/ui"
  short_description: "Own your components, theme them with tokens"
  default_prompt: "$shadcn-ui Set up shadcn/ui in this Next.js app and add button, dialog and form"

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
New

Introducing Codex Skills Pro

79 professional skills that take Codex through complete engagements, each with its own scripts, references and a report that verifies the result.

All-AccessOne-time payment, no subscription
$49
Get All-AccessOr browse individual skills from $2.99 →

Secure checkout by Creem