This episode covers design tokens: storing them in tailwind.config.js, exposing them as CSS variables, and syncing them from Figma. You also learn to build a consistent component library that can be published as an npm package.

As projects grow, the need for a single source of truth for design becomes critical. In episode 10 we cover design tokens: storing them in tailwind.config.js, exposing them as CSS variables, syncing from Figma, and building a consistent component library.
A good design system means teams stop arguing about #3b82f6 versus #2563eb. All values live in one place, managed jointly between design and engineering, and are accessed through consistent names everywhere.
Define tokens as CSS variables in the root stylesheet:
:root {
--color-brand: #3b82f6;
--color-surface: #ffffff;
--space-card: 1rem;
--radius-card: 0.5rem;
}Then wire them into the config:
theme: {
extend: {
colors: {
brand: "var(--color-brand)",
surface: "var(--color-surface)",
},
spacing: {
card: "var(--space-card)",
},
borderRadius: {
card: "var(--radius-card)",
},
},
},This combination gives two advantages: utilities like bg-brand and p-card are created, and the underlying values can still be changed at runtime through CSS. This colors: { brand: "var(--color-brand)" } pattern is the bridge between static config and dynamic theming.
Figma and Tailwind don't talk to each other automatically. A common workflow:
A simple JSON token example:
{
"color": {
"brand": "#3b82f6",
"surface": "#ffffff"
},
"spacing": {
"card": "1rem"
}
}Tools like Style Dictionary can turn JSON tokens into various outputs — including :root CSS variables or Tailwind config snippets. This flow makes Figma the source of truth, with the Tailwind config as its derivative.
Info
The key to a successful sync is a one-way flow: Figma to code, not back and forth. If the design changes, update the tokens in Figma, export, then commit. Don't change token values directly in the repo without updating Figma, or the two will drift apart.
A component library starts from small, consistent decisions. First, standardize patterns with @apply in the components layer:
@layer components {
.btn {
@apply inline-flex items-center justify-center rounded-card bg-brand px-4 py-2 font-medium text-white;
}
.card {
@apply rounded-card border border-gray-200 bg-surface p-card shadow-sm;
}
}Then wrap them with a component API in your framework — for example a Button component in React or Svelte — that provides variants and a class slot. To publish as a package, follow these steps:
npm init -y
npx tsc --initA common package structure: a src folder with components, a styles folder with CSS that includes @tailwind and @layer components, and a tailwind-preset.js that exports the tokens. Consumers just install it and register the preset in their config:
module.exports = {
content: ["./node_modules/@org/ui/**/*.{js,jsx}"],
presets: [require("@org/ui/tailwind-preset")],
};This presets: [require("@org/ui/tailwind-preset")] pattern is what spreads one design language across many applications.
Episode 10 took you to the design system level: storing tokens in the config, exposing them as CSS variables, syncing from Figma, and building a component library that can be published and used across projects.
Key takeaways:
var(...) values.@apply and @layer components.Next, in episode 11, we'll cover handling dynamic content & security — the risks of dynamic classes from user input, validation and safelist patterns, avoiding injection via class names, and content scanning strategies for multi-tenant environments.