Tailwind v3 + v4
Architecture behind Tailwind CSS v3 / v4 dual support.
Components use a single source codebase, with theme files absorbing version differences.
For setup instructions, see the Installation page.
Architecture
figma-tokens.json → variables.css is the single source of truth. v3-preset.js and v4-theme.css are mapping layers that reference variables.css.
How v3 works
- JS preset (
theme.extend) - Reference CSS variables via
var() - Define custom utilities via plugins
- Semantic color
bg-primary/50support (RGB channel variables)
How v4 works
- CSS
@theme+@utility - var() reference + HEX fallback (same-name variables overwritten by cascade)
color-mix()for runtime conversionbg-primary/50 support
Sync Logic
2 patterns in which v3/v4 theme files are generated when running sync-tokens from Figma tokens.
var() Reference (Different Variable Names)
When theme variable names differ from CSS variable names, var() is used for direct reference.
--color-primary-text--color-primary-foregroundtext-primary-foregroundCSS Cascade (Same-name Variables)
Same-name variables cannot self-reference via var(). sync-tokens generates HEX fallback values in v4-theme.css @theme, and variables.css (unlayered) always takes priority over @layer theme in the cascade. Utilities use var() references, so dark mode variable switching is automatically reflected.
var(--color-primary-text)#15A0AC → unlayered override@utility { ... var(...) }These sync operations are auto-generated by the sync-tokens CLI. There is no need to manually edit v3-preset.js / v4-theme.css.
Naming Differences
v3 and v4 differ in how theme variables are defined. The utility classes used by components are identical.
text-mdfontSize: { md: ['var(--font-size-md)', ...] }--text-md: var(--font-size-md)bg-primarycolors: { primary: 'var(--color-primary)' }--color-primary: #15A0AC (cascade)rounded-mdborderRadius: { md: 'var(--radius-md)' }--radius-md: 6px (cascade)text-foregroundcolors: { foreground: 'var(--color-text)' }--color-foreground: var(--color-text)shadow-smboxShadow: { sm: 'var(--shadow-sm)' }--shadow-sm: 0 1px 3px ... (cascade)border-bordercolors: { border: 'var(--color-border)' }--color-border: #E4E4E7 (cascade)icon-xs ~ xladdUtilities plugin@utility { width/height: var(...) }Items not in the v4 @theme namespace (duration, scale, z-index, icon-size, etc.) are defined individually via @utility. In v3, the same functionality is achieved using plugins's addUtilities().
In v4, *-foreground aliases are also available (e.g., text-muted-foreground = text-text-muted). Compatible with ecosystems like shadcn/ui.
Custom Utilities
Items not in the standard Tailwind theme are defined as custom utilities, each with its own approach for v3 and v4.
Duration
duration-micro, duration-fast, duration-normalTransition duration
Scale
scale-pressedScale animation on button press
Icon Sizes
icon-xs ~ icon-xl5-step icon sizes
Focus Ring
focus-ringFocus ring utility
Z-index
z-modal, z-tooltip, z-toastNamed Z-index layers
Animation
animate-checkbox-enter, animate-accordion-downComponent animations
Dark Mode
Dark mode is fully managed by the .dark block in themes/dark.css. No dark mode definitions are needed in the v3/v4 theme files.
:root.darkv3
Set darkMode: ['class'] in the user's tailwind.config.js.Since v3-preset.js references via var(),switching variables with the .dark class is automatically reflected.
v4
variables.css (unlayered) always overrides @theme (theme layer) via cascade. Utilities use var(), so .dark variable switching is automatically reflected.No .dark block is needed in v4-theme.css.
Opacity Modifier
Both v3 and v4 support opacity modifiers like bg-primary/50. v3 uses RGB channel variables, v4 uses color-mix().
v3
Full support via RGB channel variablesbg-primarybg-primary/50text-foreground/80border-border/50v4
Full support via color-mix()bg-primarybg-primary/50text-foreground/80border-border/50In v3, opacity modifiers like bg-primary/50 are also supported.RGB channel variables (--color-*-rgb) are auto-generated and <alpha-value> is applied.