
Figma Tokens → PrimeNG in Angular 20+: Storybook + Chromatic as Your Visual Contract
Turn your Figma design tokens into a measurable, versioned theme system that powers PrimeNG, D3/Highcharts, and role‑based dashboards—with Storybook and Chromatic guarding every pixel.
Design tokens aren’t real until Storybook proves them and Chromatic refuses to merge when they drift.Back to all posts
I’ve shipped design systems for Fortune 100 dashboards where a color tweak can ripple through 50+ screens, a PrimeNG data table, and a Highcharts time series. If those tokens don’t round-trip from Figma to Angular 20+ with Storybook and Chromatic in the loop, you’re flying blind—and someone’s Friday night becomes a hotfix.
This is the pipeline I use on AngularUX and client work: Figma tokens → Style Dictionary → CSS variables/SCSS/TS → Signals/SignalStore → PrimeNG + data viz → Storybook as the single source of visual truth → Chromatic for PR-time diffs and a11y. It keeps UX polish honest while protecting performance budgets, AA contrast, and render counts.
From Figma Tokens to PrimeNG Components—with Guardrails
Why this matters now
As teams plan 2025 Angular roadmaps, the fastest way to align design and engineering is a token pipeline with measurable guardrails. I’ve used this approach on employee trackers, airline kiosks, and telecom analytics—where visual drift becomes real dollars.
Q1 is hiring season—token debt is expensive to unwind later.
Angular 21 beta is near; lock your palette/typography before upgrades.
Executives want measurable UX: tie tokens to Lighthouse, INP, and a11y.
What you’ll get
We’ll wire the AngularUX color palette, typography, and density controls to PrimeNG and your charts, then enforce it with Chromatic and accessibility checks.
One source of truth: Figma → Storybook.
Versioned tokens with visual diffs on PRs.
PrimeNG theme that respects density and AA contrast.
Why Design Tokens Matter for Angular 20+ Dashboards
Dashboards need consistency and speed
When I built advertising analytics for a telecom provider and telematics dashboards for insurance, token drift caused brand mismatches and slow, custom patches. Tokenizing PrimeNG and Highcharts kept releases predictable and render counts down by double digits.
Role-based theming without forks.
Data virtualization for 50k+ rows must keep styles cheap.
Visualization palettes must match component chrome.
UX polish vs performance budgets
Angular 20 Signals let us switch density or color mode without lighting up the whole tree. Pair that with Lighthouse and Angular DevTools flame charts to prove tokens cost near-zero at runtime.
Keep CSS <120 KB, avoid deep selectors.
Prefer CSS variables over runtime style recalcs.
Measure INP/LCP in Storybook/CI.
How an Angular Consultant Wires Figma Tokens to PrimeNG with Storybook and Chromatic
1) Export tokens from Figma
Export a normalized JSON—don’t let local color styles leak into product. Keep brand and semantic layers separate (e.g., brand.blue.500 vs semantic.surface-card).
Use Figma Tokens or Tokens Studio export.
Organize color, radius, spacing, typography, elevation, motion.
2) Compile with Style Dictionary
Style Dictionary keeps platform outputs consistent. I generate a TS module for typed access in Angular plus CSS variables for instant PrimeNG adoption.
Emit CSS variables, SCSS maps, and tokens.ts.
Prefix variables (e.g., --ux-).
3) Signals/SignalStore theme state
A small ThemeStore wraps tokens using Signals, so components and charts react without change detection storms.
Signals flip density/mode instantly.
Feature flags via Firebase Remote Config when needed.
4) PrimeNG theming
PrimeNG’s CSS variables make this easy—override its palette/radii/shadows with your token variables.
Map PrimeNG vars to CSS tokens.
Respect density tokens for table, input, menu spacing.
5) Storybook + Chromatic
Storybook becomes your living spec. Chromatic turns design intent into a CI gate—no more surprising tints in production.
Toolbar controls for theme/density.
Chromatic runs visual diffs and a11y on PRs.
Implementation: Code You Can Paste
// style-dictionary.config.cjs
module.exports = {
source: ['tokens/**/*.json'],
platforms: {
css: {
transformGroup: 'css',
buildPath: 'src/styles/tokens/',
files: [{ destination: 'tokens.css', format: 'css/variables', options: { outputReferences: true, selector: ':root' } }]
},
scss: {
transformGroup: 'scss',
buildPath: 'src/styles/tokens/',
files: [{ destination: '_tokens.scss', format: 'scss/map-deep' }]
},
ts: {
transformGroup: 'js',
buildPath: 'src/app/tokens/',
files: [{ destination: 'tokens.ts', format: 'javascript/es6' }]
}
}
}// src/app/theme/theme.store.ts (Angular 20+)
import { Injectable, signal, computed } from '@angular/core';
import * as Tokens from '../tokens/tokens'; // generated by Style Dictionary
export type Density = 'comfortable' | 'compact';
export type Mode = 'light' | 'dark';
@Injectable({ providedIn: 'root' })
export class ThemeStore {
private mode = signal<Mode>('light');
private density = signal<Density>('comfortable');
// expose selected token slices
tokens = computed(() => ({
colors: this.mode() === 'light' ? Tokens.light.color : Tokens.dark.color,
radius: Tokens.radius,
spacing: Tokens.spacing,
typography: Tokens.typography
}));
setMode(mode: Mode) { this.mode.set(mode); this.applyToDocument(); }
setDensity(d: Density) { this.density.set(d); this.applyToDocument(); }
private applyToDocument() {
const root = document.documentElement;
root.dataset['mode'] = this.mode();
root.dataset['density'] = this.density();
}
}/* src/styles/theme-primeng-overrides.scss */
@import './tokens/tokens.css';
:root[data-mode='light'] {
--p-primary-500: var(--ux-color-brand-500);
--p-surface-0: var(--ux-color-surface-0);
--p-text-color: var(--ux-color-text-900);
}
:root[data-mode='dark'] {
--p-primary-500: var(--ux-color-brand-300);
--p-surface-0: var(--ux-color-surface-900);
--p-text-color: var(--ux-color-text-50);
}
/* Density controls map to spacing tokens */
:root[data-density='comfortable'] { --ux-density-2: 0.75rem; }
:root[data-density='compact'] { --ux-density-2: 0.5rem; }
.p-button { border-radius: var(--ux-radius-md); }
.p-inputtext { padding: var(--ux-density-2); }
.p-datatable .p-datatable-thead > tr > th { padding: var(--ux-density-2); }// .storybook/preview.ts
import type { Preview } from '@storybook/angular';
import { withThemeByDataAttribute } from '@storybook/addon-themes';
export const globalTypes = {
mode: {
name: 'Mode',
toolbar: { icon: 'mirror', items: ['light', 'dark'] },
defaultValue: 'light'
},
density: {
name: 'Density',
toolbar: { icon: 'circlehollow', items: ['comfortable', 'compact'] },
defaultValue: 'comfortable'
}
};
export const decorators = [
(Story, ctx) => {
document.documentElement.dataset['mode'] = ctx.globals['mode'];
document.documentElement.dataset['density'] = ctx.globals['density'];
return Story();
},
withThemeByDataAttribute({ themes: { light: 'light', dark: 'dark' }, defaultTheme: 'light', attributeName: 'data-mode' })
];
const preview: Preview = {
parameters: {
chromatic: { pauseAnimationAtEnd: true, diffThreshold: 0.05 },
a11y: { element: '#root', manual: false }
}
};
export default preview;# .github/workflows/chromatic.yml
name: Chromatic
on: [pull_request]
jobs:
visual-regression:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npm ci
- run: npx nx run ui:build-storybook
- uses: chromaui/action@v1
with:
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
storybookBuildDir: dist/storybook/ui
exitOnceUploaded: true// src/app/shared/chart-palette.ts (Highcharts/D3 reuse)
const cssVar = (name: string) => getComputedStyle(document.documentElement).getPropertyValue(name).trim();
export const chartColors = () => [
cssVar('--ux-color-brand-500'),
cssVar('--ux-color-accent-500'),
cssVar('--ux-color-info-500'),
cssVar('--ux-color-success-500'),
cssVar('--ux-color-warning-500'),
cssVar('--ux-color-danger-500')
];
export const highchartsTheme = () => ({
colors: chartColors(),
chart: { backgroundColor: cssVar('--ux-color-surface-0') },
xAxis: { gridLineColor: cssVar('--ux-color-border-200') },
yAxis: { gridLineColor: cssVar('--ux-color-border-200') }
});<!-- Example: PrimeNG button + table using tokens -->
<p-button label="Save" styleClass="p-button-raised" icon="pi pi-check"></p-button>
<p-table [value]="rows" [virtualScroll]="true" [virtualScrollItemSize]="48">
<ng-template pTemplate="header">
<tr><th>Name</th><th>Status</th></tr>
</ng-template>
<ng-template pTemplate="body" let-row>
<tr><td>{{row.name}}</td><td>{{row.status}}</td></tr>
</ng-template>
</p-table>Style Dictionary config
Signals-based ThemeStore
PrimeNG theme overrides
Storybook preview with Chromatic controls
Chart palette from tokens
Accessibility, Typography, and Density—Without Performance Regressions
Contrast and motion tokens
I’ve had airport kiosks fail field tests because motion overwhelmed users. Define motion.duration.short/long and respect prefers-reduced-motion with token-backed conditions in animations.
AA contrast across modes verified via Storybook a11y.
Reduce-motion tokens disable heavy animations.
Typography scale that reads, not shouts
AngularUX uses a readable scale for data-dense tables and long forms. Avoid custom per-component font sizes; source from tokens to keep rhythm and INP stable.
Use modular scales and clamp().
Expose tokens for line-height and letter-spacing.
Density controls tied to role
Signals adjust data-density; PrimeNG tables and inputs obey spacing variables. In practice, I see 0 extra change detection cost and happier power users.
Ops users want compact; execs prefer comfortable.
Tokens swap padding/gaps without full re-render.
Real Dashboards: D3/Highcharts/Canvas/Three.js on the Same Palette
Consistency across viz stacks
On a device management portal (IoT), we rendered Canvas heatmaps and D3 timelines. Mapping both to the same CSS variables eliminated the ‘two brands in one app’ problem and made dark mode a one-line switch.
Reuse CSS variables in D3/Canvas.
Three.js overlays use semantic colors.
Data virtualization harmony
For 50k+ rows, tokenized spacing keeps header heights stable, avoiding scroll jumps. Axe checks ensure focus indicators survive virtualization.
Virtual scroll tables match chart gridlines and labels.
Keep focus outlines visible under load.
When to Hire an Angular Developer for Token Systems and Legacy Rescue
Bring in help when
I step into chaotic codebases to centralize theming, migrate to Signals, and add Storybook/Chromatic guardrails. On gitPlumbers we’ve lifted delivery velocity by 70% while cutting visual regressions to near-zero. If you need to stabilize your Angular codebase, this is the playbook.
Theme PRs are breaking production visuals.
PrimeNG overrides are copy/pasted per feature.
Dark mode or density changes cause performance spikes.
Engagement shape
Remote, Nx-friendly. CI integration with Firebase previews if you want reviewers to kick the tires before merging.
2–3 weeks: token pipeline + Storybook.
4–6 weeks: full PrimeNG refactor + a11y + chart themes.
Takeaways and Next Steps
What to instrument next
Polish and engineering discipline are not opposites. Tie your palette, type, and density to measurable outcomes: fewer diffs, faster merges, and accessible dashboards.
Lighthouse budgets for CSS/JS.
INP in GA4 via Firebase Analytics.
Chromatic thresholds per story (charts vs forms).
Talk to an Angular expert
If you want to hire an Angular developer or bring in an Angular consultant to wire tokens to PrimeNG the right way, I’m available for select projects.
Discovery call within 48 hours.
Assessment in 5 business days.
Key takeaways
- Export tokens from Figma and compile with Style Dictionary into CSS variables, SCSS maps, and typed TS for Angular 20+.
- Expose tokens via Signals/SignalStore so density, color mode, and typography scale update PrimeNG and charts in real time.
- Use Storybook as the source of truth; Chromatic runs visual diffs and a11y checks on every PR to prevent regressions.
- Tokenize PrimeNG variables (colors, radii, shadows) and chart palettes so dashboards and data viz stay in sync.
- Guard performance: keep CSS under budget, avoid selector bloat, and measure with Lighthouse/INP; visual quality without regressions.
- Document and test accessibility (contrast, focus, motion) inside Storybook; ship AA by default.
Implementation checklist
- Export Figma tokens (colors, radii, spacing, typography, elevations, motion) as JSON.
- Build Style Dictionary pipeline to emit CSS variables, SCSS maps, and a typed tokens.ts for Angular.
- Create a Signals-based ThemeStore for mode, density, and brand; wire into PrimeNG and chart configs.
- Add Storybook with toolbar controls for theme/density; publish to Chromatic for visual diffs and a11y checks.
- Tokenize D3/Highcharts colors and gridlines; snapshot in Storybook to lock charts to your palette.
- Set budgets in CI (CSS size, INP/LCP); block merges on Chromatic diffs or contrast failures.
- Document keyboard, focus, and motion tokens; ensure WCAG AA with automated and manual checks.
Questions we hear from teams
- How long does it take to wire Figma tokens to PrimeNG with Storybook and Chromatic?
- For most teams: 2–3 weeks to stand up Style Dictionary, Signals-based theme state, Storybook controls, and Chromatic diffs. Add 1–2 weeks for PrimeNG refactors, AA fixes, and chart themes across D3/Highcharts.
- What does an Angular consultant do on a token engagement?
- I export/normalize Figma tokens, build the Style Dictionary pipeline, create a Signals/SignalStore theme store, map tokens to PrimeNG and charts, and add Storybook + Chromatic gates with a11y checks and budgets.
- Will tokenizing PrimeNG hurt performance?
- No—CSS variables are cheap. With Angular 20 Signals to switch mode/density and careful scoping, I’ve shipped token systems that keep CSS under budget and maintain INP/LCP targets with zero reflow surprises.
- Can this work in an Nx monorepo with Firebase previews?
- Yes. I run Storybook as an Nx target, publish Chromatic on PRs, and deploy Firebase Hosting previews so designers and PMs can review token changes in realistic pages before merge.
- How much does it cost to hire an Angular developer for this?
- Typical token pipeline engagements start at 2–3 weeks. Fixed-fee or time-and-materials are available. Book a discovery call to scope complexity—PrimeNG override depth and data viz needs drive effort most.
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