Agent skill

policyengine-tailwind-shadcn

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"

Stars 26
Forks 5

Install this agent skill to your Project

npx add-skill https://github.com/PolicyEngine/policyengine-claude/tree/main/skills/frontend/policyengine-tailwind-shadcn-skill

SKILL.md

Tailwind CSS v4 + shadcn/ui integration

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.

Architecture overview

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:

css
@import "tailwindcss";
@import "@policyengine/ui-kit/theme.css";

Tailwind v4 @theme namespaces

Tailwind 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

css
/* @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:

  • When the value references a :root CSS variable (var(--something))
  • Required for dark mode (:root values change, Tailwind must re-resolve)
  • Used for all shadcn/ui semantic tokens (primary, background, foreground, etc.)

When to use @theme:

  • When the value is a static literal (#319795, 14px, Inter)
  • Used for brand palette colors, font sizes, spacing, breakpoints

Source: Tailwind v4 Functions and Directives

shadcn/ui CSS variable convention

shadcn/ui defines semantic tokens as unprefixed CSS variables in :root:

css
: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:

css
@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

SVG var() in Recharts

Modern browsers resolve CSS custom properties in SVG presentation attributes. Recharts fill and stroke props accept var() directly:

tsx
<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

Complete namespace reference

Layer 1: :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

Layer 2: @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

Layer 3: @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

Common mistakes

1. Using @theme instead of @theme inline for var() references

css
/* 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);
}

2. Wrong namespace prefix

css
/* 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);
}

3. Using getCssVar() for Recharts

tsx
// WRONG — unnecessary helper
const color = getCssVar('--chart-1');
<Bar fill={color} />

// CORRECT — SVG accepts var() directly
<Bar fill="var(--chart-1)" />

4. Hardcoding hex in components

tsx
// 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)' }}>

5. Creating tailwind.config.ts

Tailwind v4 does NOT use tailwind.config.ts. All configuration is in CSS via @theme blocks. The ui-kit theme CSS file handles this.

6. Using old pe-* prefixed classes

tsx
// 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">

Related skills

  • policyengine-design-skill — Token values, color tables, chart branding
  • policyengine-frontend-builder-spec-skill — Mandatory technology requirements
  • policyengine-recharts-skill — Chart-specific patterns
  • policyengine-interactive-tools-skill — Standalone tool scaffolding

Expand your agent's capabilities with these related and highly-rated skills.

PolicyEngine/policyengine-claude

policyengine-healthcare

Healthcare program modeling in PolicyEngine-US — Medicaid, ACA marketplace, CHIP, and Medicare. Covers encoding rules, running analyses, and navigating the unique complexity of US healthcare programs. Triggers: "healthcare", "health insurance", "Medicaid", "ACA", "CHIP", "Medicare", "marketplace", "premium tax credit", "APTC", "PTC", "SLCSP", "benchmark plan", "rating area", "age curve", "family tier", "coverage gap", "Medicaid expansion", "MAGI", "medicaid_magi", "aca_magi", "medicaid_income_level", "medicaid_category", "enrollment", "takeup", "take-up", "per capita", "CSR", "cost sharing", "insurance premium", "second lowest silver", "required contribution percentage", "42 CFR", "IRC 36B", "categorical eligibility", "expansion adult", "healthcare reform", "healthcare analysis", "health policy".

26 5
Explore
PolicyEngine/policyengine-claude

policyengine-us

ALWAYS LOAD THIS SKILL FIRST before writing any PolicyEngine-US code. Contains the correct API patterns for household calculations and population simulations using the new policyengine package. Covers US federal and state taxes/benefits. Triggers: "what would", "how much would a", "benefit be", "eligible for", "qualify for", "single parent", "married couple", "family of", "household of", "if they earn", "earning $", "making $", "calculate benefits", "calculate taxes", "benefit for a", "what would I get", "what is the maximum", "what is the rate", "poverty line", "income limit", "benefit amount", "maximum benefit", "compare states", "TANF", "SNAP", "EITC", "CTC", "SSI", "WIC", "Section 8", "Medicaid", "ACA", "child tax credit", "earned income", "supplemental security", "housing voucher", "microsimulation", "population", "reform", "policy impact", "budgetary", "decile".

26 5
Explore
PolicyEngine/policyengine-claude

policyengine-uk

ALWAYS LOAD THIS SKILL FIRST before writing any PolicyEngine-UK code. Contains the correct API patterns for household calculations and population simulations using the new policyengine package (not policyengine_uk directly). Triggers: "what would", "how much would a", "benefit be", "eligible for", "qualify for", "single parent", "married couple", "family of", "household of", "if they earn", "with income of", "earning £", "making £", "calculate benefits", "calculate taxes", "benefit for a", "tax for a", "what would I get", "what would they get", "what is the rate", "what is the threshold", "personal allowance", "maximum benefit", "income limit", "benefit amount", "how much is", "Universal Credit", "child benefit", "pension credit", "housing benefit", "council tax", "income tax", "national insurance", "JSA", "ESA", "PIP", "disability living allowance", "working tax credit", "child tax credit", "Scotland", "Wales", "UK", "microsimulation", "population", "reform", "policy impact", "budgetary", "decile".

26 5
Explore
PolicyEngine/policyengine-claude

policyengine-canada

ALWAYS LOAD THIS SKILL FIRST before writing any PolicyEngine-Canada code. Contains Canadian federal and provincial tax/benefit rules for household calculations. IMPORTANT: PolicyEngine-Canada does NOT have representative population microdata. Do NOT attempt microsimulation or population-level estimates for Canada. Only provide household-level analysis (single-family impacts, eligibility, benefit amounts). Triggers: "what would", "how much would a", "benefit be", "eligible for", "qualify for", "single parent", "married couple", "family of", "household of", "if they earn", "earning $", "making $", "calculate benefits", "calculate taxes", "benefit for a", "what would I get", "what is the maximum", "what is the rate", "income limit", "benefit amount", "maximum benefit", "compare provinces", "CCB", "Canada Child Benefit", "GST credit", "HST credit", "GST/HST", "OAS", "Old Age Security", "GIS", "Guaranteed Income Supplement", "CWB", "Canada Workers Benefit", "EI", "Employment Insurance", "CPP", "Canada Pension Plan", "RRSP", "TFSA", "Ontario Child Benefit", "OCB", "Ontario Trillium Benefit", "OTB", "BC Climate Action", "Alberta Child Benefit", "Quebec", "CRA", "Canada Revenue Agency", "Canadian", "Canada", "Ontario", "British Columbia", "Alberta", "Saskatchewan", "Manitoba", "Nova Scotia", "New Brunswick", "PEI", "Newfoundland", "Yukon", "NWT", "Nunavut", "provincial tax", "federal tax Canada".

26 5
Explore
PolicyEngine/policyengine-claude

policyengine-ui-kit-consumer

This skill should be used when setting up a new project that uses @policyengine/ui-kit, debugging CSS or styling issues in a consumer app, or when Tailwind utility classes are not being generated. Also use when creating globals.css, configuring PostCSS, or troubleshooting "no styles", "no spacing", or "no layout" problems. Triggers: "ui-kit import", "globals.css setup", "Tailwind not working", "styles not applying", "utility classes missing", "setup ui-kit", "PostCSS config", "no styling", "CSS broken", "import ui-kit", "theme.css", "no layout", "no spacing", "@tailwindcss/postcss"

26 5
Explore
PolicyEngine/policyengine-claude

content-generation

Generate marketing content from PolicyEngine blog posts - social media images, social post copy, and branded assets

26 5
Explore

Didn't find tool you were looking for?

Be as detailed as possible for better results