Agent skill

policyengine-app

Developing policyengine-app-v2 — the main React frontend for policyengine.org

Stars 26
Forks 5

Install this agent skill to your Project

npx add-skill https://github.com/PolicyEngine/policyengine-claude/tree/main/skills/tools-and-apis/policyengine-app-skill

SKILL.md

PolicyEngine app (v2)

Architecture and patterns for developing the main PolicyEngine web application at policyengine.org.

Repository: PolicyEngine/policyengine-app-v2

Architecture

Monorepo

policyengine-app-v2/
├── packages/
│   └── design-system/          # @policyengine/design-system (npm)
├── app/                        # Main Vite application
│   ├── src/
│   │   ├── pages/              # Page components (*.page.tsx)
│   │   ├── components/         # Shared UI (charts, layouts, modals)
│   │   ├── routing/            # Guards, router config
│   │   ├── hooks/              # Custom React hooks
│   │   ├── designTokens/       # Re-exports from design-system
│   │   ├── styles/             # Mantine theme, global PostCSS
│   │   ├── data/               # Static data (apps.json, posts/)
│   │   ├── adapters/           # API fetch wrappers
│   │   ├── api/                # React Query hooks
│   │   ├── contexts/           # React Context providers
│   │   ├── types/              # TypeScript interfaces
│   │   └── utils/              # Formatters, helpers
│   ├── public/                 # Static assets (logos, post images)
│   └── vite.config.mjs
├── turbo.json
└── package.json                # Bun workspaces root

Tech stack

Layer Technology
Package manager Bun (primary), npm fallback
Build Vite + Turbo
UI framework Mantine v8
Routing React Router v7 (createBrowserRouter)
Charts Recharts (standard), Plotly (maps only)
Server state React Query
Design tokens @policyengine/design-system
Language TypeScript
Formatting Prettier + ESLint
Testing Vitest

Dual SPA mode

VITE_APP_MODE controls which entry point builds:

  • website — Full policyengine.org (pages, blog, research, embedded tools)
  • calculator — Standalone calculator at app.policyengine.org

Development

bash
bun install                          # Install dependencies
bun run dev                          # Dev server (builds design-system first)
cd app && bun run prettier -- --write .  # Format before committing
bun run lint                         # Lint (CI uses --max-warnings 0)
bun run build                        # Production build
bun run test                         # Tests

CRITICAL: Frontend verification and troubleshooting

How to verify the frontend works

The ONLY reliable way to verify the frontend compiles correctly is bun run build. This catches import errors, TypeScript errors, and missing dependencies.

curl is NOT verification. Vite serves an HTML shell regardless of whether React components actually render. A 200 from curl means nothing for an SPA. Never use curl output as evidence that the frontend works.

You cannot visually verify a frontend. After the build passes and dev server starts, tell the user it's ready for them to check in the browser. Do not claim it "looks good."

Before making any claim about dev server status, check with lsof -i :5173. Do not assume it is running.

Correct verification sequence

bash
bun run build                        # Catches all compile-time errors
# If build passes and dev server needed:
cd app && VITE_APP_MODE=website ./node_modules/.bin/vite --port 5173 &
open http://localhost:5173/us         # User checks visually

Dependency troubleshooting

When bun install fails:

  1. Read the error. Fix the specific issue.
  2. Try at most 2 approaches before asking the user for help.
  3. Hard stops — never do these:
    • rm -rf node_modules without user approval
    • Manually npm pack + tar -xzf packages into node_modules
    • Editing lockfile internals
    • Looping through increasingly obscure npm/bun flags

Design tokens

Import from @/designTokens (convenience layer that re-exports from the design-system package):

tsx
import { colors, spacing, typography } from '@/designTokens';

Never hardcode values:

tsx
// Wrong
style={{ color: '#319795', marginBottom: '16px' }}

// Correct
style={{ color: colors.primary[500], marginBottom: spacing.lg }}

See policyengine-design-skill for the full token reference.

Mantine components

All UI uses Mantine v8. Key components:

tsx
import { Stack, Group, Text, Button, Paper } from '@mantine/core';
import { colors, spacing } from '@/designTokens';

function PolicyCard({ title, description, onEdit }) {
  return (
    <Paper p={spacing.lg} withBorder>
      <Stack gap={spacing.sm}>
        <Text fw={600}>{title}</Text>
        <Text c={colors.text.secondary} fz="sm">{description}</Text>
        <Button variant="outline" onClick={onEdit}>Edit</Button>
      </Stack>
    </Paper>
  );
}
Component Usage
Stack, Group, Box Layout
Text, Title Typography
Button, ActionIcon Controls
Paper, Card Containers
Table, Modal, Tooltip Complex UI
TextInput, Select, NumberInput Forms

Charts

Recharts (standard for all new charts)

tsx
import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts';
import { ChartContainer, ChartWatermark, ImpactTooltip } from '@/components/charts';
import { colors } from '@/designTokens';

function RevenueChart({ data }) {
  return (
    <ChartContainer title="Revenue impact" csvData={data}>
      <ResponsiveContainer width="100%" height={400}>
        <BarChart data={data}>
          <XAxis dataKey="name" />
          <YAxis tickFormatter={(v) => v.toLocaleString('en-US', {
            style: 'currency', currency: 'USD', notation: 'compact', maximumFractionDigits: 1,
          })} />
          <Tooltip content={<ImpactTooltip />} />
          <Bar dataKey="value" fill={colors.primary[500]} />
        </BarChart>
      </ResponsiveContainer>
      <ChartWatermark />
    </ChartContainer>
  );
}

Semantic chart colors

Meaning Token
Primary data colors.primary[500]
Secondary colors.gray[400]
Positive/gains colors.success
Negative/losses colors.gray[600]
Error colors.error

