Esc
  • Design SystemsFrom Figma Variables to Tailwind, Without the Copy-PasteDesign tokens only help if design and code read from the same source. Here's how I set up a pipeline where Figma variables become Tailwind theme values and React props, and what I learned about naming along the way.
  • Next.jsBuilding a Like Button, From Database to DelightThe heart at the bottom of every article on this site is a tiny full-stack feature. Here's how it works, from the SQLite table and API route to optimistic updates and batched requests.
  • AnimationA Friendly Introduction to Spring PhysicsSprings make interfaces feel alive. Instead of picking a duration and an easing curve, you describe how something should move, and physics does the rest. Let's build an intuition for stiffness and damping with a live playground.
  • UI DesignDesigning Dashboards People Can Actually ScanData-heavy screens fail when every number shouts at once. A practical guide to hierarchy, tables, filters and progressive disclosure for dashboards that engineers and analysts use all day.
  • CSSUnderstanding Flexbox Alignment, Visuallyjustify-content or align-items? Main axis or cross axis? If you still guess which property to use, this interactive guide will fix that for good.
  • BrandA Brand System Is More Than a LogoA logo is the smallest part of a brand. What makes a brand recognizable is the system around it, from type and color to iconography, voice and the way the product itself behaves. Here's how I approach building one that survives contact with real products.
Design Systems

From Figma Variables to Tailwind, Without the Copy-Paste

Design tokens only help if design and code read from the same source. Here's how I set up a pipeline where Figma variables become Tailwind theme values and React props, and what I learned about naming along the way.

3 min read…

Every design system I've worked on has hit the same wall. The Figma library says the primary button is #4F46E5. The codebase says #4f46e6. Nobody remembers who changed what, and a designer spends their Friday filing "colour is slightly off" tickets.

The fix isn't more discipline. It's making sure there is one source of truth, and that everything else is generated from it.

Three layers of tokens

Before any tooling, get the structure right. I split tokens into three layers:

  1. Primitives are the raw palette: blue-600, space-4, radius-lg. They describe what a value is, never where it's used.
  2. Semantic tokens describe intent: color-action-primary, color-text-muted, space-stack-md. They point at primitives.
  3. Component tokens are optional and narrow: button-primary-bg. Use them only when a component genuinely needs to differ from the semantic default.

Components should only ever use the semantic layer. That's what makes rebranding (or adding a second brand) a matter of re-pointing tokens instead of rewriting components.

Exporting from Figma

Figma variables can be read through the REST API or exported with a plugin. Either way, aim for a plain JSON file that lives in Git:

JSON
{
  "color": {
    "action": {
      "primary": { "$value": "{blue.600}", "$type": "color" }
    },
    "text": {
      "muted": { "$value": "{gray.500}", "$type": "color" }
    }
  }
}

The $value / $type shape follows the W3C Design Tokens format, which most tools now understand. Committing the file means every token change shows up in a pull request, where engineers and designers can review it together.

Turning tokens into CSS and Tailwind

A small build step (Style Dictionary works well) turns the JSON into CSS custom properties:

CSS
:root {
  --color-action-primary: #4f46e5;
  --color-text-muted: #6b7280;
}

[data-brand="acme"] {
  --color-action-primary: #0f766e;
}

Then Tailwind reads the custom properties instead of hard-coded hex values:

JS
// tailwind.config.js
export default {
  theme: {
    extend: {
      colors: {
        action: { primary: "var(--color-action-primary)" },
        text: { muted: "var(--color-text-muted)" },
      },
    },
  },
};

Now bg-action-primary in a React component follows whatever Figma says, for every brand, without anyone copying a value by hand.

Naming is the hard part

The tooling took an afternoon. The naming took weeks. A few rules that saved me:

  • Name by role, not by look. color-danger survives a palette change; color-red doesn't.
  • Match the names in Figma and code exactly. If a designer says "text muted" in a review, an engineer should be able to search for text-muted and find it.
  • Keep the semantic layer small. If you have forty "secondary" colors, nobody will use them consistently.

Want to know when I publish something new?

I send a short email when there's a new article or project, usually a couple of times a month. No spam, and you can unsubscribe whenever you like.