While Tailwind's defaults are excellent, every project has unique design requirements. Tailwind v4 introduces a powerful CSS-first configuration approach using the @theme directive. In this lesson, you'll learn to customize Tailwind with brand colors, custom utilities, CSS variables, and arbitrary values to create your own design system.
Tailwind v4's CSS-First Approach
Tailwind v4 uses CSS variables and the @theme directive for customization instead of JavaScript configuration:
Key Changes in v4
- No tailwind.config.js needed: Configuration happens in CSS
- @theme directive: Define custom theme values in your CSS
- CSS variables: Native CSS custom properties for theming
- Better performance: CSS-based configuration is faster
- More intuitive: Design tokens live with your styles
Basic Setup
@import "tailwindcss";
/* Customize your theme using @theme */
@theme {
/* Add custom values here */
}Adding Custom Colors
Define brand colors and custom color palettes:
Single Brand Color
@import "tailwindcss";
@theme {
/* Add a single brand color */
--color-brand: #3b82f6;
}
/* Now you can use: */
/* bg-brand, text-brand, border-brand, etc. */<!-- Using the custom brand color -->
<div class="bg-brand text-white p-6 rounded-lg">
Brand color background
</div>
<button class="bg-brand hover:bg-brand/90 text-white px-6 py-3 rounded-lg">
Brand Button
</button>
<h1 class="text-brand text-4xl font-bold">
Brand Colored Heading
</h1>Full Color Scale
@import "tailwindcss";
@theme {
/* Create a complete brand color scale */
--color-brand-50: #eff6ff;
--color-brand-100: #dbeafe;
--color-brand-200: #bfdbfe;
--color-brand-300: #93c5fd;
--color-brand-400: #60a5fa;
--color-brand-500: #3b82f6;
--color-brand-600: #2563eb;
--color-brand-700: #1d4ed8;
--color-brand-800: #1e40af;
--color-brand-900: #1e3a8a;
--color-brand-950: #172554;
}
/* Now you can use: */
/* bg-brand-500, text-brand-700, border-brand-300, etc. */<!-- Using the full brand color scale -->
<div class="bg-brand-50 border-2 border-brand-200 p-6 rounded-lg">
<h3 class="text-brand-900 text-xl font-bold mb-2">
Light Brand Background
</h3>
<p class="text-brand-700">
Using different shades from our brand palette
</p>
</div>
<!-- Gradient with brand colors -->
<div class="bg-gradient-to-r from-brand-400 via-brand-500 to-brand-600 text-white p-8 rounded-lg">
<h2 class="text-3xl font-bold">Brand Gradient</h2>
</div>
<!-- Dark mode support -->
<div class="bg-brand-500 dark:bg-brand-700 text-white p-6">
Adapts to dark mode
</div>Multiple Custom Colors
@import "tailwindcss";
@theme {
/* Primary brand color */
--color-primary-500: #3b82f6;
--color-primary-600: #2563eb;
--color-primary-700: #1d4ed8;
/* Secondary accent color */
--color-secondary-500: #8b5cf6;
--color-secondary-600: #7c3aed;
--color-secondary-700: #6d28d9;
/* Success, warning, danger */
--color-success-500: #10b981;
--color-warning-500: #f59e0b;
--color-danger-500: #ef4444;
}<!-- Using multiple custom colors -->
<button class="bg-primary-500 hover:bg-primary-600 text-white px-6 py-3 rounded-lg">
Primary Action
</button>
<button class="bg-secondary-500 hover:bg-secondary-600 text-white px-6 py-3 rounded-lg">
Secondary Action
</button>
<!-- Status indicators -->
<span class="inline-flex items-center gap-2 bg-success-500/10 text-success-500 px-3 py-1 rounded-full">
✓ Success
</span>
<span class="inline-flex items-center gap-2 bg-warning-500/10 text-warning-500 px-3 py-1 rounded-full">
⚠ Warning
</span>
<span class="inline-flex items-center gap-2 bg-danger-500/10 text-danger-500 px-3 py-1 rounded-full">
✕ Error
</span>🎨 Color Naming Convention
Use semantic names for better maintainability:
--color-primaryfor main brand color--color-secondaryfor accent colors--color-success/warning/dangerfor status--color-neutralfor grays
Custom Spacing Scale
Extend or modify the spacing scale for your design system:
@import "tailwindcss";
@theme {
/* Add custom spacing values */
--spacing-18: 4.5rem; /* 72px */
--spacing-72: 18rem; /* 288px */
--spacing-84: 21rem; /* 336px */
--spacing-96: 24rem; /* 384px */
/* Or use your design system's spacing */
--spacing-xs: 0.25rem; /* 4px */
--spacing-sm: 0.5rem; /* 8px */
--spacing-md: 1rem; /* 16px */
--spacing-lg: 1.5rem; /* 24px */
--spacing-xl: 2rem; /* 32px */
--spacing-2xl: 3rem; /* 48px */
}<!-- Using custom spacing -->
<div class="p-18">72px padding</div>
<div class="w-96 h-96">24rem square</div>
<div class="gap-xl">2rem gap</div>
<!-- Responsive with custom spacing -->
<div class="p-md lg:p-2xl">
Responsive padding using custom scale
</div>Custom Typography
Add custom fonts and typography settings:
Custom Font Families
@import "tailwindcss";
/* Import Google Fonts */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Playfair+Display:wght@700;900&display=swap');
@theme {
/* Define custom font families */
--font-sans: 'Inter', ui-sans-serif, system-ui, sans-serif;
--font-serif: 'Playfair Display', ui-serif, Georgia, serif;
--font-display: 'Playfair Display', Georgia, serif;
}<!-- Using custom fonts -->
<body class="font-sans">
<h1 class="font-display text-6xl font-bold">
Elegant Display Font
</h1>
<h2 class="font-serif text-4xl">
Serif Headings
</h2>
<p class="font-sans text-base">
Body text in Inter sans-serif
</p>
</body>Custom Font Sizes
@import "tailwindcss";
@theme {
/* Custom font sizes with line heights */
--font-size-xs: 0.75rem; /* 12px */
--font-size-sm: 0.875rem; /* 14px */
--font-size-base: 1rem; /* 16px */
--font-size-lg: 1.125rem; /* 18px */
--font-size-xl: 1.25rem; /* 20px */
--font-size-2xl: 1.5rem; /* 24px */
--font-size-3xl: 1.875rem; /* 30px */
--font-size-4xl: 2.25rem; /* 36px */
--font-size-5xl: 3rem; /* 48px */
--font-size-6xl: 3.75rem; /* 60px */
--font-size-hero: 5rem; /* 80px */
}Custom Breakpoints
Define custom responsive breakpoints:
@import "tailwindcss";
@theme {
/* Custom breakpoints */
--breakpoint-xs: 480px;
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
--breakpoint-2xl: 1536px;
--breakpoint-3xl: 1920px;
}<!-- Using custom breakpoints -->
<div class="text-base md:text-lg 3xl:text-xl">
Scales up to 3xl breakpoint (1920px)
</div>
<div class="grid grid-cols-1 xs:grid-cols-2 md:grid-cols-3 xl:grid-cols-4">
<!-- Responsive grid with xs breakpoint -->
</div>Arbitrary Values
Use one-off custom values without extending the theme:
Basic Arbitrary Values
<!-- Width and height -->
<div class="w-[350px] h-[200px]">
Exactly 350px by 200px
</div>
<!-- Colors -->
<div class="bg-[#1da1f2] text-[#ffffff]">
Twitter blue background
</div>
<!-- Spacing -->
<div class="p-[17px] m-[42px]">
Custom padding and margin
</div>
<!-- Font size -->
<h1 class="text-[2.5rem] leading-[3rem]">
Custom font size and line height
</h1>
<!-- Grid columns -->
<div class="grid grid-cols-[200px_1fr_200px] gap-4">
<!-- 200px, flex, 200px layout -->
</div>Complex Arbitrary Values
<!-- Gradients -->
<div class="bg-[linear-gradient(45deg,#667eea_0%,#764ba2_100%)]">
Custom gradient
</div>
<!-- Box shadows -->
<div class="shadow-[0_35px_60px_-15px_rgba(0,0,0,0.3)]">
Custom shadow
</div>
<!-- Calc() values -->
<div class="w-[calc(100%-2rem)] h-[calc(100vh-64px)]">
Using calc() for dynamic sizing
</div>
<!-- CSS Grid template -->
<div class="grid grid-cols-[repeat(auto-fit,minmax(250px,1fr))] gap-6">
Auto-responsive grid
</div>
<!-- Transform -->
<div class="rotate-[17deg] scale-[1.05]">
Custom rotation and scale
</div>Arbitrary Properties
<!-- Any CSS property with square brackets -->
<div class="[mask-image:linear-gradient(to_bottom,black,transparent)]">
Custom CSS property
</div>
<div class="[backdrop-filter:saturate(180%)_blur(20px)]">
Complex backdrop filter
</div>
<div class="[text-shadow:2px_2px_4px_rgba(0,0,0,0.5)]">
Text with custom shadow
</div>
<!-- CSS variables -->
<div class="[--scroll-offset:100px] top-[var(--scroll-offset)]">
Using CSS variables
</div>When to Use Arbitrary Values
- ✅ One-off values: Unique to a single component
- ✅ Prototyping: Quick experimentation
- ✅ External constraints: Matching exact brand values
- ❌ Repeated values: Add to theme instead
- ❌ System values: Should be in design system
CSS Variables for Dynamic Theming
Use CSS variables for runtime theme switching:
@import "tailwindcss";
/* Define CSS variables for light/dark themes */
:root {
--color-surface: #ffffff;
--color-on-surface: #1f2937;
--color-primary: #3b82f6;
}
.dark {
--color-surface: #1f2937;
--color-on-surface: #f9fafb;
--color-primary: #60a5fa;
}
@theme {
/* Map to Tailwind utilities */
--color-surface: var(--color-surface);
--color-on-surface: var(--color-on-surface);
--color-primary: var(--color-primary);
}<!-- Components automatically adapt to theme -->
<div class="bg-surface text-on-surface p-6 rounded-lg">
<h3 class="text-primary text-xl font-bold mb-2">
Dynamic Theme
</h3>
<p>This adapts when you toggle dark mode</p>
</div>
<!-- Button with theme-aware colors -->
<button class="bg-primary hover:bg-primary/90 text-white px-6 py-3 rounded-lg">
Primary Button
</button>Creating Custom Utilities
Add your own utility classes:
@import "tailwindcss";
/* Custom utility: text gradient */
@layer utilities {
.text-gradient {
@apply bg-clip-text text-transparent bg-gradient-to-r from-purple-400 to-pink-600;
}
.text-gradient-blue {
@apply bg-clip-text text-transparent bg-gradient-to-r from-blue-400 to-cyan-600;
}
}
/* Custom utility: glassmorphism */
@layer utilities {
.glass {
@apply backdrop-blur-md bg-white/30 border border-white/20;
}
.glass-dark {
@apply backdrop-blur-md bg-black/30 border border-white/10;
}
}
/* Custom utility: animation */
@layer utilities {
.animate-float {
animation: float 3s ease-in-out infinite;
}
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
}<!-- Using custom utilities -->
<h1 class="text-6xl font-bold text-gradient">
Gradient Text
</h1>
<h2 class="text-4xl font-bold text-gradient-blue">
Blue Gradient
</h2>
<!-- Glassmorphism card -->
<div class="glass rounded-2xl p-8 shadow-xl">
<h3 class="text-xl font-bold">Glass Card</h3>
<p>With backdrop blur effect</p>
</div>
<!-- Floating animation -->
<div class="animate-float">
<img src="icon.svg" alt="Floating icon">
</div>Reusable Component Patterns
Use @apply for consistent component styles:
@import "tailwindcss";
/* Button variants */
@layer components {
.btn {
@apply px-6 py-3 rounded-lg font-semibold transition-all;
}
.btn-primary {
@apply btn bg-primary-500 hover:bg-primary-600 text-white shadow hover:shadow-lg;
}
.btn-secondary {
@apply btn bg-secondary-500 hover:bg-secondary-600 text-white shadow hover:shadow-lg;
}
.btn-outline {
@apply btn border-2 border-primary-500 text-primary-500 hover:bg-primary-500 hover:text-white;
}
}
/* Card variants */
@layer components {
.card {
@apply bg-white dark:bg-gray-800 rounded-lg shadow-lg overflow-hidden;
}
.card-header {
@apply px-6 py-4 border-b border-gray-200 dark:border-gray-700;
}
.card-body {
@apply p-6;
}
.card-footer {
@apply px-6 py-4 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900;
}
}<!-- Using component classes -->
<button class="btn-primary">Primary Button</button>
<button class="btn-secondary">Secondary Button</button>
<button class="btn-outline">Outline Button</button>
<!-- Card with sections -->
<div class="card">
<div class="card-header">
<h3 class="text-xl font-bold">Card Title</h3>
</div>
<div class="card-body">
<p>Card content goes here...</p>
</div>
<div class="card-footer">
<button class="btn-primary">Action</button>
</div>
</div>⚖️ @apply vs Utilities
Use @apply for:
- Reusable components used across the project
- Complex combinations used repeatedly
- Third-party component styling
Use utilities for:
- One-off or unique elements
- Rapid prototyping
- Maximum flexibility
Practical Example: Complete Theme
Here's a complete example of a custom theme:
@import "tailwindcss";
/* Import custom fonts */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap');
/* Theme customization */
@theme {
/* Typography */
--font-sans: 'Inter', ui-sans-serif, system-ui, sans-serif;
/* Brand colors */
--color-brand-50: #eff6ff;
--color-brand-100: #dbeafe;
--color-brand-200: #bfdbfe;
--color-brand-300: #93c5fd;
--color-brand-400: #60a5fa;
--color-brand-500: #3b82f6;
--color-brand-600: #2563eb;
--color-brand-700: #1d4ed8;
--color-brand-800: #1e40af;
--color-brand-900: #1e3a8a;
/* Accent colors */
--color-accent-500: #8b5cf6;
--color-accent-600: #7c3aed;
/* Semantic colors */
--color-success: #10b981;
--color-warning: #f59e0b;
--color-danger: #ef4444;
/* Custom spacing */
--spacing-18: 4.5rem;
/* Custom breakpoint */
--breakpoint-3xl: 1920px;
}
/* Custom utilities */
@layer utilities {
.text-gradient {
@apply bg-clip-text text-transparent bg-gradient-to-r from-brand-400 to-accent-500;
}
.glass-card {
@apply backdrop-blur-lg bg-white/30 dark:bg-gray-900/30 border border-white/20 rounded-2xl shadow-xl;
}
}
/* Component patterns */
@layer components {
.btn {
@apply px-6 py-3 rounded-lg font-semibold transition-all inline-flex items-center gap-2;
}
.btn-brand {
@apply btn bg-brand-500 hover:bg-brand-600 text-white shadow hover:shadow-lg;
}
.btn-accent {
@apply btn bg-accent-500 hover:bg-accent-600 text-white shadow hover:shadow-lg;
}
}<!-- Using the custom theme -->
<div class="min-h-screen bg-gray-50 dark:bg-gray-900 font-sans">
<!-- Header with glass effect -->
<header class="sticky top-0 glass-card">
<div class="max-w-7xl mx-auto px-6 py-4">
<div class="flex justify-between items-center">
<h1 class="text-2xl font-bold text-gradient">MyBrand</h1>
<nav class="flex gap-6">
<a href="#" class="text-gray-700 dark:text-gray-300 hover:text-brand-500">Home</a>
<a href="#" class="text-gray-700 dark:text-gray-300 hover:text-brand-500">Features</a>
<a href="#" class="text-gray-700 dark:text-gray-300 hover:text-brand-500">Pricing</a>
</nav>
<button class="btn-brand">Get Started</button>
</div>
</div>
</header>
<!-- Hero section -->
<section class="py-18 px-6">
<div class="max-w-4xl mx-auto text-center">
<h2 class="text-5xl md:text-6xl 3xl:text-7xl font-bold text-gradient mb-6">
Build Something Amazing
</h2>
<p class="text-xl text-gray-600 dark:text-gray-400 mb-8">
With our custom design system powered by Tailwind v4
</p>
<div class="flex gap-4 justify-center">
<button class="btn-brand">Get Started</button>
<button class="btn-accent">Learn More</button>
</div>
</div>
</section>
<!-- Feature cards -->
<section class="py-12 px-6">
<div class="max-w-7xl mx-auto grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="glass-card p-8">
<div class="w-12 h-12 bg-brand-500 rounded-lg mb-4"></div>
<h3 class="text-xl font-bold text-gray-900 dark:text-white mb-2">Fast</h3>
<p class="text-gray-600 dark:text-gray-400">Lightning-fast performance</p>
</div>
<div class="glass-card p-8">
<div class="w-12 h-12 bg-accent-500 rounded-lg mb-4"></div>
<h3 class="text-xl font-bold text-gray-900 dark:text-white mb-2">Beautiful</h3>
<p class="text-gray-600 dark:text-gray-400">Stunning designs</p>
</div>
<div class="glass-card p-8">
<div class="w-12 h-12 bg-success rounded-lg mb-4"></div>
<h3 class="text-xl font-bold text-gray-900 dark:text-white mb-2">Reliable</h3>
<p class="text-gray-600 dark:text-gray-400">Rock-solid stability</p>
</div>
</div>
</section>
</div>Customization Playground
Practice with custom theme values:
Build with Custom Theme
Experiment with custom colors and arbitrary values
Custom Theme Demo
Using arbitrary values and custom styles
Arbitrary Width
calc(100% - 1rem)
Custom Shadow
Arbitrary shadow value
Customization Best Practices
Follow These Guidelines:
- Start with defaults: Only customize what you need
- Use semantic names: primary, secondary, not blue, purple
- Create complete scales: 50-950 for colors, not just one shade
- Document your system: Keep a design system guide
- Test dark mode: Ensure custom colors work in both themes
- Limit arbitrary values: Use sparingly, prefer theme values
- Group related values: Keep spacing together, colors together
- Version your theme: Track changes to design tokens
Key Takeaways
- Tailwind v4 uses
@themedirective for CSS-first configuration - Add custom colors with
--color-*variables - Create complete color scales (50-950) for consistency
- Use arbitrary values with square brackets for one-off customizations
- Define custom utilities in
@layer utilities - Create component patterns with
@apply - Extend spacing, fonts, and breakpoints in
@theme - Use semantic naming for design tokens
- Test customizations in both light and dark modes
- Document your design system for team consistency
What's Next?
Congratulations! You've completed the Tailwind CSS tutorial series. You now have comprehensive knowledge of Tailwind from basics to advanced customization.
You've learned:
- ✅ Core utilities (layout, typography, colors, spacing)
- ✅ Responsive design and breakpoints
- ✅ Dark mode implementation
- ✅ Transforms, transitions, and filters
- ✅ Custom theme creation and CSS variables
Continue building projects, explore Tailwind plugins, and create your own design systems. The best way to master Tailwind is through practice!
🎯 Final Challenge!
Create a complete landing page with a custom theme! Include:
- Custom brand color palette (full 50-950 scale)
- Custom fonts from Google Fonts
- Reusable button and card components using @apply
- Custom utilities for text gradients and glass effects
- Responsive design from mobile to 4K screens
- Full dark mode support
- Smooth transitions and hover effects
Build it from scratch and showcase your Tailwind mastery!