99

Concurrent visitors over the last 30 minutes. (~)

Peak concurrent visitors -
Yesterday: 0 / Today: 0

Tailwind CSS Core Patterns

Tailwind CSS Core Patterns

This post is based on Tailwind CSS version 4.0.8.
To follow along, you need intermediate or higher CSS knowledge and at least beginner-level React knowledge.

# Overview

Tailwind CSS is a CSS framework that minimizes writing separate CSS files and lets you style quickly by applying utility classes and the like right in your HTML markup.
It also lets you handle other styling, such as responsive design, dark mode, and hover states, inside your HTML.
This is especially useful in frameworks like React, where you modularize CSS into separate files or use CSS-in-JS libraries such as Emotion or styled-components.
You can of course use Tailwind CSS in Vue and Svelte too, but since they let you include and modularize CSS in single-file components (SFC) such as *.vue and *.svelte without any extra library, it may be relatively less useful there.

Also, unlike typical CSS frameworks such as Bootstrap, Tailwind CSS doesn't provide predefined styled components by default.
Instead, you build the design you want by combining small utility classes directly in your HTML.
This way, only the styles you use end up in the final build, which reduces the bundle size even further.

In the following example, the two elements use an inline style and utility classes, and both produce the same result.

HTML
1
2
<div style={{ height: '100px', width: '100px', backgroundColor: 'red' }}></div> <div className="h-[100px] w-[100px] bg-[red]"></div>
Inline style vs. utility classes with arbitrary values

Tailwind CSS also provides predefined class values to help you write optimized styles.
However, these values are less intuitive, and you need to learn their patterns to use them effectively.
So this post analyzes and introduces the main patterns you will use most often.

HTML
1
2
<div style={{ height: '100px', width: '100px', backgroundColor: 'red' }}></div> <div className="h-25 w-25 bg-red-500"></div>
Inline style vs. regular utility classes

Tailwind CSS makes it easy to handle state-based styles with something called variants.

First, with plain inline styles you can't handle a hover effect, so you have to write separate CSS like the .box class definition below.

CSS
1
2
3
4
5
6
7
8
.box { width: 100px; height: 100px; background-color: red; } .box:hover { background-color: blue; }

With Tailwind CSS, however, you can handle the hover effect easily with the hover: variant.
In the following example, the bg-blue-500 utility class is applied only in the hover state.

HTML
1
2
<div className="box"></div> <div className="h-25 w-25 bg-red-500 hover:bg-blue-500"></div>

If you need to reuse styles, you can abstract them with the @utility directive as follows.
As in the example below, you can write properties and values, or use utility classes with the @apply directive.

CSS
1
2
3
4
5
6
7
8
9
10
11
@utility box1 { width: 100px; height: 100px; background-color: red; &:hover { background-color: blue; } } @utility box2 { @apply w-25 h-25 bg-red-500 hover:bg-blue-500; }
HTML
1
2
<div className="box1"></div> <div className="box2"></div>
Using custom utility classes

# Installation and setup

# VS Code extensions

There are several Tailwind CSS extensions you can use in VS Code.

# Tailwind CSS IntelliSense

Tailwind CSS IntelliSense is the official extension, offering features such as autocomplete, syntax highlighting, and linting.

Tailwind CSS IntelliSense

When you type part of a class, it autocompletes a list of matching utility classes.
You can pick the class you want from the list and apply it easily.

Class autocomplete

When you hover over an applied utility class, it shows the CSS that actually gets applied.
This makes it easy to understand styles that are already written.

Class preview

# Tailwind Docs

Tailwind Docs lets you search the Tailwind CSS documentation easily.
In VS Code, open the Command Palette (Cmd or Ctrl + Shift + P) and type tailwind <search term>.

Tailwind Docs

In the search results, select the style you want to apply to see how to use its utility classes right away.
The documentation page opens inside VS Code instead of a separate browser.

Searching the docs from the Command Palette

# Vite

In a Vite project, install and set up Tailwind CSS as follows.
If you don't use Prettier, skip installing prettier-plugin-tailwindcss.

BASH
1
npm i -D tailwindcss @tailwindcss/vite prettier-plugin-tailwindcss
/vite.config.ts
TS
1
2
3
4
5
6
7
8
import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import tailwindcss from '@tailwindcss/vite' // https://vite.dev/config/ export default defineConfig({ plugins: [react(), tailwindcss()] })

Set up Tailwind CSS in the global stylesheet connected to /src/main.tsx as follows.
Earlier versions used the @tailwind directive, but starting with v4, you use the CSS @import rule.

/src/index.css
CSS
1
2
3
4
/* @tailwind base; @tailwind components; @tailwind utilities; */ @import "tailwindcss";

If you use Prettier, register the plugin as follows.

/.prettierrc
JSON
1
2
3
4
5
{ "semi": false, "singleQuote": true, "plugins": ["prettier-plugin-tailwindcss"] }

# Removing non-standard rule warnings

When you use Tailwind CSS directives such as @theme and @apply, your CSS or SCSS files may show warnings.

Warnings for non-standard rules

If you use the Tailwind CSS IntelliSense extension in VS Code, you can remove the warnings by adding the following to your settings.json file.

/.vscode/settings.json
JSON
1
2
3
4
5
6
{ "files.associations": { "*.css": "tailwindcss", "*.scss": "tailwindcss" } }

# Next

When you create a new project with the latest Next, you don't need to set up Tailwind CSS separately if you choose the option to use it.
For an existing Next project, see Starting with the new Tailwind CSS v4 in a Next.js project.

BASH
1
2
3
4
5
6
7
8
npx create-next-app@latest <project-name> ✔ Would you like to use TypeScript? … Yes # Use TypeScript? ✔ Would you like to use ESLint? … Yes # Use ESLint? ✔ Would you like to use Tailwind CSS? … Yes # Use Tailwind CSS? ✔ Would you like your code inside a `src/` directory? … No # Use the src/ directory? ✔ Would you like to use App Router? (recommended) … Yes # Use the App Router? ✔ Would you like to use Turbopack for next dev? … No # Use Turbopack? ✔ Would you like to customize the import alias (@/* by default)? … No # Use a path alias other than `@/*`?

If you use Prettier, install and register the plugin as follows, just like in the Vite setup.
Removing non-standard rule warnings works the same way, so see the Vite setup described earlier.

BASH
1
npm i -D prettier-plugin-tailwindcss
/.prettierrc
JSON
1
2
3
4
5
{ "semi": false, "singleQuote": true, "plugins": ["prettier-plugin-tailwindcss"] }

# Utility classes

A Tailwind CSS utility class is a small CSS class that holds one or more CSS properties and values, and it lets you apply CSS properties easily.

# Understanding the common patterns

Most utility classes map directly to a single CSS property, such as w-* for width and m-* for margin, but some classes combine several properties, like size-*, which combines width and height.
On top of that, the naming patterns aren't consistent: the flex-basis property uses the basis-* class, but the flex-wrap property uses the flex-* class. The usage patterns vary too: the number in the px-2 class is multiplied by the base spacing (--spacing), but the number in the border-1 class is applied in px. So there is a bit of a learning curve.
To make this easier to understand, I've summarized the common patterns of utility classes.
Once you understand the common patterns, check out the table of properties and utility classes.

# Basic usage

The most common pattern combines a CSS property prefix and that property's CSS value with a dash (-).
For example, you take the CSS float: left; property and value and join them as is to write the float-left class.
For the box-sizing: border-box; property and value, you have to shorten the property name to box-*, as in the box-border class.

Also, the abbreviations aren't consistent (width becomes w-*, text-indent becomes indent-*, and border-radius becomes rounded-*), so be careful when using them.
Be careful not to write an invalid class, such as text-indent-1 instead of indent-1.

