Design Tokens to Code with Tailwind CSS & shadcn/ui
Bridging Figma design variables into production code with Tailwind design tokens, Radix UI headless primitives, and responsive layout patterns.
Overview
A design system is only as useful as its fidelity to the production codebase. This guide shows how to export Figma variables as CSS custom properties, wire them into Tailwind, and compose accessible components with shadcn/ui and Radix primitives.
Figma Variables → CSS Custom Properties
In Figma, define Variable Collections (Color, Spacing, Radius). Export via the Figma REST API or a plugin like Token Studio.
// tokens.json (Token Studio output)
{
"color": {
"brand": { "500": { "value": "#DE4520", "type": "color" } },
"neutral": { "0": { "value": "#ffffff", "type": "color" } }
},
"spacing": {
"4": { "value": "16px", "type": "spacing" }
}
}
Transform to CSS with Style Dictionary:
// sd.config.js
module.exports = {
source: ["tokens.json"],
platforms: {
css: {
transformGroup: "css",
prefix: "ds",
files: [{ destination: "tokens.css", format: "css/variables" }],
},
},
};
Output:
:root {
--ds-color-brand-500: #DE4520;
--ds-color-neutral-0: #ffffff;
--ds-spacing-4: 16px;
}
Wiring into Tailwind v4
With Tailwind CSS v4, reference CSS variables directly in the theme:
/* globals.css */
@import "tailwindcss";
@theme {
--color-brand: var(--ds-color-brand-500);
--spacing-4: var(--ds-spacing-4);
}
Now bg-brand, text-brand, p-4 all use your tokens.
shadcn/ui Component Anatomy
shadcn/ui gives you copy-pasteable components built on Radix primitives. Every component maps Radix's accessibility behavior to your design tokens:
// components/ui/button.tsx (simplified)
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors",
{
variants: {
variant: {
default: "bg-brand text-white hover:bg-brand/90",
outline: "border border-brand text-brand hover:bg-brand/10",
ghost: "hover:bg-brand/10",
},
size: {
sm: "h-8 px-3",
md: "h-10 px-4",
lg: "h-12 px-6",
},
},
defaultVariants: { variant: "default", size: "md" },
}
);
export function Button({ variant, size, asChild, ...props }: ButtonProps) {
const Comp = asChild ? Slot : "button";
return <Comp className={buttonVariants({ variant, size })} {...props} />;
}
Dark Mode with CSS Variables
Single-variable swap for light/dark:
:root {
--bg: #ffffff;
--fg: #0b0b0b;
--surface: #f4f3f1;
}
.dark {
--bg: #0b0b0b;
--fg: #f4f3f1;
--surface: #141414;
}
// All components use semantic tokens — dark mode is free
<div className="bg-[var(--bg)] text-[var(--fg)]">
<Card className="bg-[var(--surface)]" />
</div>
Responsive Fluid Typography
Use clamp() for fluid type scales that need no breakpoints:
:root {
--text-display: clamp(2.5rem, 5vw + 1rem, 6rem);
--text-body: clamp(0.875rem, 1vw + 0.5rem, 1.125rem);
}
Key Takeaways
- Single source of truth: Figma Variables → Style Dictionary → CSS custom properties → Tailwind theme
- Radix UI handles all ARIA + keyboard behavior; you only write styles
- Semantic tokens (
--bg,--fg) enable zero-overhead dark mode - Use
cva(class-variance-authority) for type-safe variant management