
Figma Tokens → PrimeNG in Angular 20+: Storybook + Chromatic Guardrails for Accessible, Performant UI
Ship a real design system pipeline: Figma tokens feed PrimeNG/Angular components with Storybook docs and Chromatic visual tests—AA accessible, fast, and measurable.
If design tokens don’t ship to code with guardrails, they’re just pretty swatches.Back to all posts
The night before a release, I watched two “brand blues” battle on a PrimeNG table header—a classic Figma-to-code drift. After a decade shipping enterprise Angular dashboards (aviation kiosks, telecom analytics, insurance telematics), I’ve learned the fix isn’t a color tweak. It’s a pipeline.
As companies plan 2025 Angular roadmaps, here’s the pipeline I ship: Figma tokens → Nx token lib → CSS variables/SCSS maps → PrimeNG theme + Angular components → Storybook docs → Chromatic visual tests. You get AA-accessible UI, predictable density and typography, and measurable guardrails. If you need to hire an Angular developer or an Angular consultant to stand this up in weeks—not months—this is how I deliver.
This walkthrough uses Angular 20+, Signals/SignalStore for runtime preferences, PrimeNG (Lara/CSS variables), Storybook for Angular, Chromatic for visual regression, and Nx to keep the system modular.
Why Figma Tokens Must Wire to PrimeNG with CI Guardrails
From vibe-coded UI to measurable system
I’ve seen enterprise dashboards where an urgent KPI card subtly diverges from spec—wrong neutral, cramped spacing, off contrast. That turns into weeks of rework later. When we wired tokens to PrimeNG and enforced Chromatic snapshots at a telecom analytics client, we cut visual regressions by 80% and sustained 99.98% uptime across releases.
Design drifts when colors/spacing are duplicated in component SCSS.
PrimeNG ships fast, but un-tethered variables cause inconsistency.
CI needs eyes: snapshot every component state and theme.
Why it matters in Angular 20+
Signals/SignalStore handle runtime theme and density without zone churn. CSS variables keep payloads small and theming instantaneous. Storybook+Chromatic give PMs and designers confidence to sign off quickly.
Signals reduce change detection cost; tokens reduce visual entropy.
SSG/SSR benefit from CSS variables (small, cacheable).
Design tokens align a11y, perf budgets, and brand.
Nx Architecture: Tokens, Theme, and Components that Ship Together
Libraries that separate concerns
Keep tokens as a source of truth and generate outputs the rest of the stack consumes. The theme lib maps those to PrimeNG’s variables; components never import raw hex values.
libs/design-tokens: Figma JSON, generators, CSS variables.
libs/ui-theme: PrimeNG theme mapping, focus rings, density.
libs/ui-components: Angular + PrimeNG wrappers with tokens.
apps/storybook-host: Storybook for interactive docs.
Example workspace structure
apps/
demo/
storybook-host/
libs/
design-tokens/
src/tokens/figma-tokens.json
src/styles/_tokens.css
src/generators/convert-tokens.ts
ui-theme/
src/styles/_primeng-map.scss
src/styles/_a11y.scss
src/index.ts
ui-components/
src/lib/button/
src/lib/card/
Implement Tokens: From Figma JSON to CSS Variables and SCSS Maps
// libs/design-tokens/src/generators/convert-tokens.ts
import fs from 'node:fs';
import path from 'node:path';
import tokens from '../tokens/figma-tokens.json';
function toCssVars(obj: any, prefix: string[] = []) {
return Object.entries(obj)
.flatMap(([k, v]) =>
typeof v === 'object' ? toCssVars(v, [...prefix, k]) : [`--${[...prefix, k].join('-')}: ${v};`]
)
.join('\n');
}
const light = `:root{\n${toCssVars(tokens)}\n}`;
fs.mkdirSync(path.resolve(__dirname, '../styles'), { recursive: true });
fs.writeFileSync(path.resolve(__dirname, '../styles/_tokens.css'), light);
console.log('Generated tokens -> _tokens.css');/* libs/ui-theme/src/styles/_primeng-map.scss */
@import '../../design-tokens/src/styles/tokens'; // postcss alias or copy step
:root {
/* Map AngularUX tokens → PrimeNG (Lara CSS vars) */
--primary-500: var(--color-brand-500);
--primary-700: var(--color-brand-700);
--surface-ground: var(--color-neutral-50);
--surface-card: var(--color-neutral-0);
--text-color: var(--color-neutral-900);
--highlight-bg: color-mix(in srgb, var(--color-brand-50) 80%, white);
/* Typography and radius */
--font-family: var(--typography-fontFamily-base);
--font-size: var(--typography-size-md);
--border-radius: var(--radius-md);
/* Spacing scale (used by PrimeFlex utilities) */
--space-2: var(--space-2);
--space-3: var(--space-3);
--space-4: var(--space-4);
--space-6: var(--space-6);
}Sample Figma tokens (AngularUX palette)
{"color":{"brand":{"50":"#e6fbfa","100":"#bff5f2","500":"#0ea5a4","700":"#0b7c7b"},"accent":{"500":"#f97316"},"neutral":{"0":"#ffffff","50":"#f8fafc","900":"#0f172a"},"info":{"500":"#0ea5ff"},"success":{"500":"#16a34a"},"warning":{"500":"#f59e0b"},"error":{"500":"#ef4444"}},"typography":{"fontFamily":{"base":"Inter, ui-sans-serif, system-ui"},"size":{"sm":"0.875rem","md":"1rem","lg":"1.125rem"}},"space":{"2":"0.5rem","3":"0.75rem","4":"1rem","6":"1.5rem"},"radius":{"sm":"6px","md":"8px"}}
Convert tokens to CSS variables at build
Use a tiny generator (run in Nx target) that writes tokens.css.
Generate :root variables and provide dark theme tokens.
Ship a single tokens.css to app and Storybook.
Wire Tokens to PrimeNG and Angular Components
/* styles.scss (app & Storybook host) */
@use 'primeng/resources/primeng.css';
@use 'primeicons/primeicons.css';
@use '@angularux/ui-theme/styles/primeng-map';
@use '@angularux/ui-theme/styles/a11y';// libs/ui-theme/src/index.ts
import { Injectable, inject } from '@angular/core';
import { SignalStore, withState, patchState } from '@ngrx/signals';
type Density = 'compact'|'cozy'|'comfortable';
interface ThemeState { theme: 'light'|'dark'; density: Density; fontScale: number; }
@Injectable({ providedIn: 'root' })
export class ThemeStore extends SignalStore(withState<ThemeState>({ theme: 'light', density: 'cozy', fontScale: 1 })) {
setTheme(theme: 'light'|'dark') { patchState(this, { theme }); document.documentElement.dataset.theme = theme; }
setDensity(density: Density) { patchState(this, { density }); document.documentElement.dataset.density = density; }
setFontScale(fontScale: number) { patchState(this, { fontScale }); document.documentElement.style.setProperty('--font-size', `${fontScale}rem`); }
}<!-- libs/ui-components/src/lib/button/button.component.html -->
<p-button [label]="label" [style]="{ 'borderRadius': 'var(--border-radius)' }" class="ux-btn"></p-button>/* libs/ui-components/src/lib/button/button.component.scss */
.ux-btn {
--_focus: color-mix(in srgb, var(--color-brand-500) 40%, white);
&:focus-visible { outline: 3px solid var(--_focus); outline-offset: 2px; }
&[data-variant="danger"] { --primary-500: var(--color-error-500); }
}// Highcharts theme (app-level) using tokens
import * as Highcharts from 'highcharts';
Highcharts.setOptions({
colors: [
getComputedStyle(document.documentElement).getPropertyValue('--color-brand-500').trim(),
getComputedStyle(document.documentElement).getPropertyValue('--accent-500').trim(),
getComputedStyle(document.documentElement).getPropertyValue('--color-info-500').trim()
],
chart: { style: { fontFamily: 'var(--font-family)' } }
});PrimeNG global theme include
Include the mapped variables before PrimeNG so components inherit.
Component-level token usage
PrimeNG components can be lightly wrapped to expose token-aware APIs.
Never hardcode hex; use CSS variables.
Respect density and font scale tokens.
Data viz mapping (Highcharts/D3)
I frequently theme Highcharts in telecom dashboards using CSS variables so designers can validate palettes in Storybook matrices.
Use CSS variables for series colors.
Respect dark mode automatically.
Accessibility, Typography, and Density Controls as First-Class Tokens
/* libs/ui-theme/src/styles/_a11y.scss */
:root {
--focus-ring: color-mix(in srgb, var(--color-brand-500) 30%, white);
}
:root[data-density="compact"] { --row-pad: 0.375rem; --ctrl-pad-y: 0.25rem; }
:root[data-density="cozy"] { --row-pad: 0.5rem; --ctrl-pad-y: 0.375rem; }
:root[data-density="comfortable"] { --row-pad: 0.75rem; --ctrl-pad-y: 0.5rem; }
.p-datatable .p-datatable-tbody > tr > td { padding-block: var(--row-pad); }
.p-inputtext { padding-block: var(--ctrl-pad-y); }
*:focus-visible { outline: 3px solid var(--focus-ring); outline-offset: 2px; }AA contrast baked in
For a device management portal, we caught a contrast regression on a warning badge in Chromatic before it shipped.
Contrast tested in Storybook via a11y add-on.
Focus rings are tokens, not ad-hoc styles.
Density without CSS churn
Compact density improved above-the-fold KPIs on a telecom analytics dashboard by 12% without hurting readability.
Use data-density on :root and attribute selectors.
Table rows, inputs, and buttons respond to density scale.
Typography scale
Engineers and designers preview small/large text on complex PrimeNG tables without guessing.
Font scale token drives rem base across app and viz.
Storybook controls verify wrapping/truncation states.
Storybook for Angular: Token Docs, Controls, and A11y Checks
// .storybook/preview.ts
import type { Preview } from '@storybook/angular';
import '../libs/design-tokens/src/styles/_tokens.css';
import '../libs/ui-theme/src/styles/_primeng-map.scss';
import '../libs/ui-theme/src/styles/_a11y.scss';
const preview: Preview = {
parameters: {
actions: { argTypesRegex: '^on.*' },
controls: { expanded: true },
a11y: { element: '#root', config: {}, options: {} }
},
globalTypes: {
theme: { defaultValue: 'light', toolbar: { items: ['light','dark'] } },
density: { defaultValue: 'cozy', toolbar: { items: ['compact','cozy','comfortable'] } }
},
decorators: [
(story, context) => {
const root = document.documentElement;
root.dataset.theme = context.globals['theme'];
root.dataset.density = context.globals['density'];
return story();
}
]
};
export default preview;// Button.stories.ts
import { Meta, StoryObj } from '@storybook/angular';
import { UxButtonComponent } from '@angularux/ui-components/button';
export default {
title: 'UX/Button',
component: UxButtonComponent,
args: { label: 'Save' }
} as Meta<UxButtonComponent>;
export const Default: StoryObj<UxButtonComponent> = {};
export const Danger: StoryObj<UxButtonComponent> = { args: { label: 'Delete', variant: 'danger' } };Preview configuration
Storybook renders the same CSS variables the app uses, so visual verification is real.
Provide global styles (tokens and PrimeNG).
Add a11y and interactions add-ons.
Token-aware stories
PMs can approve compact density on tables or test focus order on dialogs before sprint demo.
Controls for theme, density, font scale.
Per-state stories: hover, focus, error.
Chromatic in CI: Visual Budgets that Stop Token Drift
# .github/workflows/visual-regression.yml
name: Visual Regression
on: [pull_request]
jobs:
chromatic:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with: { version: 9 }
- run: pnpm install
- run: pnpm nx run storybook-host:build-storybook
- name: Publish to Chromatic
uses: chromaui/action@v1
with:
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
autoAcceptChanges: false
exitZeroOnChanges: false
onlyChanged: true
diagnostics: true
budgets:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pnpm install
- run: pnpm nx run demo:build --configuration=production
- run: node tools/check-css-budget.cjs 102400 # 100KB gzConnect GitHub Actions to Chromatic
Chromatic snapshots every story including compact/comfortable and light/dark. PMs can approve diffs within minutes.
Matrix test: theme x density.
Require check on main.
Perf and a11y alongside visuals
On gitPlumbers we target <100KB CSS (gz) and block merges if exceeded.
Run Lighthouse and Storybook a11y in CI.
CSS bundle budget keeps things lean.
How an Angular Consultant Productionizes This Tokens→PrimeNG Pipeline
Typical engagement timeline
Discovery call within 48 hours; assessment delivered in 1 week. I’ve done this in regulated environments (insurance telematics, enterprise employee tracking) where audit logs and AA compliance matter.
Week 1: Audit tokens, PrimeNG theme, accessibility gaps.
Week 2: Nx libs, generator, Storybook baseline.
Week 3: Chromatic + CI, density/typography controls.
Week 4: Rollout to top components, training, handoff.
Measurable outcomes
Your team gets a living spec: tokens, docs, and snapshots tied to actual components. If you need a remote Angular developer with Fortune 100 experience, I’m available for hire.
80% fewer visual regressions after Chromatic.
AA focus/contrast coverage in Storybook.
<100KB gz CSS budget sustained across releases.
When to Hire an Angular Developer to Fix Token→Theme Drift
Signals you need help
If this sounds familiar, bring in an Angular expert who has stabilized chaotic codebases. On one airport kiosk project, tokens plus Docker hardware simulation let us ship offline‑tolerant UX with consistent visuals across scanners and printers.
Design QA catches color/spacing issues late in sprint.
Multiple PrimeNG overrides per component with copy‑pasted hex.
Dark mode and density handled via per-component flags, not root tokens.
A11y bugs reappear after “fixes” due to lack of visual tests.
Takeaways and Next Steps
- Figma tokens are the source; CSS variables and SCSS maps are the transport; PrimeNG is the consumer; Storybook/Chromatic are the guardrails.
- Treat accessibility, typography, and density as tokens. Prove them in stories. Enforce in CI.
- Keep performance budgets visible: CSS <100KB gz, LCP improvements via lean theming, zero color drift.
- If you want this wired into your Angular 20+ app, let’s talk roadmaps, timelines, and risks.
Key takeaways
- Export Figma tokens once, generate CSS variables/SCSS maps, and map to PrimeNG’s CSS variable theme to avoid color drift.
- Use an Nx workspace to isolate libs for tokens, theming, and components. Storybook consumes the same tokens the app ships.
- Guard the visual language with Chromatic in CI: per-component snapshots, theme/density matrices, and budgeted diffs.
- Accessibility is a first-class token: spacing, focus rings, and contrast ratios enforced in stories and Chromatic thresholds.
- Use a small SignalStore to switch theme/density/typography at runtime without repaint storms.
- Instrument and measure: bundle budgets for CSS, Lighthouse and Core Web Vitals, and Storybook a11y add-on failures block merges.
Implementation checklist
- Create an Nx lib for tokens and generate CSS variables from Figma JSON.
- Map tokens to PrimeNG theme variables (Lara/Tailwind) and component styles.
- Add runtime ThemeStore with Signals/SignalStore for density and font scale.
- Document components in Storybook with token tables and interactive controls.
- Wire Chromatic in GitHub Actions with visual diff budgets and required status.
- Add accessibility stories (focus, keyboard, contrast) and enforce AA thresholds.
- Set CSS bundle budgets and track Lighthouse/Core Web Vitals in CI.
Questions we hear from teams
- How long does it take to connect Figma tokens to PrimeNG with Storybook and Chromatic?
- Most teams see a working pipeline in 3–4 weeks: audit and plan (week 1), Nx libs and token generator (week 2), Storybook baseline and Chromatic (week 3), rollout to top components and training (week 4).
- Do we need to rewrite our PrimeNG styles to adopt tokens?
- No. Map tokens to PrimeNG’s CSS variables and progressively remove hardcoded values. Wrap high-traffic components first (buttons, tables, forms), then expand coverage. Minimal churn, measurable wins.
- Will this improve accessibility?
- Yes. Contrast, focus rings, spacing, and typography become tokens with a11y stories and CI checks. Teams typically achieve WCAG 2.1 AA for core components faster and prevent regressions with Chromatic.
- What about performance budgets?
- CSS variables keep themes lean and cacheable. We enforce CSS bundle budgets (e.g., <100KB gz), run Lighthouse in CI, and monitor Core Web Vitals. Tokens prevent dead CSS bloat from scattered overrides.
- What does a typical engagement with Matthew look like?
- Discovery call within 48 hours, an assessment deck in 1 week, and a 3–4 week implementation. Remote, hands-on, collaborating with design and engineering. Available as an Angular consultant or contract Angular developer.
Ready to level up your Angular experience?
Let AngularUX review your Signals roadmap, design system, or SSR deployment plan.
NG Wave
Angular Component Library
A comprehensive collection of 110+ animated, interactive, and customizable Angular components. Converted from React Bits with full feature parity, built with Angular Signals, GSAP animations, and Three.js for stunning visual effects.
Explore Components