HTML
1
<div className="float-left box-border w-10 rounded-sm indent-1"></div>
float: left; box-sizing: border-box; width: 2.5rem; border-radius: 0.25rem; text-indent: 0.25rem;

# Special values

There is also a pattern that combines a property prefix with special values such as predefined keywords, numbers, and ratios.
For example, you write the aspect-ratio: 16/9; property and value as a class with a ratio, aspect-16/9, or with a keyword, aspect-video, and you write width: 100%; as the w-full class.

Some classes, like columns-*, accept a size name as the value, as in columns-md.
There are five basic size names (xs, sm, md, lg, xl), and you can put a number of 2 or higher in front of the smallest (xs) or largest (xl) size to express even smaller or larger values, such as 3xs, 2xs, 6xl, and 7xl.

Also, for some CSS properties that accept negative values, you can add a minus sign (-) in front of the class.
For example, you write order: -1; as the -order-1 class.

HTML
1
<div className="-order-1 -m-4 aspect-square columns-md"></div>
order: -1; margin: -1rem; aspect-ratio: 1/1; columns: 28rem;

# Value-only classes

There is a pattern that uses only the value as the class, without a property prefix.
For example, you write the display: block property and value as the block class, not display-block.

HTML
1
<span className="visible absolute block"></span>
visibility: visible; position: absolute; display: block;

# Using variables

You can add parentheses (()) to the property prefix to specify a variable without the var() function.
For example, you can write the width: var(--size); property and value as the w-(--size) class.

You can define a local variable like [--size:200px], using the arbitrary values approach covered in the next topic.

HTML
1
<div className="h-[calc(var(--size)+50px)] w-(--size) [--size:200px]"></div>
width: 200px; height: 250px;

# Arbitrary values

You can add square brackets ([]) to the property prefix to specify an arbitrary value that isn't predefined.
For example, you can write the width: 123px; property and value as the w-[123px] class.

You can also use CSS functions, as in w-[calc(100%-40px)].
Note that whitespace (such as spaces) in the middle, as in 100% - 40px, is not allowed.

In addition, you can write a property and value as is inside [].
You can use this freely, for example to declare a local variable or to apply a property that Tailwind CSS doesn't support.

HTML
1
<div className="h-[calc(var(--size)+50px)] w-[var(--size)] [--size:200px]"></div>
width: 200px; height: 250px;
HTML
1
<input className="[width:200px] text-[#ff0000] [-webkit-text-security:circle]" />
width: 200px; -webkit-text-security: circle; color: #ff0000;

# Colors

Tailwind CSS includes a color palette by default.
You write classes with the PREFIX-COLOR-SHADE pattern, like bg-red-500 and text-blue-300.

Tailwind CSS color palette

There are 22 color names you can use, from Red to Stone.
For shades, there are 9 basic steps from 100 to 900 in increments of 100, plus the lightest 50 and the darkest 950, for a total of 11.
The idea is that 500 in the middle is the base color, and you lower the number for a lighter color or raise it for a darker one.
White and black have no shades, so you write them as bg-white and border-black.

If you want to make a color transparent, add an opacity from 0 to 100 at the end of the class, like bg-red-500/70 or bg-black/15.
Note that 100 is fully opaque and 0 is fully transparent.

HTML
1
2
3
<div className="size-[100px] border-2 border-blue-700/90 bg-red-500/50 text-pink-50"> Colors </div>

Also, every utility class that applies a color can additionally use the *-inherit, *-current, and *-transparent classes.
They let you inherit the parent's color (inherit), explicitly use the current color (currentColor), or make it transparent (transparent).

HTML
1
2
3
<div className="bg-inherit"> background-color: inherit; </div> <div className="accent-current"> accent-color: currentColor; </div> <div className="border-transparent"> border-color: transparent; </div>

# Properties and utility classes table

Based on the common patterns we just looked at, here is a list of each property and its utility classes.
Press Cmd or Ctrl + F in your browser to search, and you can find a property or class name more easily.
For details, you can select a CSS property name to go to the matching Tailwind CSS documentation.

In each table, (Variable) shows whether you can use a CSS variable, like w-(--size).
[Arbitrary] shows whether you can specify an arbitrary value, like w-[100px].

# Layout

CSS property Utility class Style or value (Variable) [Arbitrary]
aspect-ratio aspect-RATIO
aspect-square
aspect-video
aspect-auto

1 / 1
16 / 9
auto
O O
columns columns-NUMBER
columns-3xs to 7xl
columns-auto


auto
O O
break-after break-after-auto
break-after-avoid
break-after-all
break-after-avoid-page
break-after-page
break-after-left
break-after-right
break-after-column
auto
avoid
all
avoid-page
page
left
right
column
break-before break-before-auto
break-before-avoid
break-before-all
break-before-avoid-page
break-before-page
break-before-left
break-before-right
break-before-column
auto
avoid
all
avoid-page
page
left
right
column
break-inside break-inside-auto
break-inside-avoid
break-inside-avoid-page
break-inside-avoid-column
auto
avoid
avoid-page
avoid-column
box-decoration-break box-decoration-clone
box-decoration-slice
clone
slice
box-sizing box-border
box-content
border-box
content-box
display inline
block
inline-block
...
hidden
sr-only
not-sr-only
inline
block
inline-block
...
none
float float-right
float-left
float-start
float-end
float-none
right
left
inline-start
inline-end
none
clear clear-left
clear-right
clear-both
clear-start
clear-end
clear-none
left
right
both
inline-start
inline-end
none
isolation isolate
isolation-auto
isolate
auto
object-fit object-contain
object-cover
object-fill
object-none
object-scale-down
contain
cover
fill
none
scale-down
object-position object-bottom
object-center
object-left
object-left-bottom
object-left-top
object-right
object-right-bottom
object-right-top
object-top
bottom
center
left
left bottom
left top
right
right bottom
right top
top
O O
overflow overflow-auto
overflow-hidden
overflow-clip
overflow-visible
overflow-scroll
overflow-x-auto
overflow-y-auto
overflow-x-hidden
overflow-y-hidden
overflow-x-clip
overflow-y-clip
overflow-x-visible
overflow-y-visible
overflow-x-scroll
overflow-y-scroll
auto
hidden
clip
visible
scroll
auto
auto
hidden
hidden
clip
clip
visible
visible
scroll
scroll
overscroll-behavior overscroll-auto
overscroll-contain
overscroll-none
overscroll-x-auto
overscroll-x-contain
overscroll-x-none
overscroll-y-auto
overscroll-y-contain
overscroll-y-none
auto
contain
none
auto
contain
none
auto
contain
none
position static
fixed
absolute
relative
sticky
static
fixed
absolute
relative
sticky
top / right / bottom / left inset-*
inset-x-*
inset-y-*
start-*
end-*
top-*
right-*
bottom-*
left-*

*-NUMBER
*-FRACTION
*-px
*-full
*-auto










0.25rem * NUMBER
100% * FRACTION
1px
100%
auto
O O
visibility visible
invisible
collapse
visible
hidden
collapse
z-index z-NUMBER
z-auto

auto
O O

# Flex & grid

CSS property Utility class Style or value (Variable) [Arbitrary]
flex-basis basis-NUMBER
basis-FRACTION
basis-full
basis-auto
basis-3xs to 7xl
0.25rem * NUMBER
100% * FRACTION
100%
auto
O O
flex-direction flex-row
flex-row-reverse
flex-col
flex-col-reverse
row
row-reverse
column
column-reverse
flex-wrap flex-nowrap
flex-wrap
flex-wrap-reverse
nowrap
wrap
wrap-reverse
flex flex-NUMBER
flex-FRACTION
flex-auto
flex-initial
flex-none

100% * FRACTION
1 1 auto
0 1 auto
none
O O
flex-grow grow
grow-NUMBER
1 O O
flex-shrink shrink
shrink-NUMBER
1 O O
order order-NUMBER
order-first
order-last
order-none

