A Simple Guide to Background Colors in Tailwind CSS
Tailwind v4 bg colors, OKLCH, dark mode & more

Background color is one of the first things you reach for when building a UI. Tailwind CSS makes it fast to apply — but if you upgraded to v4 and your bg-opacity-50 stopped working, or you're wondering why the docs now mention OKLCH, this guide has you covered.
We'll walk through everything from basic usage to the new v4 color system, custom theme variables, opacity, dark mode, gradients, and common pitfalls.
What are Tailwind's background color classes?
Tailwind provides bg-{color}-{shade} utility classes that set background-color directly in your HTML. In v4, each class maps to a CSS custom property (e.g., var(--color-blue-500)), making colors overridable at runtime without a rebuild.
The class naming convention is:
Color name —
red,blue,green,slate,zinc, etc.Shade number —
50(lightest) through950(darkest), with 11 steps per color.
<div class="bg-blue-500 text-white p-4">
Medium blue background
</div>
<div class="bg-slate-100 p-4">
Very light slate background
</div>Utility classes for special cases include bg-transparent, bg-white, bg-black, bg-inherit, and bg-current.
How to pick the right shade
Shades 50–200 suit subtle page backgrounds and cards. Shades 300–500 work well for interactive elements like buttons. Shades 600–950 are good for accents, dark surfaces, and high-contrast UI.
What are Tailwind's background color classes?
Tailwind provides bg-{color}-{shade} utility classes — such as bg-blue-500 or bg-slate-900 — that set background-color directly in your HTML. In v4, each class maps to a CSS custom property (e.g., var(--color-blue-500)), making colors overridable at runtime without a rebuild.
What changed in Tailwind v4 for background colors? Three things changed significantly.
First, the entire default palette switched from RGB/HEX to the OKLCH color space, producing more vivid colors on wide-gamut displays. Second, bg-opacity-{amount} was removed — you now use the slash modifier (bg-sky-500/50). Third, custom colors move from tailwind.config.js into a @theme {} block in your CSS file.

How do you add a custom background color in Tailwind v4?
Define your color as a CSS variable inside @theme, and Tailwind generates the utility class automatically.
How do you control background color opacity in Tailwind v4?
Use the slash modifier directly on the color class — bg-blue-500/50 for 50% opacity, bg-blue-500/25 for 25%. The old bg-opacity-* utility no longer exists in v4.
How do you use a CSS variable as a background color?
Use the bg-(--my-var) shorthand introduced in v4. It auto-wraps your variable in var(), so bg-(--brand-color) compiles to background-color: var(--brand-color). For one-off arbitrary values you can still write bg-[var(--brand-color)].
How do you set up dark mode background colors in Tailwind v4?
Use the dark: variant prefix on any bg-* class. In v4, dark mode configuration moved out of tailwind.config.js entirely — you either rely on the automatic media-query strategy or configure a custom variant in CSS.
Why isn't my dynamic background class showing up in production?
Tailwind scans source files at build time, so class names assembled at runtime (like template literals) are invisible to the scanner. The fix is to write out the complete class string in your source code, or add the pattern to your content safelist.

