Skip to main content

Customization

In this chapter, you learn the CSS-First way of configuring Tailwind CSS v4.

CSS-First Configuration in v4

In v4, configuration is done directly inside a CSS file. tailwind.config.js is unnecessary.

@import "tailwindcss";

@theme {
/* Customize the theme here */
}

Customizing the theme

Adding colors

@import "tailwindcss";

@theme {
/* Brand colors */
--color-brand-50: #f0f9ff;
--color-brand-100: #e0f2fe;
--color-brand-200: #bae6fd;
--color-brand-300: #7dd3fc;
--color-brand-400: #38bdf8;
--color-brand-500: #0ea5e9;
--color-brand-600: #0284c7;
--color-brand-700: #0369a1;
--color-brand-800: #075985;
--color-brand-900: #0c4a6e;
--color-brand-950: #082f49;
}
<div class="bg-brand-500 text-brand-50">Brand color</div>

Overriding an existing color

@theme {
--color-blue-500: #3b82f6; /* Change the default value */
}

Adding fonts

@theme {
--font-display: "Montserrat", sans-serif;
--font-body: "Noto Sans JP", sans-serif;
}
<h1 class="font-display">Display font</h1>
<p class="font-body">Body font</p>

Customizing spacing

The numeric spacing in v4 (p-18 / w-128, etc.) derives dynamically from --spacing (default 0.25rem), so no additional definition is needed in @theme.

<!-- Usable as-is without a definition (p-18 = 4.5rem, w-128 = 32rem) -->
<div class="p-18 w-128">Dynamic spacing</div>

If you want to change the entire scale, override --spacing itself. Only when you need a named value that cannot be expressed with the number × --spacing scale should you add it with --spacing-*.

@theme {
--spacing: 2px; /* p-4 becomes 8px */
}

Adding breakpoints

@theme {
--breakpoint-xs: 480px;
--breakpoint-3xl: 1920px;
}
<div class="xs:block 3xl:max-w-7xl">Custom breakpoint</div>

Adding shadows

@theme {
--shadow-soft: 0 2px 15px -3px rgba(0, 0, 0, 0.07),
0 10px 20px -2px rgba(0, 0, 0, 0.04);
--shadow-glow: 0 0 20px rgba(59, 130, 246, 0.5);
}
<div class="shadow-soft">Soft shadow</div>
<div class="shadow-glow">Glow shadow</div>

Adding border radii

The built-in values in v4 go up to rounded-4xl (2rem). Add larger values with --radius-*.

@theme {
--radius-5xl: 2.5rem;
}
<div class="rounded-5xl">Large border radius</div>

Adding animations

@theme {
--animate-fade-in: fade-in 0.5s ease-out;
--animate-slide-up: slide-up 0.3s ease-out;
}

@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}