-infinity
infinity
0
O O
grid-template-columns grid-cols-NUMBER
grid-cols-none
grid-cols-subgrid
repeat(NUMBER, minmax(0, 1fr))
none
subgrid
O O
grid-column col-span-NUMBER
col-span-full
col-start-NUMBER
col-start-auto
col-end-NUMBER
col-end-auto
col-auto
col-NUMBER
span NUMBER / span NUMBER
1 / -1

auto

auto
auto
O O
grid-template-rows grid-rows-NUMBER
grid-rows-none
grid-rows-subgrid
repeat(NUMBER, minmax(0, 1fr))
none
subgrid
O O
grid-row row-span-NUMBER
row-span-full
row-start-NUMBER
row-start-auto
row-end-NUMBER
row-end-auto
row-auto
row-NUMBER
span NUMBER / span NUMBER
1 / -1

auto

auto
auto
O O
grid-auto-flow grid-flow-row
grid-flow-col
grid-flow-dense
grid-flow-row-dense
grid-flow-col-dense
row
column
dense
row dense
column dense
grid-auto-columns auto-cols-auto
auto-cols-min
auto-cols-max
auto-cols-fr
auto
min-content
max-content
minmax(0, 1fr)
O O
grid-auto-rows auto-rows-auto
auto-rows-min
auto-rows-max
auto-rows-fr
auto
min-content
max-content
minmax(0, 1fr)
O O
gap gap-NUMBER
gap-x-NUMBER
gap-y-NUMBER
0.25rem * NUMBER O O
justify-content justify-start
justify-end
justify-center
justify-between
justify-around
justify-evenly
justify-stretch
justify-baseline
justify-normal
flex-start
flex-end
center
space-between
space-around
space-evenly
stretch
baseline
normal
justify-items justify-items-start
justify-items-end
justify-items-center
justify-items-stretch
justify-items-normal
start
end
center
stretch
normal
justify-self justify-self-auto
justify-self-start
justify-self-end
justify-self-center
justify-self-stretch
auto
start
end
center
stretch
align-content content-normal
content-center
content-start
content-end
content-between
content-around
content-evenly
content-baseline
content-stretch
normal
center
flex-start
flex-end
space-between
space-around
space-evenly
baseline
stretch
align-items items-start
items-end
items-center
items-baseline
items-stretch
flex-start
flex-end
center
baseline
stretch
align-self self-auto
self-start
self-end
self-center
self-stretch
self-baseline
auto
flex-start
flex-end
center
stretch
baseline
place-content place-content-center
place-content-start
place-content-end
place-content-between
place-content-around
place-content-evenly
place-content-baseline
place-content-stretch
center
start
end
space-between
space-around
space-evenly
baseline
stretch
place-items place-items-start
place-items-end
place-items-center
place-items-baseline
place-items-stretch
start
end
center
baseline
stretch
place-self place-self-auto
place-self-start
place-self-end
place-self-center
place-self-stretch
auto
start
end
center
stretch

# Spacing

space-* means the margin between elements, and you apply it to the parent element, not to the target elements.
The following example adds 8px (0.5rem) of margin between each <li>.
No margin is added above the first <li> or below the last <li>.

HTML
1
2
3
4
5
<ul className="space-y-2"> <li>Apple</li> <li>Banana</li> <li>Cherry</li> </ul>

Margin between elements

CSS property Utility class Style or value (Variable) [Arbitrary]
padding p-*
px-*
py-*
ps-*
pe-*
pt-*
pb-*
pl-*
pr-*

*-NUMBER
*-px










0.25rem * NUMBER
1px
O O
margin m-*
mx-*
my-*
ms-*
me-*
mt-*
mb-*
ml-*
mr-*
space-x-*
space-y-*
space-x-reverse
space-y-reverse

*-NUMBER
*-px














0.25rem * NUMBER
1px
O O

# Sizing

CSS property Utility class Style or value (Variable) [Arbitrary]
width w-*
w-screen
size-*

*-3xs to 7xl
*-NUMBER
*-FRACTION
*-auto
*-px
*-full
*-dvw
*-dvh
*-lvw
*-lvh
*-svw
*-svh
*-min
*-max
*-fit

100vw



0.25rem * NUMBER
100% * FRACTION
auto
1px
100%
100dvw
100dvh
100lvw
100lvh
100svw
100svh
min-content
max-content
fit-content
O O
min-width min-w-*

*-(same as above)
O O
max-width max-w-*

*-(same as above)
O O
height h-*
h-screen
size-*

*-(same as above)

100vh
O O
min-height min-h-*

*-(same as above)
O O
max-height max-h-*

*-(same as above)
O O

# Fonts & text

When you set the font size with a text-* class, you can also set the line height (line-height) by adding a /.

HTML
1
2
<div className="text-sm">font-size: 14px;</div> <div className="text-sm/6">font-size: 14px; line-height: 1.5;</div>

The CSS content property can be used in the ::before and ::after pseudo-element selectors, so you use content-* classes with the before: and after: variants.
When you write text as the value, you must use _ instead of spaces, and conversely, to use an actual _, you need to escape it.
Note that inside the url() function, you don't need to escape it.

HTML
1
2
3
<div className="before:content-['Have_a_great_day!']">Have a great day!</div> <div className="before:content-['Have\_a\_great\_day!']">Have_a_great_day!</div> <div className="before:content-[url('https://www.heropy.dev/images/profile_m.jpg')]">HEROPY</div>
CSS property Utility class Style or value (Variable) [Arbitrary]
font-family font-sans
font-serif
font-mono
var(--font-sans)
var(--font-serif)
var(--font-mono)
O O
font-size text-xs to 9xl
text-base

1rem
O O
font-smoothing antialiased
subpixel-antialiased
antialiased
auto
font-style italic
not-italic
italic
normal
font-weight font-thin
font-extralight
font-light
font-normal
font-medium
font-semibold
font-bold
font-extrabold
font-black
100
200
300
400
500
600
700
800
900
O O
font-stretch font-stretch-ultra-condensed
font-stretch-extra-condensed
font-stretch-condensed
font-stretch-semi-condensed
font-stretch-normal
font-stretch-semi-expanded
font-stretch-expanded
font-stretch-extra-expanded
font-stretch-ultra-expanded
font-stretch-PERCENT
ultra-condensed (50%)
extra-condensed (62.5%)
condensed (75%)
semi-condensed (87.5%)
normal (100%)
semi-expanded (112.5%)
expanded (125%)
extra-expanded (150%)
ultra-expanded (200%)
O O
font-variant-numeric normal-nums
ordinal
slashed-zero
lining-nums
oldstyle-nums
proportional-nums
tabular-nums
diagonal-fractions
stacked-fractions
normal
ordinal
slashed-zero
lining-nums
oldstyle-nums
proportional-nums
tabular-nums
diagonal-fractions
stacked-fractions
letter-spacing tracking-tighter
tracking-tight
tracking-normal
tracking-wide
tracking-wider
tracking-widest
-0.05em
-0.025em
0em
0.025em
0.05em
0.1em
O O
line-clamp line-clamp-NUMBER
line-clamp-none

unset
O O
line-height leading-none
leading-NUMBER

0.25rem * NUMBER
O O
list-style-image list-image-none O O
list-style-position list-inside
list-outside
inside
outside
list-style-type list-disc
list-decimal
list-none
disc
decimal
none
O O
text-align text-left
text-center
text-right
text-justify
text-start
text-end
left
center
right
justify
start
end
color text-COLOR O O
text-decoration-line underline
overline
line-through
no-underline
underline
overline
line-through
none
text-decoration-color decoration-COLOR O O
text-decoration-style decoration-solid
decoration-double
decoration-dotted
decoration-dashed
decoration-wavy
solid
double
dotted
dashed
wavy
text-decoration-thickness decoration-NUMBER
decoration-from-font
decoration-auto
NUMBERpx
from-font
auto
O O
text-underline-offset underline-offset-NUMBER
underline-offset-auto
NUMBERpx
auto
O O
text-transform uppercase
lowercase
capitalize
normal-case
uppercase
lowercase
capitalize
none
text-overflow truncate


