npx claudepluginhub policyengine/policyengine-claude --plugin data-scienceThis skill uses the workspace's default tool permissions.
Single source of truth for PolicyEngine's visual identity. Design tokens are defined as CSS custom properties in `@policyengine/ui-kit/theme.css`. Every frontend project imports this single CSS file.
Senior-level UI/UX design expert for building data-driven, premium production interfaces. Use when you need to: 1. Design complex applications (dashboards, SaaS, AI tools) from scratch 2. Generate comprehensive design systems (tokens, palettes, typography) 3. Audit existing UI for quality, accessibility, and "craft" 4. Search for proven real-world design patterns and implementation details Trigger: "design a...", "audit this...", "create a design system", "find icons", "fintech dashboard", "landing page"
Provides UI/UX design intelligence: 50+ styles, 161 color palettes, 57 font pairings, 99 guidelines, 25 charts across React, Next.js, Vue, Svelte, Tailwind, shadcn/ui, mobile stacks. For planning, building, reviewing web/mobile UIs.
Provides prioritized UI/UX guidelines for web and mobile apps covering accessibility, touch interactions, performance, responsive layouts, typography, color palettes, animations, style selection, and charts. Use for designing components, pages, or reviewing UX issues.
Share bugs, ideas, or general feedback.
Single source of truth for PolicyEngine's visual identity. Design tokens are defined as CSS custom properties in @policyengine/ui-kit/theme.css. Every frontend project imports this single CSS file.
When to use which format:
| Context | Approach | Example |
|---|---|---|
| React components | Tailwind semantic classes | className="bg-primary text-foreground" |
| Brand palette | Tailwind direct classes | className="bg-teal-500 text-gray-600" |
| Recharts (SVG) | CSS vars directly in fill/stroke | fill="var(--chart-1)" |
| Inline styles | CSS vars | style={{ color: "var(--primary)" }} |
| Python (Plotly) | Hex with CSS var comment | TEAL = "#319795" # --chart-1 |
<meta> tags, static HTML | Hex values with CSS var name in comment | content="#319795" |
Python has no CSS runtime, so hex values are acceptable — but always comment with the CSS var name so values stay traceable to the design system.
Install:
bun install @policyengine/ui-kit
Import the theme CSS in your globals.css:
@import "tailwindcss";
@import "@policyengine/ui-kit/theme.css";
The first line enables Tailwind v4 utilities. The second provides all PE design tokens, @theme configuration, and base styles. Both are required — see policyengine-ui-kit-consumer-skill for details.
Source: PolicyEngine/policyengine-ui-kit/src/theme/tokens.css
The theme CSS has three layers:
:root — shadcn/ui semantic variables (--primary, --background, --chart-1, etc.)@theme inline — Bridges :root vars to Tailwind utilities (bg-primary, text-foreground)@theme — Brand palette (bg-teal-500, text-gray-600), font sizes, spacing, breakpoints| Token | Hex | Tailwind class | Usage |
|---|---|---|---|
teal-500 | #319795 | bg-teal-500 | Main brand color — charts, highlights |
teal-400 | #38B2AC | bg-teal-400 | Lighter interactive elements |
teal-600 | #2C7A7B | bg-teal-600 / bg-primary | Hover state, buttons |
teal-700 | #285E61 | bg-teal-700 | Active/pressed state |
teal-50 | #E6FFFA | bg-teal-50 | Tinted backgrounds |
teal-800 | #234E52 | bg-teal-800 | Dark text on light teal |
| Role | CSS variable | Tailwind class | Hex |
|---|---|---|---|
| Primary | --primary | bg-primary | #2C7A7B |
| Background | --background | bg-background | #FFFFFF |
| Foreground | --foreground | text-foreground | #000000 |
| Muted | --muted | bg-muted | #F2F4F7 |
| Muted foreground | --muted-foreground | text-muted-foreground | #6B7280 |
| Border | --border | border-border | #E2E8F0 |
| Destructive | --destructive | bg-destructive | #DC2626 |
| Card | --card | bg-card | #FFFFFF |
| Ring | --ring | ring-ring | #319795 |
| CSS variable | Tailwind class | Hex | Usage |
|---|---|---|---|
--chart-1 | fill-chart-1 | #319795 | Primary series (teal) |
--chart-2 | fill-chart-2 | #0EA5E9 | Secondary series (blue) |
--chart-3 | fill-chart-3 | #285E61 | Tertiary series (dark teal) |
--chart-4 | fill-chart-4 | #026AA2 | Quaternary series (dark blue) |
--chart-5 | fill-chart-5 | #6B7280 | Quinary series (gray) |
These are the brand fill values — use for status dots, badges, and tinted surfaces. They are not WCAG-AA-compliant when set as text on a white background; for text, use the accessible-on-white variants below.
| Color | Hex | Tailwind class |
|---|---|---|
| Success | #22C55E | text-success / bg-success |
| Error | #DC2626 | text-destructive / bg-destructive |
| Warning | #FEC601 | text-warning / bg-warning |
| Info | #1890FF | text-info / bg-info |
Distinct from the brand fills above — these are the values you use when you actually need to render colored text on a white background and want to clear WCAG 2.2 AA (4.5:1 contrast at small text). Available since ui-kit 0.5.0 as --text-warning / --text-error / --text-success and the corresponding Tailwind utilities.
| Token | Hex | Tailwind class | Contrast on white |
|---|---|---|---|
--text-warning | #c2410c (Tailwind orange-700) | text-warning-foreground | 5.18:1 ✓ |
--text-error | #B91C1C (Tailwind red-700) | text-error-foreground | 5.94:1 ✓ |
--text-success | #285E61 (PE teal-700) | text-success-foreground | 7.07:1 ✓ |
The plain text-warning / text-destructive / text-success brand classes are intentionally not AA on white — they're meant for soft-tinted backgrounds and badge fills, not paragraph text.
Available since ui-kit 0.6.0. Activate by adding class="dark" to any ancestor element — no JS or media query required. Every shadcn semantic token (--primary, --background, --card, --border, etc.) and accessible text variant has a dark-mode value pinned to clear AA on the dark page background #0B0E14. Dark-mode values are tested by tests/theme/contrast.test.ts in ui-kit and exposed under the :root.dark selector in theme.css.
<!-- consumer site dark-mode toggle: just toggle the .dark class on <html> -->
<html class="dark">
...
</html>
If you're rendering charts, prefer the CSS-var form (fill="var(--chart-1)") over a hex literal — that way the chart picks up dark-mode swaps automatically. For Plotly / Python where there's no CSS runtime, use chartPalette.light and chartPalette.dark from @policyengine/ui-kit and pick by detected theme.
ui-kit's palette.gray is Slate-flavored (matches the dashboard's neutral surfaces and --border / --muted tokens). The legacy @policyengine/ui-kit/legacy/tokens shim still exports the Tailwind-3 grays (#6B7280 for 500, #4B5563 for 600) for design-system migration parity, but new code should use the canonical Slate values below.
| Token | Hex | Tailwind class |
|---|---|---|
gray-50 | #F0F9FF | bg-gray-50 |
gray-100 | #F2F4F7 | bg-gray-100 |
gray-200 | #E2E8F0 | bg-gray-200 |
gray-300 | #CBD5E1 | bg-gray-300 |
gray-400 | #94A3B8 | bg-gray-400 |
gray-500 | #64748B | text-gray-500 |
gray-600 | #475569 | text-gray-600 |
gray-700 | #344054 | text-gray-700 |
gray-800 | #1E293B | text-gray-800 |
gray-900 | #101828 | text-gray-900 |
Two font families only: Inter + JetBrains Mono. No serif fonts, no Roboto, no Public Sans.
| Context | Font | CSS variable | Tailwind |
|---|---|---|---|
| Everything (UI, charts, blog, tools) | Inter | --font-sans | font-sans |
| Code | JetBrains Mono | --font-mono | font-mono |
Loading Inter:
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
| Tailwind class | Size | Usage |
|---|---|---|
text-xs | 12px | Small labels, captions |
text-sm | 14px | Body text, form labels |
text-base | 16px | Large body text |
text-lg | 18px | Subheadings |
text-xl | 20px | Section titles |
text-2xl | 24px | Page titles |
text-3xl | 28px | Large headings |
All UI text uses sentence case — capitalize only the first word and proper nouns.
Standard Tailwind spacing classes (p-4, gap-2, m-6) use the default Tailwind scale. Named spacing tokens:
| Token | Value | Tailwind class |
|---|---|---|
| Header | 58px | h-header |
| Sidebar | 280px | w-sidebar |
| Content | 976px | max-w-content |
| Tailwind class | Value |
|---|---|
rounded-sm | 4px |
rounded-md | 6px |
rounded-lg | 8px |
import { BarChart, Bar, XAxis, YAxis, Tooltip } from "recharts";
<BarChart data={data}>
<XAxis dataKey="name" niceTicks="snap125" domain={["auto", "auto"]} style={{ fontFamily: "var(--font-sans)" }} />
<YAxis niceTicks="snap125" domain={["auto", "auto"]} style={{ fontFamily: "var(--font-sans)" }} />
<Tooltip separator=": " />
<Bar dataKey="value" fill="var(--chart-1)" />
</BarChart>
SVG fill and stroke attributes accept var() directly — no helper function needed.
import plotly.graph_objects as go
TEAL = "#319795" # --chart-1
CHART_FONT = "Inter"
LOGO_URL = "https://raw.githubusercontent.com/PolicyEngine/policyengine-app-v2/main/app/public/assets/logos/policyengine/teal.png"
def format_fig(fig):
fig.update_layout(
font=dict(family=CHART_FONT, color="black", size=14),
plot_bgcolor="white",
paper_bgcolor="white",
template="plotly_white",
height=600,
width=800,
margin=dict(l=60, r=40, t=40, b=60),
modebar=dict(bgcolor="rgba(0,0,0,0)", color="rgba(0,0,0,0)"),
)
fig.add_layout_image(dict(
source=LOGO_URL,
xref="paper", yref="paper",
x=1.0, y=-0.10,
sizex=0.10, sizey=0.10,
xanchor="right", yanchor="bottom",
))
return fig
| Meaning | CSS variable | Hex |
|---|---|---|
| Positive / bonus / gains | --chart-1 | #319795 |
| Negative / penalty / losses | --chart-5 or --destructive | #6B7280 or #DC2626 |
| Neutral / baseline | --border | #E2E8F0 |
| Multi-series | --chart-1 through --chart-5 | See chart table above |
Inverted metrics (taxes): When a positive delta means bad (higher taxes), use invertDelta logic to show "Penalty" label and swap colors.
var(--font-sans), 14pxvar(--font-sans), 12pxvar(--font-sans), horizontal, above chartEvery PolicyEngine dashboard must include a favicon. The ui-kit exports the logo as a favicon-ready SVG:
cp node_modules/@policyengine/ui-kit/src/assets/logos/policyengine/teal-square.svg public/favicon.svglayout.tsx metadata:
export const metadata: Metadata = {
// ...
icons: { icon: '/favicon.svg' },
};
The ui-kit also exports logos.favicon (SVG) and logos.faviconPng (PNG fallback) for programmatic use.
All logo files in policyengine-app-v2/app/public/assets/logos/policyengine/:
| File | Background | Format |
|---|---|---|
teal.png / teal.svg | Light | Wide |
teal-square.png / teal-square.svg | Light | Square (for chart watermarks) |
white.png / white.svg | Dark | Wide |
white-square.svg | Dark | Square |
Raw URL for charts:
https://raw.githubusercontent.com/PolicyEngine/policyengine-app-v2/main/app/public/assets/logos/policyengine/teal.png
| Project type | Token source | Font setup |
|---|---|---|
| Standalone tool | @import "@policyengine/ui-kit/theme.css" | Google Fonts: Inter |
| app-v2 | import { colors } from '@/designTokens' | Built-in (Mantine + Inter) |
| Python chart | Hardcode or load tokens.json from @policyengine/design-system | Inter for Plotly |
| Blog HTML | Hardcode from token values | Google Fonts: Inter |
#319795 on white passes WCAG AA for large text (3.8:1)text-foreground (#000000) on white passes AAA (21:1)text-muted-foreground (#6B7280) on white passes AA (4.6:1)policyengine-interactive-tools-skill — Building standalone tools that use these tokenspolicyengine-vercel-deployment-skill — Deploying standalone toolspolicyengine-app-skill — app-v2 developmentpolicyengine-writing-skill — Content style (complements visual style)