Core Concepts
Using utilities to style elements on hover, focus, and more.
Every utility class in Tailwind can be applied conditionally by adding a modifier to the beginning of the class name that describes the condition you want to target.
For example, to apply the bg-sky-700
class on hover, use the hover:bg-sky-700
class:
Hover over this button to see the background color change
<button class="bg-sky-500 hover:bg-sky-700 ...">
Save changes
</button>
When writing CSS the traditional way, a single class name would do different things based on the current state.
Traditionally the same class name applies different styles on hover
.btn-primary {
background-color: #0ea5e9;
}
.btn-primary:hover {
background-color: #0369a1;
}
In Tailwind, rather than adding the styles for a hover state to an existing class, you add another class to the element that only does something on hover.
In Tailwind, separate classes are used for the default state and the hover state
.bg-sky-500 {
background-color: #0ea5e9;
}
.hover\:bg-sky-700:hover {
background-color: #0369a1;
}
Notice how hover:bg-sky-700
only defines styles for the :hover
state? It does nothing by default, but as soon as you hover over an element with that class, the background color will change to sky-700
.
This is what we mean when we say a utility class can be applied conditionally — by using modifiers you can control exactly how your design behaves in different states, without ever leaving your HTML.
Tailwind includes modifiers for just about everything you’ll ever need, including:
:hover
, :focus
, :first-child
, and :required
::before
, ::after
, ::placeholder
, and ::selection
prefers-reduced-motion
[dir="rtl"]
and [open]
These modifiers can even be stacked to target more specific situations, for example changing the background color in dark mode, at the medium breakpoint, on hover:
<button class="dark:md:hover:bg-fuchsia-600 ...">
Save changes
</button>
In this guide you’ll learn about every modifier available in the framework, how to use them with your own custom classes, and even how to create your own.
Style elements on hover, focus, and active using the hover
, focus
, and active
modifiers:
Try interacting with this button to see the hover, focus, and active states
<button class="bg-violet-500 hover:bg-violet-600 active:bg-violet-700 focus:outline-none focus:ring focus:ring-violet-300 ...">
Save changes
</button>
Tailwind also includes modifiers for other interactive states like :visited
, :focus-within
, :focus-visible
, and more.
See the pseudo-class reference for a complete list of available pseudo-class modifiers.
Style an element when it is the first-child or last-child using the first
and last
modifiers:
Kristen Ramos
kristen.ramos@example.com
Floyd Miles
floyd.miles@example.com
Courtney Henry
courtney.henry@example.com
Ted Fox
ted.fox@example.com
<ul role="list" class="p-6 divide-y divide-slate-200">
{#each people as person}
<!-- Remove top/bottom padding when first/last child -->
<li class="flex py-4 first:pt-0 last:pb-0">
<img class="h-10 w-10 rounded-full" src="{person.imageUrl}" alt="" />
<div class="ml-3 overflow-hidden">
<p class="text-sm font-medium text-slate-900">{person.name}</p>
<p class="text-sm text-slate-500 truncate">{person.email}</p>
</div>
</li>
{/each}
</ul>
You can also style an element when it’s an odd or even child using the odd
and even
modifiers:
Name | Title | |
---|---|---|
Jane Cooper | Regional Paradigm Technician | jane.cooper@example.com |
Cody Fisher | Product Directives Officer | cody.fisher@example.com |
Leonard Krasner | Senior Designer | leonard.krasner@example.com |
Emily Selman | VP, Hardware Engineering | emily.selman@example.com |
Anna Roberts | Chief Strategy Officer | anna.roberts@example.com |
<table>
<!-- ... -->
<tbody>
{#each people as person}
<!-- Use a white background for odd rows, and slate-50 for even rows -->
<tr class="odd:bg-white even:bg-slate-50">
<td>{person.name}</td>
<td>{person.title}</td>
<td>{person.email}</td>
</tr>
{/each}
</tbody>
</table>
Tailwind also includes modifiers for other structural pseudo-classes like :only-child
, :first-of-type
, :empty
, and more.
See the pseudo-class reference for a complete list of available pseudo-class modifiers.
Style form elements in different states using modifiers like required
, invalid
, and disabled
:
Try making the email address valid to see the styles change
<form>
<label class="block">
<span class="block text-sm font-medium text-slate-700">Username</span>
<!-- Using form state modifiers, the classes can be identical for every input -->
<input type="text" value="tbone" disabled class="mt-1 block w-full px-3 py-2 bg-white border border-slate-300 rounded-md text-sm shadow-sm placeholder-slate-400
focus:outline-none focus:border-sky-500 focus:ring-1 focus:ring-sky-500
disabled:bg-slate-50 disabled:text-slate-500 disabled:border-slate-200 disabled:shadow-none
invalid:border-pink-500 invalid:text-pink-600
focus:invalid:border-pink-500 focus:invalid:ring-pink-500
"/>
</label>
<!-- ... -->
</form>
Using modifiers for this sort of thing can reduce the amount of conditional logic in your templates, letting you use the same set of classes regardless of what state an input is in and letting the browser apply the right styles for you.
Tailwind also includes modifiers for other form states like :read-only
, :indeterminate
, :checked
, and more.
See the pseudo-class reference for a complete list of available pseudo-class modifiers.
When you need to style an element based on the state of some parent element, mark the parent with the group
class, and use group-*
modifiers like group-hover
to style the target element:
Hover over the card to see both text elements change color
<a href="#" class="group block max-w-xs mx-auto rounded-lg p-6 bg-white ring-1 ring-slate-900/5 shadow-lg space-y-3 hover:bg-sky-500 hover:ring-sky-500">
<div class="flex items-center space-x-3">
<svg class="h-6 w-6 stroke-sky-500 group-hover:stroke-white" fill="none" viewBox="0 0 24 24"><!-- ... --></svg>
<h3 class="text-slate-900 group-hover:text-white text-sm font-semibold">New project</h3>
</div>
<p class="text-slate-500 group-hover:text-white text-sm">Create a new project from a variety of starting templates.</p>
</a>
This pattern works with every pseudo-class modifier, for example group-focus
, group-active
, or even group-odd
.
When nesting groups, you can style something based on the state of a specific parent group by giving that parent a unique group name using a group/{name}
class, and including that name in modifiers using classes like group-hover/{name}
:
<ul role="list">
{#each people as person}
<li class="group/item hover:bg-slate-100 ...">
<img src="{person.imageUrl}" alt="" />
<div>
<a href="{person.url}">{person.name}</a>
<p>{person.title}</p>
</div>
<a class="group/edit invisible hover:bg-slate-200 group-hover/item:visible ..." href="tel:{person.phone}">
<span class="group-hover/edit:text-gray-700 ...">Call</span>
<svg class="group-hover/edit:translate-x-0.5 group-hover/edit:text-slate-500 ...">
<!-- ... -->
</svg>
</a>
</li>
{/each}
</ul>
Groups can be named however you like and don’t need to be configured in any way — just name your groups directly in your markup and Tailwind will automatically generate the necessary CSS.
You can create one-off group-*
modifiers on the fly by providing your own selector as an arbitrary value between square brackets:
<div class="group is-published">
<div class="hidden group-[.is-published]:block">
Published
</div>
</div>
.group.is-published .group-\[\.is-published\]\:block {
display: block;
}
For more control, you can use the &
character to mark where .group
should end up in the final selector relative to the selector you are passing in:
<div class="group">
<div class="group-[:nth-of-type(3)_&]:block">
<!-- ... -->
</div>
</div>
:nth-of-type(3) .group .group-\[\:nth-of-type\(3\)_\&\]\:block {
display: block;
}
When you need to style an element based on the state of a sibling element, mark the sibling with the peer
class, and use peer-*
modifiers like peer-invalid
to style the target element:
Try making the email address valid to see the warning disappear
<form>
<label class="block">
<span class="block text-sm font-medium text-slate-700">Email</span>
<input type="email" class="peer ..."/>
<p class="mt-2 invisible peer-invalid:visible text-pink-600 text-sm">
Please provide a valid email address.
</p>
</label>
</form>
This makes it possible to do all sorts of neat tricks, like floating labels for example without any JS.
This pattern works with every pseudo-class modifier, for example peer-focus
, peer-required
, and peer-disabled
.
It’s important to note that the peer
marker can only be used on previous siblings because of how the general sibling combinator works in CSS.
Won't work, only previous siblings can be marked as peers
<label>
<span class="peer-invalid:text-red-500 ...">Email</span>
<input type="email" class="peer ..."/>
</label>
When using multiple peers, you can style something on the state of a specific peer by giving that peer a unique name using a peer/{name}
class, and including that name in modifiers using classes like peer-checked/{name}
:
<fieldset>
<legend>Published status</legend>
<input id="draft" class="peer/draft" type="radio" name="status" checked />
<label for="draft" class="peer-checked/draft:text-sky-500">Draft</label>
<input id="published" class="peer/published" type="radio" name="status" />
<label for="published" class="peer-checked/published:text-sky-500">Published</label>
<div class="hidden peer-checked/draft:block">Drafts are only visible to administrators.</div>
<div class="hidden peer-checked/published:block">Your post will be publicly visible on your site.</div>
</fieldset>
Peers can be named however you like and don’t need to be configured in any way — just name your peers directly in your markup and Tailwind will automatically generate the necessary CSS.
You can create one-off peer-*
modifiers on the fly by providing your own selector as an arbitrary value between square brackets:
<form>
<label for="email">Email:</label>
<input id="email" name="email" type="email" class="is-dirty peer" required />
<div class="peer-[.is-dirty]:peer-required:block hidden">This field is required.</div>
<!-- ... -->
</form>
.peer.is-dirty:required ~ .peer-\[\.is-dirty\]\:peer-required\:block {
display: block;
}
For more control, you can use the &
character to mark where .peer
should end up in the final selector relative to the selector you are passing in:
<div>
<input type="text" class="peer" />
<div class="hidden peer-[:nth-of-type(3)_&]:block">
<!-- ... -->
</div>
</div>
:nth-of-type(3) .peer ~ .peer-\[\:nth-of-type\(3\)_\&\]\:block {
display: block;
}
Style the ::before
and ::after
pseudo-elements using the before
and after
modifiers:
<label class="block">
<span class="after:content-['*'] after:ml-0.5 after:text-red-500 block text-sm font-medium text-slate-700">
Email
</span>
<input type="email" name="email" class="mt-1 px-3 py-2 bg-white border shadow-sm border-slate-300 placeholder-slate-400 focus:outline-none focus:border-sky-500 focus:ring-sky-500 block w-full rounded-md sm:text-sm focus:ring-1" placeholder="you@example.com" />
</label>
When using these modifiers, Tailwind will automatically add content: ''
by default so you don’t have to specify it unless you want a different value:
When you look annoyed all the time, people think that you're busy.
<blockquote class="text-2xl font-semibold italic text-center text-slate-900">
When you look
<span class="before:block before:absolute before:-inset-1 before:-skew-y-3 before:bg-pink-500 relative inline-block">
<span class="relative text-white">annoyed</span>
</span>
all the time, people think that you're busy.
</blockquote>
It’s worth noting that you don’t really need ::before
and ::after
pseudo-elements for most things in Tailwind projects — it’s usually simpler to just use a real HTML element.
For example, here’s the same design from above but using a <span>
instead of the ::before
pseudo-element, which is a little easier to read and is actually less code:
<blockquote class="text-2xl font-semibold italic text-center text-slate-900">
When you look
<span class="relative">
<span class="block absolute -inset-1 -skew-y-3 bg-pink-500" aria-hidden="true"></span>
<span class="relative text-white">annoyed</span>
</span>
all the time, people think that you're busy.
</blockquote>
Save before
and after
for situations where it’s important that the content of the pseudo-element is not actually in the DOM and can’t be selected by the user.
Note that if you’ve disabled our preflight base styles, the content property will not be set to an empty string by default, and you will need to include content-['']
any time you use the before
and after
modifiers.
If you've disabled preflight make sure to set the content manually
<div class="before:content-[''] before:block ...">
<!-- ... -->
</div>
Style the placeholder text of any input or textarea using the placeholder
modifier:
<label class="relative block">
<span class="sr-only">Search</span>
<span class="absolute inset-y-0 left-0 flex items-center pl-2">
<svg class="h-5 w-5 fill-slate-300" viewBox="0 0 20 20"><!-- ... --></svg>
</span>
<input class="placeholder:italic placeholder:text-slate-400 block bg-white w-full border border-slate-300 rounded-md py-2 pl-9 pr-3 shadow-sm focus:outline-none focus:border-sky-500 focus:ring-sky-500 focus:ring-1 sm:text-sm" placeholder="Search for anything..." type="text" name="search"/>
</label>
Style the button in file inputs using the file
modifier:
<form class="flex items-center space-x-6">
<div class="shrink-0">
<img class="h-16 w-16 object-cover rounded-full" src="https://images.unsplash.com/photo-1580489944761-15a19d654956?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1361&q=80" alt="Current profile photo" />
</div>
<label class="block">
<span class="sr-only">Choose profile photo</span>
<input type="file" class="block w-full text-sm text-slate-500
file:mr-4 file:py-2 file:px-4
file:rounded-full file:border-0
file:text-sm file:font-semibold
file:bg-violet-50 file:text-violet-700
hover:file:bg-violet-100
"/>
</label>
</form>
Note that Tailwind’s border reset is not applied to file input buttons. This means that to add a border to a file input button, you need to explicitly set the border-style using a class like file:border-solid
alongside any border-width utility:
<input type="file" class="file:border file:border-solid ..." />
Style the counters or bullets in lists using the marker
modifier:
<ul role="list" class="marker:text-sky-400 list-disc pl-5 space-y-3 text-slate-500"> <li>5 cups chopped Porcini mushrooms</li> <li>1/2 cup of olive oil</li> <li>3lb of celery</li> </ul>
<ul role="list" class="marker:text-sky-400 list-disc pl-5 space-y-3 text-slate-400"> <li>5 cups chopped Porcini mushrooms</li> <li>1/2 cup of olive oil</li> <li>3lb of celery</li> </ul>
We’ve designed the marker
modifier to be inheritable, so although you can use it directly on an <li>
element, you can also use it on a parent to avoid repeating yourself.
Style the active text selection using the selection
modifier:
Try selecting some of this text with your mouse
So I started to walk into the water. I won't lie to you boys, I was terrified. But I pressed on, and as I made my way past the breakers a strange calm came over me. I don't know if it was divine intervention or the kinship of all living things but I tell you Jerry at that moment, I was a marine biologist.
<div class="selection:bg-fuchsia-300 selection:text-fuchsia-900">
<p>
So I started to walk into the water. I won't lie to you boys, I was
terrified. But I pressed on, and as I made my way past the breakers
a strange calm came over me. I don't know if it was divine intervention
or the kinship of all living things but I tell you Jerry at that moment,
I <em>was</em> a marine biologist.
</p>
</div>
We’ve designed the selection
modifier to be inheritable, so you can add it anywhere in the tree and it will be applied to all descendant elements.
This makes it easy to set the selection color to match your brand across your entire site:
<html>
<head>
<!-- ... -->
</head>
<body class="selection:bg-pink-300">
<!-- ... -->
</body>
</html>
Style the first line in a block of content using the first-line
modifier, and the first letter using the first-letter
modifier:
Well, let me tell you something, funny boy. Y'know that little stamp, the one that says "New York Public Library"? Well that may not mean anything to you, but that means a lot to me. One whole hell of a lot.
Sure, go ahead, laugh if you want to. I've seen your type before: Flashy, making the scene, flaunting convention. Yeah, I know what you're thinking. What's this guy making such a big stink about old library books? Well, let me give you a hint, junior.
<p class="first-line:uppercase first-line:tracking-widest first-letter:text-7xl first-letter:font-bold first-letter:text-slate-900 first-letter:mr-3 first-letter:float-left "> Well, let me tell you something, funny boy. Y'know that little stamp, the one that says "New York Public Library"? Well that may not mean anything to you, but that means a lot to me. One whole hell of a lot. </p>
<p class="first-line:uppercase first-line:tracking-widest first-letter:text-7xl first-letter:font-bold first-letter:text-white first-letter:mr-3 first-letter:float-left "> Well, let me tell you something, funny boy. Y'know that little stamp, the one that says "New York Public Library"? Well that may not mean anything to you, but that means a lot to me. One whole hell of a lot. </p>
Style the backdrop of a native <dialog>
element using the backdrop
modifier:
<dialog class="backdrop:bg-gray-50">
<form method="dialog">
<!-- ... -->
</form>
</dialog>
If you’re using native <dialog>
elements in your project, you may also want to read about styling open/closed states using the open
modifier.
To style an element at a specific breakpoint, use responsive modifiers like md
and lg
.
For example, this will render a 3-column grid on mobile, a 4-column grid on medium-width screens, and a 6-column grid on large-width screens:
<div class="grid grid-cols-3 md:grid-cols-4 lg:grid-cols-6">
<!-- ... -->
</div>
Check out the Responsive Design documentation for an in-depth look at how these features work.
The prefers-color-scheme
media query tells you whether the user prefers a light theme or dark theme, and is usually configured at the operating system level.
Use utilities with no modifier to target light mode, and use the dark
modifier to provide overrides for dark mode:
Light mode
The Zero Gravity Pen can be used to write in any orientation, including upside-down. It even works in outer space.
Dark mode
The Zero Gravity Pen can be used to write in any orientation, including upside-down. It even works in outer space.
<div class="bg-white dark:bg-slate-900 rounded-lg px-6 py-8 ring-1 ring-slate-900/5 shadow-xl">
<div>
<span class="inline-flex items-center justify-center p-2 bg-indigo-500 rounded-md shadow-lg">
<svg class="h-6 w-6 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true"><!-- ... --></svg>
</span>
</div>
<h3 class="text-slate-900 dark:text-white mt-5 text-base font-medium tracking-tight">Writes Upside-Down</h3>
<p class="text-slate-500 dark:text-slate-400 mt-2 text-sm">
The Zero Gravity Pen can be used to write in any orientation, including upside-down. It even works in outer space.
</p>
</div>
Check out the Dark Mode documentation for an in-depth look at how this feature works.
The prefers-reduced-motion
media query tells you if the user has requested that you minimize non-essential motion.
Use the motion-reduce
modifier to conditionally add styles when the user has requested reduced motion:
Try emulating `prefers-reduced-motion: reduce` in your developer tools to hide the spinner
<button type="button" class="bg-indigo-500 ..." disabled>
<svg class="motion-reduce:hidden animate-spin ..." viewBox="0 0 24 24"><!-- ... --></svg>
Processing...
</button>
Tailwind also includes a motion-safe
modifier that only adds styles when the user has not requested reduced motion. This can be useful when using the motion-reduce
helper would mean having to “undo” a lot of styles:
<!-- Using `motion-reduce` can mean lots of "undoing" styles -->
<button class="hover:-translate-y-0.5 transition motion-reduce:hover:translate-y-0 motion-reduce:transition-none ...">
Save changes
</button>
<!-- Using `motion-safe` is less code in these situations -->
<button class="motion-safe:hover:-translate-x-0.5 motion-safe:transition ...">
Save changes
</button>
The prefers-contrast
media query tells you if the user has requested more or less contrast.
Use the contrast-more
modifier to conditionally add styles when the user has requested more contrast:
Try emulating `prefers-contrast: more` in your developer tools to see the changes
<form>
<label class="block">
<span class="block text-sm font-medium text-slate-700">Social Security Number</span>
<input class="border-slate-200 placeholder-slate-400 contrast-more:border-slate-400 contrast-more:placeholder-slate-500"/>
<p class="mt-2 opacity-10 contrast-more:opacity-100 text-slate-600 text-sm">
We need this to steal your identity.
</p>
</label>
</form>
Tailwind also includes a contrast-less
modifier you can use to conditionally add styles when the user has requested less contrast.
Use the portrait
and landscape
modifiers to conditionally add styles when the viewport is in a specific orientation:
<div>
<div class="portrait:hidden">
<!-- ... -->
</div>
<div class="landscape:hidden">
<p>
This experience is designed to be viewed in landscape. Please rotate your
device to view the site.
</p>
</div>
</div>
Use the print
modifier to conditionally add styles that only apply when the document is being printed:
<div>
<article class="print:hidden">
<h1>My Secret Pizza Recipe</h1>
<p>This recipe is a secret, and must not be shared with anyone</p>
<!-- ... -->
</article>
<div class="hidden print:block">
Are you seriously trying to print this? It's secret!
</div>
</div>
Use the supports-[...]
modifier to style things based on whether a certain feature is supported in the user’s browser.
<div class="flex supports-[display:grid]:grid ...">
<!-- ... -->
</div>
Under the hood the supports-[...]
modifier generates @supports rules
and takes anything you’d use with @supports (...)
between the square brackets, like a property/value pair, and even expressions using and
and or
.
For terseness, if you only need to check if a property is supported (and not a specific value), you can just specify the property name:
<div class="bg-black/75 supports-[backdrop-filter]:bg-black/25 supports-[backdrop-filter]:backdrop-blur ...">
<!-- ... -->
</div>
You can configure shortcuts for common @supports
rules you’re using in your project in the theme.supports
section of your tailwind.config.js
file:
module.exports = {
theme: {
supports: {
grid: 'display: grid',
},
},
}
You can then use these custom supports-*
modifiers in your project:
<div class="supports-grid:grid">
<!-- ... -->
</div>
Use the aria-*
modifier to conditionally style things based on ARIA attributes.
For example, to apply the bg-sky-700
class when the aria-checked
attribute is set to true
, use the aria-checked:bg-sky-700
class:
<div aria-checked="true" class="bg-gray-600 aria-checked:bg-sky-700">
<!-- ... -->
</div>
By default we’ve included modifiers for the most common boolean ARIA attributes:
Modifier | CSS |
---|---|
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”] |
You can customize which aria-*
modifiers are available by editing theme.aria
or theme.extend.aria
in your tailwind.config.js
file:
module.exports = {
theme: {
extend: {
aria: {
asc: 'sort="ascending"',
desc: 'sort="descending"',
},
},
},
};
If you need to use a one-off aria
modifier that doesn’t make sense to include in your theme, or for more complex ARIA attributes that take specific values, use square brackets to generate a property on the fly using any arbitrary value.
Invoice # | Client | Amount |
---|---|---|
#100 | Pendant Publishing | $2,000.00 |
#101 | Kruger Industrial Smoothing | $545.00 |
#102 | J. Peterman | $10,000.25 |
<table>
<thead>
<tr>
<th
aria-sort="ascending"
class="aria-[sort=ascending]:bg-[url('/img/down-arrow.svg')] aria-[sort=descending]:bg-[url('/img/up-arrow.svg')]"
>
Invoice #
</th>
<!-- ... -->
</tr>
</thead>
<!-- ... -->
</table>
.aria-\[sort\=ascending\]\:bg-\[url\(\'\/img\/down-arrow\.svg\'\)\][aria-sort=ascending] {
background-image: url('/img/down-arrow.svg');
}
.aria-\[sort\=descending\]\:bg-\[url\(\'\/img\/up-arrow\.svg\'\)\][aria-sort=descending] {
background-image: url('/img/up-arrow.svg');
}
ARIA state modifiers can also target parent and sibling elements using the group-aria-*
and peer-aria-*
modifiers:
<table>
<thead>
<tr>
<th aria-sort="ascending" class="group">
Invoice #
<svg class="group-aria-[sort=ascending]:rotate-0 group-aria-[sort=descending]:rotate-180"><!-- ... --></svg>
</th>
<!-- ... -->
</tr>
</thead>
<!-- ... -->
</table>
.group[aria-sort=ascending] .group-aria-\[sort\=ascending\]\:rotate-0 {
--tw-rotate: 0deg;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.group[aria-sort=descending] .group-aria-\[sort\=descending\]\:rotate-180 {
--tw-rotate: 180deg;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
Use the data-*
modifier to conditionally apply styles based on data attributes.
Since there are no standard data-*
attributes by definition, by default we only support arbitrary values out of the box, for example:
<!-- Will apply -->
<div data-size="large" class="data-[size=large]:p-8">
<!-- ... -->
</div>
<!-- Will not apply -->
<div data-size="medium" class="data-[size=large]:p-8">
<!-- ... -->
</div>
You can configure shortcuts for common data attribute selectors you’re using in your project in the theme.data
section of your tailwind.config.js
file:
module.exports = {
theme: {
data: {
checked: 'ui~="checked"',
},
},
}
You can then use these custom data-*
modifiers in your project:
<div data-ui="checked active" class="data-checked:underline">
<!-- ... -->
</div>
Use the rtl
and ltr
modifiers to conditionally add styles in right-to-left and left-to-right modes respectively when building multi-directional layouts:
Left-to-right
Tom Cook
Director of Operations
Right-to-left
تامر كرم
الرئيس التنفيذي
<div class="group flex items-center"> <img class="shrink-0 h-12 w-12 rounded-full" src="..." alt="" /> <div class="ltr:ml-3 rtl:mr-3"> <p class="text-sm font-medium text-slate-700 group-hover:text-slate-900">...</p> <p class="text-sm font-medium text-slate-500 group-hover:text-slate-700">...</p> </div> </div>
<div class="group flex items-center"> <img class="shrink-0 h-12 w-12 rounded-full" src="..." alt="" /> <div class="ltr:ml-3 rtl:mr-3"> <p class="text-sm font-medium text-slate-300 group-hover:text-white">...</p> <p class="text-sm font-medium text-slate-500 group-hover:text-slate-300">...</p> </div> </div>
Note that the ltr
modifier will not take effect unless the dir
attribute is explicitly set to ltr
, so if you are building a multi-directional site make sure to always set a direction, not just in rtl
mode.
Always set the direction, even if left-to-right is your default
<html dir="ltr">
<!-- ... -->
</html>
Remember, these modifiers are only useful if you are building a site that needs to support both left-to-right and right-to-left layouts. If you’re building a site that only needs to support a single direction, you don’t need these modifiers — just apply the styles that make sense for your content.
Use the open
modifier to conditionally add styles when a <details>
or <dialog>
element is in an open state:
Try toggling the disclosure to see the styles change
The mug is round. The jar is round. They should call it Roundtine.
<div class="max-w-lg mx-auto p-8">
<details class="open:bg-white dark:open:bg-slate-900 open:ring-1 open:ring-black/5 dark:open:ring-white/10 open:shadow-lg p-6 rounded-lg" open>
<summary class="text-sm leading-6 text-slate-900 dark:text-white font-semibold select-none">
Why do they call it Ovaltine?
</summary>
<div class="mt-3 text-sm leading-6 text-slate-600 dark:text-slate-400">
<p>The mug is round. The jar is round. They should call it Roundtine.</p>
</div>
</details>
</div>
Just like arbitrary values let you use custom values with your utility classes, arbitrary variants let you write custom selector modifiers directly in your HTML.
Arbitrary variants are just format strings that represent the selector, wrapped in square brackets. For example, this arbitrary modifier selects an element only when it is the third child:
<ul role="list">
{#each items as item}
<li class="[&:nth-child(3)]:underline">{item}</li>
{/each}
</ul>
/* https://media.giphy.com/media/uPnKU86sFa2fm/giphy.gif */
.\[\&\:nth-child\(3\)\]\:underline:nth-child(3) {
text-decoration-style: underline
}
The format string is the same as what you’d use with the addVariant
plugin API, with the &
representing the selector being modified.
Arbitrary variants can be stacked with built-in modifiers or with each other, just like the rest of the modifiers in Tailwind:
<ul role="list">
{#each items as item}
<li class="lg:[&:nth-child(3)]:hover:underline">{item}</li>
{/each}
</ul>
/* https://media.giphy.com/media/Sd3cd0SrUKZEyWmAlM/giphy.gif */
@media (min-width: 1024px) {
.lg\:\[\&\:nth-child\(3\)\]\:hover\:underline:hover:nth-child(3) {
text-decoration-line: underline;
}
}
If you need spaces in your selector, you can use an underscore. For example, this arbitrary modifier selects all p
elements within the element where you’ve added the class:
<div class="[&_p]:mt-4">
<p>Lorem ipsum...</p>
<ul>
<li>
<p>Lorem ipsum...</p>
</li>
<!-- ... -->
</ul>
</div>
.\[\&_p\]\:mt-4 p {
margin-top: 1rem;
}
You can also use at-rules like @media
or @supports
in arbitrary variants:
<div class="flex [@supports(display:grid)]:grid">
<!-- ... -->
</div>
/* https://media.giphy.com/media/sEms56zTGDx96/giphy.gif */
@supports (display: grid) {
.\[\@supports\(display\:grid\)\]\:grid {
display: grid;
}
}
With at-rule custom modifiers the &
placeholder isn’t necessary, just like when nesting with a preprocessor.
You can even combine at-rules and regular selector modifiers by including the selector modifier within curly braces after the at-rule:
<button type="button" class="[@media(any-hover:hover){&:hover}]:opacity-100">
<!-- ... -->
</button>
/* https://media.giphy.com/media/l0IypeKl9NJhPFMrK/giphy.gif */
@media (any-hover: hover) {
.\[\@media\(any-hover\:hover\)\{\&\:hover\}\]\:opacity-100:hover {
opacity: 1
}
}
If you find yourself using the same arbitrary modifier multiple times in your project, it might be worth extracting it to a plugin using the addVariant
API:
let plugin = require('tailwindcss/plugin')
module.exports = {
// ...
plugins: [
plugin(function ({ addVariant }) {
// Add a `third` variant, ie. `third:pb-0`
addVariant('third', '&:nth-child(3)')
})
]
}
Learn more in the adding variant plugins documentation.
All of Tailwind’s modifiers are available to use with your own custom classes as long as you’ve defined them in one of Tailwind’s layers or added them using a plugin:
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer utilities {
.content-auto {
content-visibility: auto;
}
}
<div class="lg:content-auto">
<!-- ... -->
</div>
When stacking modifiers, they are applied from the inside-out, like nested function calls:
// These modifiers:
'dark:group-hover:focus:opacity-100'
// ...are applied like this:
dark(groupHover(focus('opacity-100')))
For the most part this doesn’t actually matter, but there are a few situations where the order you use actually generates meaningfully different CSS.
For example, if you have darkMode
configured to class
, combining the dark
and group-hover
modifiers generates a different result depending on the order you use:
/* dark:group-hover:opacity-100 */
.dark .group:hover .dark\:group-hover\:opacity-100 {
opacity: 1;
}
/* group-hover:dark:opacity-100 */
.group:hover .dark .group-hover\:dark\:opacity-100 {
opacity: 1;
}
In the first example, the dark
element needs to be a parent of the group
element, but in the second example it’s reversed.
Another place this is important is when using modifiers like prose-headings
that are included with the official typography plugin:
/* prose-headings:hover:underline */
.prose-headings\:hover\:underline:hover :is(:where(h1, h2, h3, h4, th)) {
text-decoration: underline;
}
/* hover:prose-headings:underline */
.hover\:prose-headings\:underline :is(:where(h1, h2, h3, h4, th)):hover {
text-decoration: underline;
}
In the first example, every single heading is underlined when you hover over the article itself, whereas in the second example each heading is only underlined when you hover over that heading.
A quick reference table of every single modifier included in Tailwind by default.
Modifier | CSS |
---|---|
hover | &:hover |
focus | &:focus |
focus-within | &:focus-within |
focus-visible | &:focus-visible |
active | &:active |
visited | &:visited |
target | &:target |
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 |
empty | &:empty |
disabled | &:disabled |
enabled | &:enabled |
checked | &:checked |
indeterminate | &:indeterminate |
default | &:default |
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 |
selection | &::selection |
file | &::file-selector-button |
backdrop | &::backdrop |
placeholder | &::placeholder |
sm | @media (min-width: 640px) |
md | @media (min-width: 768px) |
lg | @media (min-width: 1024px) |
xl | @media (min-width: 1280px) |
2xl | @media (min-width: 1536px) |
min-[…] | @media (min-width: …) |
max-sm | @media not all and (min-width: 640px) |
max-md | @media not all and (min-width: 768px) |
max-lg | @media not all and (min-width: 1024px) |
max-xl | @media not all and (min-width: 1280px) |
max-2xl | @media not all and (min-width: 1536px) |
max-[…] | @media (max-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) |
@media print | |
supports-[…] | @supports (…) |
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 | [dir=“rtl”] & |
ltr | [dir=“ltr”] & |
open | &[open] |
This is a comprehensive list of examples for all the pseudo-class modifiers included in Tailwind to complement the pseudo-classes documentation at the beginning of this guide.
Style an element when the user hovers over it with the mouse cursor using the hover
modifier:
<div class="bg-black hover:bg-white ...">
<!-- ... -->
</div>
Style an element when it has focus using the focus
modifier:
<input class="border-gray-300 focus:border-blue-400 ..." />
Style an element when it or one of its descendants has focus using the focus-within
modifier:
<div class="focus-within:shadow-lg ...">
<input type="text" />
</div>
Style an element when it has been focused using the keyboard using the focus-visible
modifier:
<button class="focus:outline-none focus-visible:ring ...">
Submit
</button>
Style an element when it is being pressed using the active
modifier:
<button class="bg-blue-500 active:bg-blue-600 ...">
Submit
</button>
Style a link when it has already been visited using the visited
modifier:
<a href="https://seinfeldquotes.com" class="text-blue-600 visited:text-purple-600 ...">
Inspiration
</a>
Style an element if its ID matches the current URL fragment using the target
modifier:
<div id="about" class="target:shadow-lg ...">
<!-- ... -->
</div>
Style an element if it’s the first child using the first
modifier:
<ul>
{#each people as person}
<li class="py-4 first:pt-0 ...">
<!-- ... -->
</li>
{/each}
</ul>
Style an element if it’s the last child using the last
modifier:
<ul>
{#each people as person}
<li class="py-4 last:pb-0 ...">
<!-- ... -->
</li>
{/each}
</ul>
Style an element if it’s the only child using the only
modifier:
<ul>
{#each people as person}
<li class="py-4 only:py-0 ...">
<!-- ... -->
</li>
{/each}
</ul>
Style an element if it’s an oddly numbered child using the odd
modifier:
<table>
{#each people as person}
<tr class="bg-white odd:bg-gray-100 ...">
<!-- ... -->
</tr>
{/each}
</table>
Style an element if it’s an evenly numbered child using the even
modifier:
<table>
{#each people as person}
<tr class="bg-white even:bg-gray-100 ...">
<!-- ... -->
</tr>
{/each}
</table>
Style an element if it’s the first child of its type using the first-of-type
modifier:
<nav>
<img src="/logo.svg" alt="Vandelay Industries" />
{#each links as link}
<a href="#" class="ml-2 first-of-type:ml-6 ...">
<!-- ... -->
</a>
{/each}
</table>
Style an element if it’s the last child of its type using the last-of-type
modifier:
<nav>
<img src="/logo.svg" alt="Vandelay Industries" />
{#each links as link}
<a href="#" class="mr-2 last-of-type:mr-6 ...">
<!-- ... -->
</a>
{/each}
<button>More</button>
</table>
Style an element if it’s the only child of its type using the only-of-type
modifier:
<nav>
<img src="/logo.svg" alt="Vandelay Industries" />
{#each links as link}
<a href="#" class="mx-2 only-of-type:mx-6 ...">
<!-- ... -->
</a>
{/each}
<button>More</button>
</table>
Style an element if it has no content using the empty
modifier:
<ul>
{#each people as person}
<li class="empty:hidden ...">{person.hobby}</li>
{/each}
</ul>
Style an input when it’s disabled using the disabled
modifier:
<input class="disabled:opacity-75 ..." />
Style an input when it’s enabled using the enabled
modifier, most helpful when you only want to apply another style when an element is not disabled:
<input class="enabled:hover:border-gray-400 disabled:opacity-75 ..." />
Style a checkbox or radio button when it’s checked using the checked
modifier:
<input type="checkbox" class="appearance-none checked:bg-blue-500 ..." />
Style a checkbox or radio button in an indeterminate state using the indeterminate
modifier:
<input type="checkbox" class="appearance-none indeterminate:bg-gray-300 ..." />
Style an option, checkbox or radio button that was the default value when the page initially loaded using the default
modifier:
<input type="checkbox" class="default:ring-2 ..." />
Style an input when it’s required using the required
modifier:
<input class="required:border-red-500 ..." />
Style an input when it’s valid using the valid
modifier:
<input class="valid:border-green-500 ..." />
Style an input when it’s invalid using the invalid
modifier:
<input class="invalid:border-red-500 ..." />
Style an input when it’s value is within a specified range limit using the in-range
modifier:
<input min="1" max="5" class="in-range:border-green-500 ..." />
Style an input when it’s value is outside of a specified range limit using the out-of-range
modifier:
<input min="1" max="5" class="out-of-range:border-red-500 ..." />
Style an input when the placeholder is shown using the placeholder-shown
modifier:
<input class="placeholder-shown:border-gray-500 ..." placeholder="you@example.com" />
Style an input when it has been autofilled by the browser using the autofill
modifier:
<input class="autofill:bg-yellow-200 ..." />
Style an input when it is read-only using the read-only
modifier:
<input class="read-only:bg-gray-100 ..." />