text-ellipsis
text-clip
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
ellipsis
clip
text-wrap text-wrap
text-nowrap
text-balance
text-pretty
wrap
nowrap
balance
pretty
text-indent indent-NUMBER
indent-px
0.25rem * NUMBER
1px
O O
vertical-align align-baseline
align-top
align-middle
align-bottom
align-text-top
align-text-bottom
align-sub
align-super
baseline
top
middle
bottom
text-top
text-bottom
sub
super
O O
white-space whitespace-normal
whitespace-nowrap
whitespace-pre
whitespace-pre-line
whitespace-pre-wrap
whitespace-break-spaces
normal
nowrap
pre
pre-line
pre-wrap
break-spaces
word-break break-normal

break-words
break-all
break-keep
overflow-wrap: normal
word-break: normal
break-word
break-all
keep-all
hyphens hyphens-none
hyphens-manual
hyphens-auto
none
manual
auto
content content-none none O O

# Backgrounds

To set a gradient on an element's background, you can use the from-*, via-*, and to-* classes to set the colors of the start, middle, and end points.
The gradient in the following example starts with red on the left, passes through yellow, and ends with blue on the right.

HTML
1
<div className="h-20 w-full bg-linear-to-r from-red-500 via-yellow-500 to-blue-500"></div>

from-red-500 via-yellow-500 to-blue-500

The from-*, via-*, and to-* classes can set not only the color of each point but also its stop position.
In the following example, red stops at 10%, yellow at 30%, and blue at 50%, and the colors in between blend naturally.

HTML
1
<div className="h-20 w-full bg-linear-to-r from-red-500 from-10% via-yellow-500 via-30% to-blue-500 to-50%"></div>

from-10% via-30% to-50%

Note that in CSS, an element's background color uses the background-color property, but a background gradient has to use the background-image property.

CSS property Utility class Style or value (Variable) [Arbitrary]
background-attachment bg-fixed
bg-local
bg-scroll
fixed
local
scroll
background-clip bg-clip-border
bg-clip-padding
bg-clip-content
bg-clip-text
border-box
padding-box
content-box
text
background-color bg-COLOR O O
background-image bg-none
bg-linear-to-t
bg-linear-to-tr
bg-linear-to-r
bg-linear-to-br
bg-linear-to-b
bg-linear-to-bl
bg-linear-to-l
bg-linear-to-tl
bg-linear-ANGLE
bg-radial
bg-conic-ANGLE
from-COLOR
from-PERCENT
via-COLOR
via-PERCENT
to-COLOR
to-PERCENT
none
linear-gradient(to top)
linear-gradient(to top right)
linear-gradient(to right)
linear-gradient(to bottom right)
linear-gradient(to bottom)
linear-gradient(to bottom left)
linear-gradient(to left)
linear-gradient(to top left)
linear-gradient(ANGLE)
radial-gradient(in oklab)
conic-gradient(from ANGLE in oklab)
O O
background-origin bg-origin-border
bg-origin-padding
bg-origin-content
border-box
padding-box
content-box
background-position bg-bottom
bg-center
bg-left
bg-left-bottom
bg-left-top
bg-right
bg-right-bottom
bg-right-top
bg-top
bottom
center
left
left bottom
left top
right
right bottom
right top
top
O O
background-repeat bg-repeat
bg-repeat-x
bg-repeat-y
bg-repeat-space
bg-repeat-round
bg-no-repeat
repeat
repeat-x
repeat-y
space
round
no-repeat
background-size bg-auto
bg-cover
bg-contain
auto
cover
contain
O O

# Borders

divide-* means the line (border) between elements, and you apply it to the parent element, not to the target elements.
The following example adds a line between each <li>.
No line appears above the first <li> or below the last <li>.

HTML
1
2
3
4
5
<ul className="divide-y-2"> <li>Apple</li> <li>Banana</li> <li>Cherry</li> </ul>

Lines between elements

CSS property Utility class Style or value (Variable) [Arbitrary]
border-radius rounded-*
rounded-s-*
rounded-e-*
rounded-t-*
rounded-r-*
rounded-b-*
rounded-l-*
rounded-ss-*
rounded-se-*
rounded-ee-*
rounded-es-*
rounded-tl-*
rounded-tr-*
rounded-br-*
rounded-bl-*

*-xs
to
*-3xl
*-none
*-full

start-start (top left) + end-start (bottom left)
start-end (top right) + end-end (bottom right)
top-left + top-right
top-right + bottom-right
bottom-right + bottom-left
top-left + bottom-left
start-start
start-end
end-end
end-start
top-left
top-right
bottom-right
bottom-left

0.125rem (2px)

1.5rem (24px)
0
infinity px
O O
border-width border
border-NUMBER
border-x
border-x-NUMBER
border-y
border-y-NUMBER
border-s
border-s-NUMBER
border-e
border-e-NUMBER
border-t
border-t-NUMBER
border-r
border-r-NUMBER
border-b
border-b-NUMBER
border-l
border-l-NUMBER
divide-x
divide-x-NUMBER
divide-y
divide-y-NUMBER
divide-x-reverse
divide-y-reverse
1px
NUMBERpx
1px
NUMBERpx
1px
NUMBERpx
1px
NUMBERpx
1px
NUMBERpx
1px
NUMBERpx
1px
NUMBERpx
1px
NUMBERpx
1px
NUMBERpx
1px
NUMBERpx
1px
NUMBERpx
O O
border-color border-COLOR
border-x-COLOR
border-y-COLOR
border-s-COLOR
border-e-COLOR
border-t-COLOR
border-r-COLOR
border-b-COLOR
border-l-COLOR
divide-COLOR
O O
border-style border-solid
border-dashed
border-dotted
border-double
border-hidden
border-none
divide-solid
divide-dashed
divide-dotted
divide-double
divide-hidden
divide-none
solid
dashed
dotted
double
hidden
none
outline-width outline
outline-NUMBER
1px
NUMBERpx
O O
outline-color outline-COLOR O O
outline-style outline-solid
outline-dashed
outline-dotted
outline-double
outline-none
outline-hidden
solid
dashed
dotted
double
none
outline: 2px solid transparent;
outline-offset: 2px;
outline-offset outline-offset-NUMBER NUMBERpx O O

# Effects

CSS property Utility class Style or value (Variable) [Arbitrary]
box-shadow shadow-2xs to 2xl
shadow-none
shadow-COLOR
inset-shadow-2xs to sm
inset-shadow-none
inset-shadow-COLOR
ring
ring-NUMBER
ring-COLOR
inset-ring
inset-ring-NUMBER
inset-ring-COLOR

none


none

0 0 0 1px
0 0 0 NUMBERpx

inset 0 0 0 1px
inset 0 0 0 NUMBERpx
O O
opacity opacity-NUMBER NUMBER% O O
mix-blend-mode mix-blend-normal
mix-blend-multiply
mix-blend-screen
mix-blend-overlay
mix-blend-darken
mix-blend-lighten
mix-blend-color-dodge
mix-blend-color-burn
mix-blend-hard-light
mix-blend-soft-light
mix-blend-difference
mix-blend-exclusion
mix-blend-hue
mix-blend-saturation
mix-blend-color
mix-blend-luminosity
mix-blend-plus-darker
mix-blend-plus-lighter
normal
multiply
screen
overlay
darken
lighten
color-dodge
color-burn
hard-light
soft-light
difference
exclusion
hue
saturation
color
luminosity
plus-darker
plus-lighter
background-blend-mode bg-blend-normal
bg-blend-multiply
bg-blend-screen
bg-blend-overlay
bg-blend-darken
bg-blend-lighten
bg-blend-color-dodge
bg-blend-color-burn
bg-blend-hard-light
bg-blend-soft-light
bg-blend-difference
bg-blend-exclusion
bg-blend-hue
bg-blend-saturation
bg-blend-color
bg-blend-luminosity
normal
multiply
screen
overlay
darken
lighten
color-dodge
color-burn
hard-light
soft-light
difference
exclusion
hue
saturation
color
luminosity