What changed in Tailwind v4 for background colors?
Three things changed significantly. It's worth understanding each before diving deeper.
1. Colors are now OKLCH. The entire default palette moved from RGB/HEX to the OKLCH color space. OKLCH produces more perceptually uniform and vibrant colors, especially on wide-gamut (P3) displays. The shade numbers look the same — bg-blue-500 is still "medium blue" — but the underlying value is now oklch(62.3% 0.214 259.815) instead of a hex code.
2. bg-opacity-* is gone. The old bg-opacity-50 utility was removed. You now use the slash modifier directly on the color class.
3. Custom colors move to CSS. Instead of editing tailwind.config.js, you define custom design tokens inside a @theme {} block in your CSS file.
How do you add a custom background color in Tailwind v4?
Define your color as a CSS variable inside @theme, and Tailwind generates the utility class automatically.
/* In your main CSS file (e.g., globals.css) */
@import "tailwindcss";
@theme {
--color-brand: oklch(0.62 0.20 250);
--color-surface-dark: oklch(0.15 0.01 260);
}Now bg-brand and bg-surface-dark are available in your HTML:
<div class="bg-brand text-white p-4">
Brand color background
</div>You can use any CSS color format — hex, RGB, HSL, or OKLCH. Tailwind recommends OKLCH for custom colors to match the v4 default palette.
Replacing the default palette entirely is also possible:
@theme {
--*: initial; /* Wipe all defaults */
--color-primary: oklch(0.55 0.22 260);
--color-secondary: oklch(0.72 0.14 150);
}This leaves only bg-primary and bg-secondary available, which is useful for tight design systems with no stray colors.
How do you control background color opacity in Tailwind v4?
Use the slash modifier directly on the color class. The old bg-opacity-* utility was removed in v4.
<!-- v3 (deprecated) -->
<div class="bg-blue-500 bg-opacity-50">...</div>
<!-- v4 (correct) -->
<div class="bg-blue-500/50">...</div>
The modifier accepts any value from your opacity scale, or an arbitrary value:
<div class="bg-sky-500/75">75% opacity</div>
<div class="bg-sky-500/25">25% opacity</div>
<div class="bg-sky-500/[.06]">6% opacity (arbitrary)</div>Under the hood, v4 uses color-mix() to compute the opacity, which is more composable than the old CSS variable approach.
How do you use a CSS variable as a background color?
Use the bg-(--my-var) shorthand introduced in v4. It auto-wraps the variable in var() for you.
<div class="bg-(--my-brand-color)">...</div>
<!-- Compiles to: background-color: var(--my-brand-color) -->This is especially useful for runtime theming — for example, colors that change based on a user preference or a data attribute:
:root {
--bg-surface: oklch(98% 0.01 260);
}
[data-theme="dark"] {
--bg-surface: oklch(15% 0.01 260);
}<div class="bg-(--bg-surface) p-6">
Adapts to the active theme automatically
</div>For one-off arbitrary values, you can still write bg-[var(--brand-color)] or use any valid CSS color inline:
<div class="bg-[#ff5733]">Hex color</div>
<div class="bg-[hsl(210,100%,50%)]">HSL color</div>
<div class="bg-[oklch(0.7_0.15_200)]">OKLCH color</div>How do you apply background colors on hover, focus, and active states?
Add state variant prefixes before any bg-* class. The syntax is identical to v3.
<!-- Hover -->
<button class="bg-blue-500 hover:bg-blue-700 text-white py-2 px-4 rounded">
Hover me
</button>
<!-- Focus -->
<input class="bg-white focus:bg-gray-50 border rounded py-2 px-3" type="text">
<!-- Active -->
<button class="bg-green-500 active:bg-green-700 text-white py-2 px-4 rounded">
Click me
</button>Add transition duration-200 for a smooth animated change:
<button class="bg-indigo-500 hover:bg-fuchsia-500 transition duration-200 text-white py-2 px-4 rounded">
Animated hover
</button>How do you make background colors responsive?
Prefix any bg-* class with a breakpoint variant. Tailwind uses a mobile-first approach, so unprefixed classes apply at all sizes and prefixed classes override from that breakpoint upward.
<div class="bg-red-500 sm:bg-green-500 md:bg-blue-500 lg:bg-yellow-500">
Changes color at each breakpoint
</div>
Default breakpoints in v4: sm (640px), md (768px), lg (1024px), xl (1280px), 2xl (1536px).
How do you set up dark mode background colors in Tailwind v4?
Use the dark: variant prefix on any bg-* class. In v4, Tailwind defaults to the prefers-color-scheme media query strategy, so no configuration is needed for basic usage.
<div class="bg-white dark:bg-gray-900 text-black dark:text-white p-4">
Adapts to the OS dark mode preference
</div>For a class-based toggle (add a dark class to <html>), define a custom variant in your CSS:
@import "tailwindcss";
@custom-variant dark (&:is(.dark *));Then toggle dark mode with JavaScript:
document.documentElement.classList.toggle('dark');How do you create gradient backgrounds in Tailwind v4?
Use bg-gradient-to-{direction} with from-*, via-*, and to-* color stops. In v4, you can also control the color interpolation space for richer gradients.
<!-- Basic left-to-right gradient -->
<div class="bg-gradient-to-r from-green-400 to-blue-500 text-white p-6">
Gradient background
</div>
<!-- Three-color gradient with a midpoint -->
<div class="bg-gradient-to-r from-pink-500 via-red-500 to-yellow-500 text-white p-6">
Three-color gradient
</div>New in v4: gradient color space interpolation
You can now specify the color space used for gradient interpolation with a modifier:
<!-- Interpolate through OKLCH for more vivid gradients -->
<div class="bg-linear-to-r/oklch from-indigo-500 to-teal-400">...</div>
<!-- Default: OKLAB -->
<div class="bg-linear-to-r from-indigo-500 to-teal-400">...</div>OKLCH and HSL interpolation produce more vibrant midpoint colors when start and end colors are far apart on the color wheel.
Gradient directions: to-t, to-tr, to-r, to-br, to-b, to-bl, to-l, to-tl.
How do you set a full-page background color?
![Using Arbitrary Colors: [#ff5733] Using Arbitrary Colors: [#ff5733]](/tailwind-bg-color-guide-5.jpg)
Apply your background class to the <body> tag or to a wrapper div with min-h-screen.
<!-- On body -->
<body class="bg-gray-100">
<!-- content -->
</body>
<!-- On a wrapper -->
<div class="bg-gray-100 min-h-screen">
<!-- content -->
</div>For dark mode full-page surfaces:
<body class="bg-white dark:bg-gray-950">
<!-- content -->
</body>Why isn't my dynamic background class showing up in production?
Tailwind scans source files at build time, so class names assembled at runtime — like template literals — are invisible to the scanner.
<!-- This will be purged in production -->
<div class="bg-{{ color }}-500">...</div>
<!-- This won't work either -->
<div class="{{ `bg-${color}-500` }}">...</div>The correct approach is to keep all possible class strings as complete literals in your source:
// Use a lookup object with full class names
const colorMap = {
red: 'bg-red-500',
blue: 'bg-blue-500',
green: 'bg-green-500',
};
<div className={colorMap[color]}>...</div>
Alternatively, add a safelist pattern to your build config. In v4 this is handled via a @source annotation or the Vite/PostCSS plugin config.
Accessibility and contrast

Always verify that your text color has sufficient contrast against its background. The WCAG AA standard requires a minimum contrast ratio of 4.5:1 for normal text.
A few practical rules with Tailwind's default palette:
text-whiteonbg-blue-600or darker — generally passes AA.text-gray-900onbg-gray-100or lighter — generally passes AA.Always verify with a contrast checker tool when mixing custom OKLCH colors.
Best practices
Keep your color palette small and consistent - define your brand colors once in
@themeand reuse them everywhere. Use the default Tailwind palette for neutral surfaces and only reach for custom colors for brand-specific values.Avoid scattering arbitrary hex values throughout your templates; one-off colors are fine for prototyping but should be promoted to named tokens before shipping.
FAQ
Does bg-opacity-* still work in Tailwind v4?
No. The bg-opacity-* utility was removed in v4. Replace bg-blue-500 bg-opacity-50 with bg-blue-500/50.
Can I use hex or RGB values in custom themes in v4?
Yes. @theme { --color-brand: #3b82f6; } works fine. Tailwind recommends OKLCH for new custom colors to match the default palette, but any valid CSS color format is accepted.
What is OKLCH and do I need to learn it?
OKLCH is a modern CSS color space with better perceptual uniformity than RGB or HSL. For most use cases, you don't need to understand the math - just know that Tailwind's built-in palette now uses it, and your colors will look slightly more vivid on newer displays. If you define custom colors, OKLCH is the preferred format but not mandatory.
Why does my background color look different in v4 than in v3?
The default palette was re-tuned when migrated to OKLCH. Colors kept the same names and relative lightness, but some shades are slightly more saturated. If you need to match exact v3 colors, define them manually in @theme using the original hex values.

Yucel is a digital product creator and content writer with a knack for full-stack development. He loves blending technical know-how with engaging storytelling to build practical, user-friendly solutions. When he's not coding or writing, you'll likely find him exploring new tech trends or getting inspired by nature.