Plotly (maps only)

Plotly is only used for geographic visualizations (choropleths, hex maps). All other charts use Recharts.

Chart components

Component Purpose
ChartContainer Card wrapper with title, CSV download
ChartWatermark PolicyEngine logo below chart
ImpactTooltip Formatted hover tooltip
ImpactBarLabel Values above/below bars

Routing

Routes in app/src/WebsiteRouter.tsx:

/:countryId/
  ├── (StaticLayout)
  │   ├── home (index)
  │   ├── research/:slug (blog posts)
  │   ├── brand/*, team, donate
  │   └── model
  ├── (AppLayout)
  │   └── :slug → AppPage.tsx (apps.json)
  └── (full-page embeds)

Country guards

  • CountryGuardSimple — Validates countryId, redirects to default
  • CountryAppGuard — Validates slug+countryId for apps

Adding a new page

  1. Create app/src/pages/MyPage.page.tsx
  2. Add route in WebsiteRouter.tsx under appropriate layout
  3. Use useParams() for countryId

Embedded apps (apps.json)

Interactive tools register in app/src/data/apps/apps.json and render via AppPage.tsx:

json
{
  "type": "iframe",
  "slug": "marriage",
  "title": "Marriage calculator",
  "source": "https://marriage-zeta-beryl.vercel.app/",
  "countryId": "us",
  "displayWithResearch": true
}

See policyengine-interactive-tools-skill for the full embedding pattern.

Ingredient CRUD pages

Policies, Reports, Simulations, and Populations follow a shared pattern using IngredientReadView and RenameIngredientModal. See app/.claude/skills/ingredient-patterns.md for details.

Blog posts

Markdown files in app/src/data/posts/articles/. Metadata in posts.json.

Sentence case

Strictly enforced everywhere:

tsx
// Correct
<Title order={2}>Your saved policies</Title>

// Wrong
<Title order={2}>Your Saved Policies</Title>

Exceptions: proper nouns (PolicyEngine), acronyms (IRS), official names (Child Tax Credit).

Deployment

  • Automatic on push to main via Vercel
  • Domain: policyengine.org (website), app.policyengine.org (calculator)
  • Team: policy-engine scope

Key files

File Purpose
app/src/WebsiteRouter.tsx Main routes
app/src/pages/AppPage.tsx Renders embedded apps
app/src/data/apps/apps.json Tool registry
app/src/designTokens/ Token imports
app/src/components/charts/ Chart components
packages/design-system/ Token source of truth
app/.claude/skills/ Local skills (design-tokens, chart-standards, ingredient-patterns)

URL patterns

Production domains

Domain Purpose
policyengine.org Marketing website, research, blog
app.policyengine.org Calculator app (policies, households, reports)

Calculator URLs (app.policyengine.org)

app.policyengine.org/:countryId/                             # Dashboard
app.policyengine.org/:countryId/policies                     # Saved policies
app.policyengine.org/:countryId/policies/create               # Policy builder
app.policyengine.org/:countryId/households                    # Saved households
app.policyengine.org/:countryId/households/create             # Household builder
app.policyengine.org/:countryId/reports                       # Saved reports
app.policyengine.org/:countryId/reports/create                # Report builder
app.policyengine.org/:countryId/simulations                   # Saved simulations
app.policyengine.org/:countryId/simulations/create            # Simulation builder
app.policyengine.org/:countryId/report-output/:reportId       # Report output (overview)
app.policyengine.org/:countryId/report-output/:reportId/:subpage/:view  # Specific chart

Report output subpages and views:

/report-output/:reportId/budget                               # Budget overview
/report-output/:reportId/distributional/incomeDecile          # Distributional by income
/report-output/:reportId/distributional/wealthDecile          # Distributional by wealth
/report-output/:reportId/winners-losers/incomeDecile          # Winners/losers by income
/report-output/:reportId/winners-losers/wealthDecile          # Winners/losers by wealth
/report-output/:reportId/poverty/age                          # Poverty by age
/report-output/:reportId/poverty/gender                       # Poverty by gender
/report-output/:reportId/poverty/race                         # Poverty by race (US only)
/report-output/:reportId/deep-poverty/age                     # Deep poverty by age
/report-output/:reportId/deep-poverty/gender                  # Deep poverty by gender
/report-output/:reportId/inequality                           # Inequality measures

Country IDs: us, uk, ca, ng, il

Website URLs (policyengine.org)

policyengine.org/:countryId/                     # Country home
policyengine.org/:countryId/research             # Research index
policyengine.org/:countryId/research/:slug       # Research article
policyengine.org/:countryId/blog                 # Blog index
policyengine.org/:countryId/blog/:postName       # Blog post
policyengine.org/:countryId/model                # Policy model explorer
policyengine.org/:countryId/:slug                # Embedded app (from apps.json)

Legacy v1 URLs (DO NOT USE)

The old policyengine-app (v1) used a different URL pattern that no longer works:

# WRONG — v1 format, returns "App not found"
policyengine.org/us/policy?reform=73278&baseline=2&region=enhanced_us&timePeriod=2025
policyengine.org/us/reform/2/280039/over/2/us?focus=policyOutput.winnersAndLosers.incomeDecile

Always use app.policyengine.org for calculator functionality.

Related skills

  • policyengine-design-skill — Full token reference
  • policyengine-interactive-tools-skill — Building standalone tools
  • policyengine-vercel-deployment-skill — Deployment patterns
  • policyengine-writing-skill — Content style
  • policyengine-api-skill — Backend API

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

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"

26 5
Explore

Didn't find tool you were looking for?

Be as detailed as possible for better results