skills/frontend/policyengine-tailwind-shadcn-skill/SKILL.md
Tailwind CSS v4 + shadcn/ui integration patterns for PolicyEngine frontend projects. Covers @theme namespaces, CSS variable conventions, SVG var() usage, and common mistakes. Triggers: "Tailwind v4", "@theme", "shadcn", "CSS variables", "design tokens CSS", "theme.css", "@theme inline"
npx skillsauth add policyengine/policyengine-claude policyengine-tailwind-shadcnInstall 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.
Technical reference for how PolicyEngine's CSS-first design token architecture works. This skill explains the mechanism — how Tailwind v4 and shadcn/ui consume CSS variables. For the actual token values (colors, fonts, spacing), see policyengine-design-skill.
PolicyEngine uses a single CSS file (@policyengine/ui-kit/theme.css) as the source of truth for all design tokens. This file has three layers:
Layer 1: :root { --primary: #2C7A7B; } ← Raw values (shadcn/ui convention)
Layer 2: @theme inline { --color-primary: var(--primary); } ← Bridge to Tailwind
Layer 3: @theme { --color-teal-500: #319795; } ← Brand palette + fonts + sizes
Consumers import it in their globals.css:
@import "tailwindcss";
@import "@policyengine/ui-kit/theme.css";
@theme namespacesTailwind v4 uses CSS custom properties in @theme blocks to generate utility classes. The namespace prefix determines which utilities are created:
| CSS variable prefix | Tailwind utility | Example variable | Example class |
|---|---|---|---|
| --color-* | bg-*, text-*, border-*, fill-* | --color-primary: #2C7A7B | bg-primary, text-primary |
| --color-teal-* | bg-teal-*, text-teal-* | --color-teal-500: #319795 | bg-teal-500 |
| --text-* | text-* (font size) | --text-sm: 14px | text-sm |
| --font-* | font-* | --font-sans: Inter, ... | font-sans |
| --radius-* | rounded-* | --radius-lg: 8px | rounded-lg |
| --spacing-* | p-*, m-*, gap-*, w-*, h-* | --spacing-header: 58px | h-header |
| --breakpoint-* | sm:, md:, lg: | --breakpoint-md: 62rem | md:flex-col |
Source: Tailwind v4 Theme docs
@theme vs @theme inline/* @theme — bakes values directly into generated CSS */
@theme {
--color-teal-500: #319795; /* Resolved at build time */
}
/* @theme inline — preserves var() for runtime resolution */
@theme inline {
--color-primary: var(--primary); /* Resolved at runtime via :root */
}
When to use @theme inline:
:root CSS variable (var(--something)):root values change, Tailwind must re-resolve)When to use @theme:
#319795, 14px, Inter)Source: Tailwind v4 Functions and Directives
shadcn/ui defines semantic tokens as unprefixed CSS variables in :root:
:root {
--primary: #2C7A7B;
--background: #FFFFFF;
--foreground: #000000;
--muted: #F2F4F7;
--muted-foreground: #6B7280;
--border: #E2E8F0;
--chart-1: #319795;
--chart-2: #0EA5E9;
/* ... */
}
These are then bridged to Tailwind via @theme inline:
@theme inline {
--color-primary: var(--primary); /* → bg-primary, text-primary */
--color-background: var(--background); /* → bg-background */
--color-foreground: var(--foreground); /* → text-foreground */
--color-chart-1: var(--chart-1); /* → fill-chart-1 */
}
Source: shadcn/ui Theming, shadcn/ui Tailwind v4 guide
var() in RechartsModern browsers resolve CSS custom properties in SVG presentation attributes. Recharts fill and stroke props accept var() directly:
<Bar fill="var(--chart-1)" />
<Line stroke="var(--chart-2)" />
<CartesianGrid stroke="var(--border)" />
No helper function needed. The old getCssVar() / getComputedStyle() pattern is unnecessary.
Source: shadcn/ui Charts
:root (shadcn/ui semantic)| Variable | Hex | Usage |
|----------|-----|-------|
| --primary | #2C7A7B | Primary actions, buttons |
| --primary-foreground | #FFFFFF | Text on primary |
| --background | #FFFFFF | Page background |
| --foreground | #000000 | Body text |
| --muted | #F2F4F7 | Muted backgrounds |
| --muted-foreground | #6B7280 | Secondary text |
| --border | #E2E8F0 | Borders, dividers |
| --card | #FFFFFF | Card backgrounds |
| --destructive | #DC2626 | Error states (red-600, clears WCAG AA on the white --destructive-foreground) |
| --ring | #319795 | Focus rings |
| --chart-1 through --chart-5 | Teal→Gray | Chart series |
@theme inline (bridges)Maps each :root var to Tailwind's --color-* namespace. Example:
--color-primary: var(--primary) → enables bg-primary, text-primary--color-chart-1: var(--chart-1) → enables fill-chart-1@theme (brand palette)| Namespace | Example | Tailwind class |
|-----------|---------|---------------|
| --color-teal-* | --color-teal-500: #319795 | bg-teal-500 |
| --color-gray-* | --color-gray-600: #4B5563 | text-gray-600 |
| --color-blue-* | --color-blue-500: #0EA5E9 | bg-blue-500 |
| --text-* | --text-sm: 14px | text-sm |
| --font-* | --font-sans: Inter, ... | font-sans |
| --spacing-* | --spacing-header: 58px | h-header |
@theme instead of @theme inline for var() references/* WRONG — var() won't resolve, Tailwind bakes the literal string */
@theme {
--color-primary: var(--primary);
}
/* CORRECT — var() resolves at runtime */
@theme inline {
--color-primary: var(--primary);
}
/* WRONG — creates a utility called "primary", not a color */
@theme {
--primary: #2C7A7B;
}
/* CORRECT — --color-* prefix creates bg-primary, text-primary */
@theme inline {
--color-primary: var(--primary);
}
// WRONG — unnecessary helper
const color = getCssVar('--chart-1');
<Bar fill={color} />
// CORRECT — SVG accepts var() directly
<Bar fill="var(--chart-1)" />
// WRONG
<div style={{ color: '#319795' }}>
// CORRECT — use Tailwind class
<div className="text-teal-500">
// CORRECT — use CSS var for inline styles
<div style={{ color: 'var(--primary)' }}>
Tailwind v4 does NOT use tailwind.config.ts. All configuration is in CSS via @theme blocks. The ui-kit theme CSS file handles this.
// WRONG — old convention
<div className="bg-pe-primary-500 text-pe-text-secondary p-pe-lg">
// CORRECT — standard Tailwind/shadcn classes
<div className="bg-teal-500 text-muted-foreground p-4">
policyengine-design-skill — Token values, color tables, chart brandingpolicyengine-frontend-builder-spec-skill — Mandatory technology requirementspolicyengine-recharts-skill — Chart-specific patternspolicyengine-interactive-tools-skill — Standalone tool scaffoldingdevelopment
ALWAYS LOAD THIS SKILL for PolicyEngine PR reviews, including when the user invokes $review-program or Codex /review on a PolicyEngine PR. Performs read-only code validation, source-reference checks, regulatory review, optional PDF audit, summary reporting, and optional GitHub comment posting.
development
Use when the user invokes $fix-pr or asks Codex to apply fixes to a PolicyEngine PR based on $review-program findings, GitHub review comments, CI failures, or local review reports.
development
Use when the user invokes $encode-policy-v2 or asks Codex to implement a new PolicyEngine-US state benefit program from official rules. Covers research, source collection, requirement extraction, scoped implementation, tests, validation, and draft PR preparation.
development
Deploying PolicyEngine frontend apps to Vercel - naming, scope, team settings