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"
From essentialnpx claudepluginhub policyengine/policyengine-claude --plugin data-scienceThis skill uses the workspace's default tool permissions.
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Migrates code, prompts, and API calls from Claude Sonnet 4.0/4.5 or Opus 4.1 to Opus 4.5, updating model strings on Anthropic, AWS, GCP, Azure platforms.
Optimizes cloud costs on AWS, Azure, GCP via rightsizing, tagging strategies, reserved instances, spot usage, and spending analysis. Use for expense reduction and governance.
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 | #EF4444 | Error states |
--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 scaffolding