@keyframes slide-up {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
<div class="animate-fade-in">Fade in</div>
<div class="animate-slide-up">Slide up</div>

Custom utilities

The difference between @utility and @apply

@utility is a new directive added in v4, and it takes a different approach from @apply. @apply is a mechanism for reusing existing utility classes inside CSS, whereas @utility defines a new utility class itself. Because it can also be combined automatically with variants such as hover: and md:, it is an extension method that aligns with Tailwind's design philosophy.

The @utility directive

From v4.1, text-shadow-2xs through text-shadow-lg became built-in utilities. Choose names for your custom utilities that do not collide with the built-ins.

@utility text-shadow-hard {
text-shadow: 2px 2px 0 rgba(0, 0, 0, 0.15);
}

@utility text-glow {
text-shadow: 0 0 10px rgba(59, 130, 246, 0.6);
}
<h1 class="text-shadow-hard">Crisp text shadow</h1>
<h1 class="text-glow">Glowing text shadow</h1>

Utilities that include nesting (pseudo-elements)

@utility scrollbar-hide {
-ms-overflow-style: none;
scrollbar-width: none;

&::-webkit-scrollbar {
display: none;
}
}

Custom variants

The @custom-variant directive

/* Hover group variant */
@custom-variant hocus (&:hover, &:focus);

/* Theme attribute variant */
@custom-variant theme-midnight (&:where([data-theme="midnight"] *));

/* Custom dark mode implementation */
@custom-variant dark (&:where(.dark, .dark *));

Built-in variants such as print: work as-is without a definition.

<button class="hocus:bg-blue-600">On hover or focus</button>
<div class="print:hidden">Hidden when printing</div>

Adding plugins

You can use plugins in v4 as well. First, install them with npm.

npm install -D @tailwindcss/typography @tailwindcss/forms
@import "tailwindcss";
@plugin "@tailwindcss/typography";
@plugin "@tailwindcss/forms";

@tailwindcss/typography

<article class="prose lg:prose-xl">
<h1>Heading</h1>
<p>Body text...</p>
</article>

@tailwindcss/forms

<input type="text" class="form-input rounded-lg" />
<select class="form-select rounded-lg">
<option>Option</option>
</select>

Resetting default values

You can reset the default styles and use only your own values.

@theme {
/* Reset all colors */
--color-*: initial;

/* Define only your own colors */
--color-primary: #3b82f6;
--color-secondary: #6b7280;
--color-accent: #10b981;
}

Creating a preset

You can create a reusable theme as a CSS file.

preset-corporate.css
@theme {
--color-primary-50: #eff6ff;
--color-primary-500: #3b82f6;
--color-primary-900: #1e3a8a;

--font-sans: "Inter", system-ui, sans-serif;
--font-heading: "Montserrat", sans-serif;

--radius: 0.5rem;
}
main.css
@import "tailwindcss";
@import "./preset-corporate.css";

Practical example: A corporate theme

@import "tailwindcss";

@theme {
/* Brand colors */
--color-primary-50: #f0f9ff;
--color-primary-100: #e0f2fe;
--color-primary-200: #bae6fd;
--color-primary-300: #7dd3fc;
--color-primary-400: #38bdf8;
--color-primary-500: #0ea5e9;
--color-primary-600: #0284c7;
--color-primary-700: #0369a1;
--color-primary-800: #075985;
--color-primary-900: #0c4a6e;

--color-accent-500: #10b981;
--color-accent-600: #059669;

/* Fonts */
--font-sans: "Noto Sans JP", "Inter", system-ui, sans-serif;
--font-heading: "Montserrat", sans-serif;

/* Custom shadow */
--shadow-card: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
0 2px 4px -2px rgba(0, 0, 0, 0.1);

/* Custom animation */
--animate-fade-in: fade-in 0.3s ease-out;
}

@keyframes fade-in {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}

/* Custom utilities */
@utility card {
background-color: white;
border-radius: var(--radius-lg);
box-shadow: var(--shadow-card);
padding: var(--spacing-6);
}

@utility btn-primary {
display: inline-flex;
align-items: center;
justify-content: center;
padding: var(--spacing-2) var(--spacing-4);
background-color: var(--color-primary-500);
color: white;
border-radius: var(--radius-lg);
font-weight: 500;
transition: background-color 0.2s;

&:hover {
background-color: var(--color-primary-600);
}
}
<div class="card">
<h2 class="font-heading text-xl font-bold">Card title</h2>
<p class="mt-2 text-gray-600">Card content...</p>
<button class="btn-primary mt-4">Action</button>
</div>

CSS layers

Tailwind v4 uses CSS layers to organize styles.

@layer base {
/* Base styles */
html {
scroll-behavior: smooth;
}

body {
font-family: var(--font-sans);
}
}

@layer components {
/* Component styles */
.btn {
@apply px-4 py-2 rounded-lg font-medium transition-colors;
}
}
Do not use @layer utilities in v4

Custom classes inside @layer utilities from v3 and earlier are not registered as utilities in v4, and cannot use variants such as hover:. The official API for custom utilities is @utility (see "Custom utilities" earlier in this chapter).

Summary

  • Customize the theme inside CSS with @theme
  • Define theme variables with --color-* / --font-*, etc.
  • Create custom utilities with @utility
  • Create custom variants with @custom-variant
  • Add plugins with @plugin
  • Organize styles with @layer
  • v4 takes a CSS-native approach