# Filters

CSS property Utility class Style or value (Variable) [Arbitrary]
filter filter-none
blur-xs to 3xl
blur-none
brightness-NUMBER
contrast-NUMBER
drop-shadow-xs to 2xl
drop-shadow-none
grayscale
grayscale-NUMBER
hue-rotate-NUMBER
invert
invert-NUMBER
saturate-NUMBER
sepia
sepia-NUMBER
none

none
brightness(NUMBER%)
contrast(NUMBER%)

drop-shadow(0 0 #0000)
grayscale(100%)
grayscale(NUMBER%)
hue-rotate(NUMBERdeg)
invert(100%)
invert(NUMBER%)
saturate(NUMBER%)
sepia(100%)
sepia(NUMBER%)
O O
backdrop-filter backdrop-filter-none
backdrop-blur-xs to 3xl
backdrop-blur-none
backdrop-brightness-NUMBER
backdrop-contrast-NUMBER
backdrop-grayscale
backdrop-grayscale-NUMBER
backdrop-hue-rotate-NUMBER
backdrop-invert
backdrop-invert-NUMBER
backdrop-opacity-NUMBER
backdrop-saturate-NUMBER
backdrop-sepia
backdrop-sepia-NUMBER
none

none
brightness(NUMBER%)
contrast(NUMBER%)
grayscale(100%)
grayscale(NUMBER%)
hue-rotate(NUMBERdeg)
invert(100%)
invert(NUMBER%)
opacity(NUMBER%)
saturate(NUMBER%)
sepia(100%)
sepia(NUMBER%)
O O

# Tables

CSS property Utility class Style or value (Variable) [Arbitrary]
border-collapse border-collapse
border-separate
collapse
separate
border-spacing border-spacing-NUMBER
border-spacing-x-NUMBER
border-spacing-y-NUMBER
O O
table-layout table-auto
table-fixed
auto
fixed
caption-side caption-top
caption-bottom
top
bottom

# Transitions & animation

You can create your own animation utility classes by defining --animate-* theme variables inside the @theme directive.
This is useful for defining an animation you'll reuse many times.

CSS
1
2
3
4
5
6
7
8
9
10
11
@theme { --animate-my-animation: my-keyframes 1s infinite alternate; @keyframes my-keyframes { 0% { transform: translateX(0); } 100% { transform: translateX(100px); } } }
HTML
1
<div className="animate-my-animation"></div>
CSS property Utility class Style or value (Variable) [Arbitrary]
transition-property transition
transition-all
transition-colors
transition-opacity
transition-shadow
transition-transform
transition-none
colors + opacity + shadow + transform + filters
all
O O
transition-behavior transition-normal
transition-discrete
normal
allow-discrete
transition-duration duration-NUMBER
duration-initial
NUMBERms
initial
O O
transition-timing-function ease-linear
ease-in
ease-out
ease-in-out
ease-initial
linear
cubic-bezier(0.4, 0, 1, 1)
cubic-bezier(0, 0, 0.2, 1)
cubic-bezier(0.4, 0, 0.2, 1)
initial
O O
transition-delay delay-NUMBER NUMBERms O O
animation animate-spin
animate-ping
animate-pulse
animate-bounce
animate-none
spin 1s linear infinite
ping 1s cubic-bezier(0, 0, 0.2, 1) infinite
pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite
bounce 1s infinite
none
O O

# Transforms

CSS property Utility class Style or value (Variable) [Arbitrary]
backface-visibility backface-hidden
backface-visible
hidden
visible
perspective perspective-dramatic
perspective-near
perspective-normal
perspective-midrange
perspective-distant
perspective-none
100px
300px
500px
800px
1200px
none
O O
perspective-origin perspective-origin-center
perspective-origin-top
perspective-origin-top-right
perspective-origin-right
perspective-origin-bottom-right
perspective-origin-bottom
perspective-origin-bottom-left
perspective-origin-left
perspective-origin-top-left
center
top
top right
right
bottom right
bottom
bottom left
left
top left
O O
rotate rotate-none
rotate-NUMBER
rotate-x-NUMBER
rotate-y-NUMBER
rotate-z-NUMBER
none
NUMBERdeg
O O
scale scale-none
scale-NUMBER
scale-x-NUMBER
scale-y-NUMBER
scale-z-NUMBER
scale-3d
none
NUMBER% NUMBER%
NUMBER% 1
1 NUMBER%
1 1 NUMBER%
1 1 1
O O
skew skew-NUMBER
skew-x-NUMBER
skew-y-NUMBER
skewX(NUMBERdeg) skewY(NUMBERdeg)
skewX(NUMBERdeg)
skewY(NUMBERdeg)
O O
transform transform-none
transform-gpu
transform-cpu
none
translateZ(0)
O O
transform-origin origin-center
origin-top
origin-top-right
origin-right
origin-bottom-right
origin-bottom
origin-bottom-left
origin-left
origin-top-left
center
top
top right
right
bottom right
bottom
bottom left
left
top left
O O
transform-style transform-3d
transform-flat
preserve-3d
flat
translate translate-*
translate-x-*
translate-y-*
translate-z-*
translate-none

*-NUMBER
*-FRACTION
*-full
*-px






0.25rem * NUMBER
100% * FRACTION
100% 100%
1px 1px
O O

# Interactivity

CSS property Utility class Style or value (Variable) [Arbitrary]
accent-color accent-COLOR O O
appearance appearance-none
appearance-auto
none
auto
caret-color caret-COLOR O O
color-scheme scheme-normal
scheme-dark
scheme-light
scheme-light-dark
scheme-only-dark
scheme-only-light
normal
dark
light
light dark
only dark
only light
cursor cursor-auto
cursor-default
cursor-pointer
cursor-wait
...
auto
default
pointer
wait
...
O O
field-sizing field-sizing-fixed
field-sizing-content
fixed
content
pointer-events pointer-events-auto
pointer-events-none
auto
none
resize resize-none
resize
resize-x
resize-y
none
both
horizontal
vertical
scroll-behavior scroll-auto
scroll-smooth
auto
smooth
scroll-margin scroll-m-*
scroll-mx-*
scroll-my-*
scroll-ms-*
scroll-me-*
scroll-mt-*
scroll-mr-*
scroll-mb-*
scroll-ml-*

*-NUMBER










0.25rem * NUMBER
O O
scroll-padding scroll-p-*
scroll-px-*
scroll-py-*
scroll-ps-*
scroll-pe-*
scroll-pt-*
scroll-pr-*
scroll-pb-*
scroll-pl-*

*-NUMBER










0.25rem * NUMBER
O O
scroll-snap-align snap-start
snap-end
snap-center
snap-align-none
start
end
center
none
scroll-snap-stop snap-normal
snap-always
normal
always
scroll-snap-type snap-none
snap-x
snap-y
snap-both
snap-mandatory
snap-proximity
none
x proximity
y proximity
both proximity
mandatory
proximity
touch-action touch-auto
touch-none
touch-pan-x
touch-pan-left
touch-pan-right
touch-pan-y
touch-pan-up
touch-pan-down
touch-pinch-zoom
touch-manipulation
auto
none
pan-x
pan-left
pan-right
pan-y
pan-up
pan-down
pinch-zoom
manipulation
user-select select-none
select-text
select-all
select-auto
none
text
all
auto
will-change will-change-auto
will-change-scroll
will-change-contents
will-change-transform
auto
scroll-position
contents
transform
O O

# SVG

CSS property Utility class Style or value (Variable) [Arbitrary]
fill fill-none
fill-COLOR
none O O
stroke stroke-none
stroke-COLOR
none O O
stroke-width stroke-NUMBER O O

# Accessibility

CSS property Utility class Style or value (Variable) [Arbitrary]
forced-color-adjust forced-color-adjust-auto
forced-color-adjust-none
auto
none

# Variants

Every Tailwind CSS utility class can be applied conditionally through variants.
A typical example is the hover: variant, which applies styles when you hover over an element.
In the following example, the blue background is applied only on hover.

HTML
1
<div className="size-25 bg-red-500 hover:bg-blue-500"></div>

You can also nest variants.
In the following example, the first <div> contains a <span>, so the blue background is applied on hover, but the second <div> has no <span>, so the blue background is not applied even when you hover over it.

HTML
1
2
3
4
<div className="size-25 bg-red-500 hover:has-[span]:bg-blue-500"> <span>1</span> </div> <div className="size-25 bg-red-500 hover:has-[span]:bg-blue-500">2</div>

# Understanding the main variants

# Dark mode

Many operating systems, such as Windows and macOS, let you choose light or dark mode, and the choice is automatically applied to the browser as well.
You can detect dark mode with the prefers-color-scheme feature of the CSS @media rule.

CSS
1
2
3
4
5
6
7
@media (prefers-color-scheme: dark) { html { color-scheme: dark; background-color: oklch(0.13 0.028 261.692); /* bg-gray-900 */ color: #fff; } }
Example of applying styles in dark mode (not Tailwind CSS code)

With Tailwind CSS, you only need to specify which styles to use in dark mode with the dark: variant.

HTML
1
<input className="border-1 border-gray-300 dark:border-gray-600" />

If you want to control dark mode manually instead of automatically, you can use the @custom-variant directive as follows.
The dark: variant then works when an element has the dark class itself or is a descendant of such an element.

/src/index.css
CSS
1
2
3
@import "tailwindcss"; @custom-variant dark (&:where(.dark, .dark *));

Now add the dark class manually.
To control the whole document, you usually add it to <html>.

/index.html
HTML
1
2
3
4
<!doctype html> <html lang="ko" class="dark"> <!-- ... --> </html>

Adding the dark class doesn't automatically apply dark mode styles.
If there are styles you want, you have to write them yourself.

CSS
1
2
3
4
5
html.dark { color-scheme: dark; background-color: oklch(0.13 0.028 261.692); /* bg-gray-900 */ color: #fff; }

# Media queries

Tailwind CSS provides five breakpoints from sm to 2xl, and besides the usual mobile-first breakpoints, it also provides the opposite, a desktop-first system.
For desktop-first development, you use the max-* variants.

Variant CSS Width (px)
sm @media (width >= 40rem) 640px
md @media (width >= 48rem) 768px
lg @media (width >= 64rem) 1024px
xl @media (width >= 80rem) 1280px
2xl @media (width >= 96rem) 1536px
max-sm @media (width < 40rem) 640px
max-md @media (width < 48rem) 768px
max-lg @media (width < 64rem) 1024px
max-xl @media (width < 80rem) 1280px
max-2xl @media (width < 96rem) 1536px
max-[...] @media (width < ...)
min-[...] @media (width >= ...)

Let's look at a simple example to make this clear.
In the following example, the element's background color is red when the viewport width is 767px or less, and blue when it is 768px or more (md:).

HTML
1
<div className="size-25 bg-red-500 md:bg-blue-500"></div>

In the following example, the element's background color is blue when the viewport width is 767px or less (max-md:), and red when it is 768px or more.

HTML
1
<div className="size-25 bg-red-500 max-md:bg-blue-500"></div>

You can also nest variants to specify a breakpoint range.
In the following example, the element's background color is red by default, and blue when the viewport width is 768px or more and 1279px or less.

HTML
1
<div className="size-25 bg-red-500 md:max-xl:bg-blue-500"></div>

If you want to define your own breakpoints, change the default --breakpoint-* theme variables in the Tailwind CSS @theme directive.
The following example uses the breakpoints from Bootstrap.

CSS
1
2
3
4
5
6
7
@theme { --breakpoint-sm: 576px; --breakpoint-md: 768px; --breakpoint-lg: 992px; --breakpoint-xl: 1200px; --breakpoint-2xl: 1400px; }

# Container queries

Container queries follow a pattern similar to the media queries we just looked at.
They provide 13 breakpoints from 3xs to 7xl, you can use the max-* variants, and you can also specify ranges.
However, unlike media queries, which are based on the viewport, container queries require you to mark the reference ancestor element with the @container class and put an @ in front of the variant you use.

In the following example, the background color of the third <div> is blue when the first <div>, which has the @container class, is 3xl (768px) wide or wider, and red when it is 767px wide or narrower.

HTML
1
2
3
4
5
<div className="@container max-w-[1000px] border-2"> <div> <div className="size-25 bg-red-500 @3xl:bg-blue-500"></div> </div> </div>

If the default container query breakpoints such as 3xs don't fit a particular situation, you can also use an arbitrary value like @min-[768px].
The following example produces the same result as the previous one.

HTML
1
2
3
4
5
<div className="@container max-w-[1000px] border-2"> <div> <div className="size-25 bg-red-500 @min-[768px]:bg-blue-500"></div> </div> </div>

# Styling descendant (child) elements

In most cases, you write utility classes directly on the element itself, but sometimes you may need to write styles on an ancestor element.
In that case, you can style child elements with the *: variant and descendant elements with the **: variant.
In the following example, the classes on <ul> add padding to each <li> and a text color to each <a> that has an href attribute.
So <a>Apple</a> doesn't get the text color.

HTML
1
2
3
4
5
6
7
8
9
10
11
<ul className="*:p-2 **:[a[href]]:text-red-400"> <li> <a>Apple</a> </li> <li> <a href="#">Banana</a> </li> <li> <a href="#">Cherry</a> </li> </ul>

If you want to style a descendant element based on the state of an ancestor element, you can use the group-*: variant.
First, add the group class to the ancestor element to mark it as the target.
Then, on the descendant element, use the group-*: variant to write the utility classes that apply based on the ancestor's state (a selector, utility class, variant, and so on).

Each <p> in the following example is shown only when you hover over its <li> or when the <li> has the active class.
So Answer B is shown right away, and Answer A is shown on hover.

HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
<section> <h2>Frequently asked questions</h2> <ul> <li className="group"> <h4>Question A</h4> <p className="invisible group-[.active]:visible group-hover:visible">Answer A</p> </li> <li className="group active"> <h4>Question B</h4> <p className="invisible group-[.active]:visible group-hover:visible">Answer B</p> </li> </ul> </section>

# Styling sibling elements

You can also apply styles based on the state of a sibling element.
First, add the peer class to the sibling element to mark it as the target.
Then use the peer-*: variant to write the utility classes that apply based on the sibling's state.

In the following example, <p> is shown on the screen depending on whether the email in <input> is valid.
Note that the invalid: variant is the same as the CSS :invalid pseudo-class.

HTML
1
2
3
4
5
6
7
8
9
<label> <input type="email" className="peer" /> <p className="invisible text-red-500 peer-invalid:visible"> Please enter a valid email address. </p> </label>

# Variants and CSS table

Now that you understand what variants are, take a quick look at the following table to see which variants exist and which CSS each one matches.
You don't need to remember all of them!
When you need details, select a variant name to go to the matching Tailwind CSS documentation.

Variant CSS
hover @media (hover: hover) { &:hover }
focus &:focus
focus-within &:focus-within
focus-visible &:focus-visible
active &:active
visited &:visited
target &:target
* :is(& > *)
** :is(& *)
has-[...] &:has(...)
group-[...] &:is(:where(.group)... *)
peer-[...] &:is(:where(.peer)... ~ *)
in-[...] :where(...) &
not-[...] &:not(...)
inert &:is([inert], [inert] *)
first &:first-child
last &:last-child
only &:only-child
odd &:nth-child(odd)
even &:nth-child(even)
first-of-type &:first-of-type
last-of-type &:last-of-type
only-of-type &:only-of-type
nth-[...] &:nth-child(...)
nth-last-[...] &:nth-last-child(...)
nth-of-type-[...] &:nth-of-type(...)
nth-last-of-type-[...] &:nth-last-of-type(...)
empty &:empty
disabled &:disabled
enabled &:enabled
checked &:checked
indeterminate &:indeterminate
default &:default
optional &:optional
required &:required
valid &:valid
invalid &:invalid
in-range &:in-range
out-of-range &:out-of-range
placeholder-shown &:placeholder-shown
autofill &:autofill
read-only &:read-only
before &::before
after &::after
first-letter &::first-letter
first-line &::first-line
marker &::marker, & *::marker
selection &::selection
file &::file-selector-button
backdrop &::backdrop
placeholder &::placeholder
sm @media (width >= 40rem)
md @media (width >= 48rem)
lg @media (width >= 64rem)
xl @media (width >= 80rem)
2xl @media (width >= 96rem)
min-[...] @media (width >= ...)
max-sm @media (width < 40rem)
max-md @media (width < 48rem)
max-lg @media (width < 64rem)
max-xl @media (width < 80rem)
max-2xl @media (width < 96rem)
max-[...] @media (width < ...)
@3xs @container (width >= 16rem)
@2xs @container (width >= 18rem)
@xs @container (width >= 20rem)
@sm @container (width >= 24rem)
@md @container (width >= 28rem)
@lg @container (width >= 32rem)
@xl @container (width >= 36rem)
@2xl @container (width >= 42rem)
@3xl @container (width >= 48rem)
@4xl @container (width >= 56rem)
@5xl @container (width >= 64rem)
@6xl @container (width >= 72rem)
@7xl @container (width >= 80rem)
@min-[...] @container (width >= ...)
@max-3xs @container (width < 16rem)
@max-2xs @container (width < 18rem)
@max-xs @container (width < 20rem)
@max-sm @container (width < 24rem)
@max-md @container (width < 28rem)
@max-lg @container (width < 32rem)
@max-xl @container (width < 36rem)
@max-2xl @container (width < 42rem)
@max-3xl @container (width < 48rem)
@max-4xl @container (width < 56rem)
@max-5xl @container (width < 64rem)
@max-6xl @container (width < 72rem)
@max-7xl @container (width < 80rem)
@max-[...] @container (width < ...)
dark @media (prefers-color-scheme: dark)
portrait @media (orientation: portrait)
landscape @media (orientation: landscape)
motion-safe @media (prefers-reduced-motion: no-preference)
motion-reduce @media (prefers-reduced-motion: reduce)
contrast-more @media (prefers-contrast: more)
contrast-less @media (prefers-contrast: less)
print @media print
supports-[...] @supports (...)
aria-busy &[aria-busy="true"]
aria-checked &[aria-checked="true"]
aria-disabled &[aria-disabled="true"]
aria-expanded &[aria-expanded="true"]
aria-hidden &[aria-hidden="true"]
aria-pressed &[aria-pressed="true"]
aria-readonly &[aria-readonly="true"]
aria-required &[aria-required="true"]
aria-selected &[aria-selected="true"]
aria-[...] &[aria-...]
data-[...] &[data-...]
rtl &:where(:dir(rtl), [dir="rtl"], [dir="rtl"] *)
ltr &:where(:dir(ltr), [dir="ltr"], [dir="ltr"] *)
open &:is([open], :popover-open, :open)
forced-colors @media (forced-colors: active)
starting @starting-style

# Directives

Directives are special Tailwind CSS rules that you use in CSS, not in HTML.
Let's look at the main directives you'll use often.

# @theme

You can redefine the default theme variables that Tailwind CSS uses, such as fonts, colors, and breakpoints, or create new theme variables.

CSS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@theme { /* Modify default theme variables */ --font-sans: 'Pretendard', sans-serif; /* ui-sans-serif, system-ui, ... */ --color-white: #e9e9e9; /* #fff */ --color-black: #1a1a1a; /* #000 */ --spacing: 10px; /* 0.25rem */ --radius-md: 10px; /* 0.375rem */ --breakpoint-md: 800px; /* 768px */ --breakpoint-lg: 1100px; /* 1024px */ --breakpoint-xl: 1400px; /* 1280px */ /* Create custom theme variables */ --color-my-highlight: #e96900; }
HTML
1
<div className="bg-(--color-my-highlight)"</div>

The namespaces of theme variables you can redefine are as follows.

Theme variable Utility class
--color-* Color-related, like bg-red-500 and text-sky-300
--font-* Font-related, like font-sans
--text-* Text size, like text-xl
--font-weight-* Font weight, like font-bold
--tracking-* Letter spacing, like tracking-wide
--leading-* Line height, like leading-tight
--breakpoint-* Media query breakpoints, like sm:*
--container-* Container query breakpoints, like @sm:* and max-w-md
--spacing-* Spacing-related, like px-4 and max-h-16
--radius-* Corner rounding, like rounded-sm
--shadow-* Shadows, like shadow-md
--inset-shadow-* Inset shadows, like inset-shadow-xs
--drop-shadow-* Filter shadows, like drop-shadow-md
--blur-* Blur effects, like blur-md
--perspective-* Perspective effects, like perspective-near
--aspect-* Aspect ratios, like aspect-video
--ease-* Timing functions, like ease-out
--animate-* Animations, like animate-spin

# @utility

You can define your own utility classes with the @utility directive.

CSS
1
2
3
4
5
@utility heropy-box-300 { width: 300px; height: 300px; border-radius: 10px; }
HTML
1
<div className="heropy-box-300"></div>

You can also define more complex utility classes.
When you pass a plain number without a unit, you can use the number or integer type in the --value() function.
There are a few more types you can use this way, such as percentage and ratio, but they are hard to recommend because they aren't intuitive, behave differently depending on the property, and don't handle every value.

CSS
1
2
3
4
5
@utility heropy-box-* { width: --value(number)px; height: --value(number)px; border-radius: 10px; }
HTML
1
2
3
<div className="heropy-box-100"></div> <div className="heropy-box-300"></div> <div className="heropy-box-500"></div>

So if you're creating a utility class that accepts many kinds of values, I recommend the arbitrary value approach with [].
With a wildcard character, you can assign any value to the --value() function.

CSS
1
2
3
4
5
@utility heropy-box-* { width: --value([*]); height: --value([*]); border-radius: 10px; }
HTML
1
2
3
<div className="heropy-box-[300px]"></div> <div className="heropy-box-[50%]"></div> <div className="heropy-box-[10dvw]"></div>

For colors, specify the --color-* theme variable.
Then you can use the many colors that Tailwind CSS provides.

CSS
1
2
3
4
5
6
@utility heropy-box-color-* { width: 100px; height: 100px; border-radius: 10px; background-color: --value(--color-*); }
HTML
1
2
<div className="heropy-box-color-red-500"></div> <div className="heropy-box-color-blue-400"></div>

Along with the --value() function, you can also use the --modifier() function.
This lets you handle an extra value written with /, like text-sm/6 or bg-red-500/70.

CSS
1
2
3
4
5
6
7
@utility heropy-box-color-* { width: 100px; height: 100px; border-radius: 10px; background-color: --value(--color-*); opacity: --modifier(number); }
HTML
1
2
<div className="heropy-box-color-red-500"></div> <div className="heropy-box-color-blue-400/0.5"></div>

# @apply

The @apply directive lets you use utility classes in CSS.

The following example defines a new utility class by writing CSS properties and values directly, without @apply.

CSS
1
2
3
4
5
@utility heropy-flex-center { display: flex; align-items: center; justify-content: center; }

The following example produces the same result as the previous one, but uses utility classes instead of CSS properties.
If you're familiar with utility classes, this may be more convenient.

CSS
1
2
3
@utility heropy-flex-center { @apply flex items-center justify-center; }
HTML
1
<div className="heropy-flex-center"></div>

# @variant

You can use the @variant directive to add variants to a utility class.
The following example defines the hover style for elements that have a heropy-box-* class.

CSS
1
2
3
4
5
6
7
8
9
@utility heropy-box-* { width: --value([*]); height: --value([*]); border-radius: 10px; transition: transform 0.3s; @variant hover { transform: scale(1.1); } }

You can also rewrite the previous example with the @apply directive as follows.

CSS
1
2
3
4
5
6
@utility heropy-box-* { @apply size-[--value([*])] rounded-[10px] transition duration-300; @variant hover { @apply scale-[1.1]; } }
HTML
1
<div className="heropy-box-[300px]"></div>

You can also nest the @variant directive multiple times.
In the following example, the background color (bg-blue-100) is applied only when you hover over a focused element.

CSS
1
2
3
4
5
6
7
8
9
@utility heropy-input { @apply rounded-[10px] transition duration-300; @variant focus { @apply border-blue-400; @variant hover { @apply bg-blue-100; } } }
HTML
1
<input className="heropy-input" />

# @custom-variant

You can also create your own variants with the @custom-variant directive.
Here, I created a state-open: variant that checks whether the data-state HTML attribute of an ancestor element is open.

CSS
1
@custom-variant state-open '[data-state="open"] &';

When the data-state attribute of the first <div> is open, the background color of the second <div> becomes blue.

TSX
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { useState } from 'react' export default function () { const [isActive, setIsActive] = useState(false) return ( <> <div data-state={isActive ? 'open' : 'close'} onClick={() => setIsActive(!isActive)}> <div className="state-open:bg-blue-500"></div> </div> </> ) }

# More usage patterns

# Using a prefix

If utility classes conflict with classes that already exist in your project, or if you want to mark utility classes clearly, you can use the prefix option as follows.

CSS
1
@import "tailwindcss" prefix(tw);

Now you must always add the tw: prefix to utility classes.

HTML
1
<div className="tw:h-25 tw:w-25 tw:bg-red-500"></div>

# Priority

If you need to force a utility class to apply in a special situation, add ! to the end of its name to apply !important.
In the following example, the h-50 class takes priority.

HTML
1
<div className="h-50! h-100"></div>

# Applying dynamic styles

Tailwind CSS scans your project for utility classes and generates the CSS you need based on the classes you actually use.
So you shouldn't build classes dynamically.

For example, say you want to make an element whose width grows every time the count state increases. If you write the utility class so that it's built dynamically, like w-[${width}px] in the following example, the style won't be applied.

TSX
1
2
3
4
5
6
7
8
9
10
11
12
13
import { useState } from 'react' export default function () { const [width, setWidth] = useState(100) return ( <div className={`w-[${width}px] rounded-md bg-blue-500 p-3 font-bold text-white transition duration-300`} onClick={() => setWidth(width + 20)}> {width}px </div> ) }

So you need a separate style binding as follows.

TSX
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { useState } from 'react' export default function () { const [width, setWidth] = useState(100) return ( <div className={`rounded-md bg-blue-500 p-3 font-bold text-white transition duration-300`} style={{ width: `${width}px` }} onClick={() => setWidth(width + 20)}> {width}px </div> ) }
This works.

Instead of binding an inline style for every piece of dynamic data, you can also predefine utility classes and apply them when needed, as in the following example.

TSX
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
export default function Button({ color, children }: { color: 'primary' | 'secondary' | 'error' children: React.ReactNode }) { const colors = { primary: 'bg-blue-500 hover:bg-blue-600 text-white', secondary: 'bg-gray-400 hover:bg-gray-500 text-black', error: 'bg-red-500 hover:bg-red-600 text-white' } return ( <button className={`${colors[color]} cursor-pointer rounded-md px-3 py-2 transition duration-200`}> {children} </button> ) }

To make styles more reusable and easier to maintain, you can also organize utility classes with the CVA (Class Variance Authority) library.

BASH
1
npm i class-variance-authority
TSX
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import { cva, type VariantProps } from 'class-variance-authority' const buttonStyles = cva( 'cursor-pointer rounded-md px-3 py-2 transition duration-200', { variants: { color: { primary: 'bg-blue-500 hover:bg-blue-600 text-white', secondary: 'bg-gray-400 hover:bg-gray-500 text-black', error: 'bg-red-500 hover:bg-red-600 text-white' } }, defaultVariants: { color: 'primary' } } ) export default function Button({ color, children }: { color?: VariantProps<typeof buttonStyles>['color'] children: React.ReactNode }) { return <button className={buttonStyles({ color })}>{children}</button> }

The utility classes you write in the cva function are plain JS strings, so autocomplete and preview don't work.
If you want to use the features of the Tailwind CSS IntelliSense extension in the cva or cx function, you can configure it as follows.

/.vscode/settings.json
JSON
1
2
3
4
5
6
{ "tailwindCSS.experimental.classRegex": [ ["cva\\(((?:[^()]|\\([^()]*\\))*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"], ["cx\\(((?:[^()]|\\([^()]*\\))*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"] ] }

Using color swatches, autocomplete, preview, and more for utility classes in the cva function

# Merging styles

When you use Tailwind CSS and conflicting utility classes are applied to a single element, it's hard to tell which class will take priority, and the style you want may not be applied.
In the following example, the isActive state is true, but the styles of the w-25 and bg-blue-500 classes aren't applied to the element.
That's because the w-75 and bg-red-500 classes take priority.

Note that in this situation, priority is decided by declaration order, not specificity, and that order can change at any time with the Tailwind CSS build, so trying to learn the pattern is pointless.
So how should you solve this?

TSX
1
2
3
4
5
6
7
8
9
10
import { useState } from 'react' export default function App() { const [isActive] = useState(true) return ( <> <div className={`h-25 w-75 bg-red-500 ${isActive && 'w-25 bg-blue-500'}`}></div> </> ) }

This is where you can use the tailwind-merge library to solve the problem.
First, install the library.

BASH
1
npm i tailwind-merge

The library's twMerge() function makes the class applied later take priority.
You can list all the classes in a single argument as in the first <div>, but code formatting may change the order of the classes.
So if you split them into multiple arguments in order, as in the second <div>, you can set the priority more clearly and safely.
Now the styles of the w-25 and bg-blue-500 classes are applied according to the isActive state.

TS
1
2
// The higher the number, the higher the priority! twMerge('priority1', 'priority2', 'priority3')
TSX
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { useState } from 'react' import { twMerge } from 'tailwind-merge' export default function App() { const [isActive] = useState(true) return ( <> <div className={twMerge( `h-25 w-75 bg-red-500 ${isActive && 'w-25 bg-blue-500'}` )}></div> <div className={twMerge( `h-25 w-75 bg-red-500`, isActive && 'w-25 bg-blue-500' )}></div> </> ) }

Sometimes you may just need to combine utility classes regardless of priority.
Since you're already using tailwind-merge, try the twJoin() function instead of clsx.

TSX
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { useState } from 'react' import { twJoin } from 'tailwind-merge' export default function App() { const [isActive] = useState(true) const colorRed = 'bg-red-500' return ( <> <div className={twJoin( ['h-25 w-25'], colorRed, isActive && 'bg-blue-500', ['w-75'] )}></div> {/* ⬇️ They are combined like this. */} <div className="h-25 w-25 w-75 bg-blue-500 bg-red-500"></div> </> ) }