Tailwind CSS v4 CSS-First Configuration Using @theme
In this tutorial, you will learn about Tailwind CSS v4 CSS. We cover key concepts, practical examples, and best practices to help you master this topic.
Tailwind CSS v4 replaces the JavaScript tailwind.config.js with CSS-first configuration using the @theme directive, where design tokens are defined as native CSS custom properties.
What You'll Learn
You will learn how to define colors, fonts, spacing, breakpoints, and other design tokens using @theme in your CSS file, and understand the advantages of CSS-first over JavaScript config.
Why It Matters
CSS-first configuration keeps design tokens in the same file as your styles, reducing context switching. DodaTech's v4 projects define all tokens in a single tokens.css file imported globally.
Real-World Use
Doda Browser's v4 upgrade moved all design tokens from a 120-line tailwind.config.js to a 35-line @theme block in tokens.css, making token management visible alongside component styles.
flowchart LR
A[Migration] --> B[CSS-First Config]
B --> C[@theme Directive]
B --> D[Design Tokens]
B --> E[CSS Variables]
B --> F[No JS Config]
style B fill:#38bdf8,stroke:#0284c7,color:#fff
style C fill:#22c55e,stroke:#16a34a,color:#fff
Basic @theme Syntax
@import "tailwindcss";
@theme {
--color-primary: #3b82f6;
--color-primary-dark: #2563eb;
--color-primary-light: #93c5fd;
--font-family-sans: "Inter", sans-serif;
--font-family-mono: "JetBrains Mono", monospace;
--spacing-section: 5rem;
--spacing-card: 1.5rem;
}
Expected output: Every CSS variable defined in @theme becomes a Tailwind utility. --color-primary generates bg-primary, text-primary, border-primary, etc.
Color Palettes with Shades
@theme {
/* Full shade scale */
--color-brand-50: #f5f3ff;
--color-brand-100: #ede9fe;
--color-brand-200: #ddd6fe;
--color-brand-300: #c4b5fd;
--color-brand-400: #a78bfa;
--color-brand-500: #7c3aed;
--color-brand-600: #6d28d9;
--color-brand-700: #5b21b6;
--color-brand-800: #4c1d95;
--color-brand-900: #3b0764;
/* Single semantic colors */
--color-success: #22c55e;
--color-warning: #f59e0b;
--color-error: #ef4444;
--color-info: #3b82f6;
}
<div class="bg-brand-100 text-brand-700 border-brand-300 px-4 py-2 rounded">
Brand palette with shade scale
</div>
<div class="bg-success text-white px-4 py-2 rounded">
Semantic success color
</div>
Expected output: The brand-* palette generates bg-brand-50 through bg-brand-900. Single semantic colors generate bg-success, text-success, etc.
Typography Configuration
@theme {
/* Font families */
--font-family-sans: "Inter", "system-ui", sans-serif;
--font-family-serif: "Merriweather", "Georgia", serif;
--font-family-mono: "JetBrains Mono", "Fira Code", monospace;
--font-family-display: "Playfair Display", serif;
/* Font sizes (override defaults) */
--font-size-xxs: 0.625rem;
--font-size-10xl: 10rem;
/* Font weights (optional - Tailwind defaults work) */
--font-weight-semibold: 600;
}
Expected output: Custom font families become font-sans, font-serif, font-mono, font-display utilities. Custom font sizes add text-xxs and text-10xl to the scale.
Spacing and Sizing Tokens
@theme {
/* Custom spacing values */
--spacing-18: 4.5rem;
--spacing-88: 22rem;
--spacing-128: 32rem;
/* Named spacing for specific components */
--spacing-nav: 4rem;
--spacing-sidebar: 16rem;
/* Custom widths and heights */
--width-sidebar: 16rem;
--height-nav: 4rem;
/* Custom max-widths */
--max-width-article: 65ch;
--max-width-page: 80rem;
}
<div class="w-sidebar h-nav bg-gray-100">Fixed sidebar</div>
<div class="max-w-article mx-auto p-18">Article content</div>
Expected output: Custom spacing values generate p-18, m-88, gap-18. Named sizes generate w-sidebar, h-nav, max-w-article utilities.
Responsive Breakpoints
@theme {
/* Override default breakpoints */
--breakpoint-sm: 36rem;
--breakpoint-md: 48rem;
--breakpoint-lg: 64rem;
--breakpoint-xl: 80rem;
--breakpoint-2xl: 96rem;
/* Add custom breakpoints */
--breakpoint-xs: 30rem;
--breakpoint-3xl: 120rem;
--breakpoint-sidebar: 60rem;
}
Expected output: Modified breakpoints change where responsive prefixes (sm:, md:, lg:) activate. Custom breakpoints become new prefixes: xs:, 3xl:, sidebar:.
Combining with CSS Variables
/* tokens.css */
@theme {
--color-primary: rgb(var(--color-primary-rgb) / <alpha-value>);
}
/* theme.css */
:root {
--color-primary-rgb: 59 130 246;
}
.dark {
--color-primary-rgb: 147 197 253;
}
<div class="bg-primary/50 text-primary">Primary with opacity support</div>
Expected output: CSS variable-based colors with <alpha-value> support opacity modifiers like bg-primary/50. Dark mode changes the variable value.
Common Mistakes
1. Using JavaScript Variable Syntax
--color-primary: #3b82f6; works. --color-primary: { 500: '#3b82f6' } does not. v4 uses flat CSS variable names.
2. Forgetting --color- Prefix
Custom properties must follow naming conventions: --color-* for colors, --font-family-* for fonts, --spacing-* for spacing. Mismatched prefixes do not generate utilities.
3. Not Reloading After @theme Changes
CSS file changes via @theme require a fresh build. Unlike tailwind.config.js, @theme changes do not trigger HMR in all setups.
4. Using Spaces Instead of Quotes in Font Names
Font names with spaces must be quoted: "Inter" not Inter. Without quotes, the font name breaks at the space.
5. Overriding All Defaults Accidentally
Defining --color-blue-500 overrides the default blue-500. Use unique names like --color-brand-500 to extend without overriding.
Practice Questions
What directive replaces tailwind.config.js?
@themein your main CSS file.How do you add a custom color in v4?
--color-name: value;inside @theme. Generates bg-name, text-name, border-name.What prefix do spacing variables use?
--spacing-*. Custom values like--spacing-18generate p-18, m-18, gap-18.How do you add custom breakpoints?
--breakpoint-name: value;in @theme. Generates the name: prefix for responsive utilities.Can you use @theme alongside tailwind.config.js? Use one or the other. v4 prioritizes @theme. If both exist, @theme values take precedence.
Challenge
Create a full design token set using @theme: 5 brand colors with 50-900 shades, 3 font families, 5 custom spacing values, 2 custom breakpoints, and named tokens for commonly used dimensions.
FAQ
Mini Project
Create a tokens.css file with @theme for a brand: 4 colors with full shades, 2 font families, custom spacing for a dashboard layout, custom breakpoints for sidebar and wide-screen, and a custom animation duration.
What's Next
Now master the Theme Function for referencing design tokens in custom CSS. Then explore Variants in v4 for the new variant system.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro