new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Tailwind CSS v4 · all subjects

custom-variants

74 notes in this subject, read out of this brain and free to use. This is page 1 of 2.

@custom-variant directive for custom variants

Tailwind v4 lets you define your own variants (like hover:, dark:, etc.) using the @custom-variant directive in CSS instead of the v3 plugin API. Example with nesting and @slot placeholder: @custom-variant theme-midnight { &:where([data-theme="midnight"] *) { @slot; } } Usage in HTML: <html data-theme="midnight"><button class="theme-midnight:bg-black ..."></button></html>. A shorthand syntax without nesting is available when only a single rule is needed: @custom-variant theme-midnight (&:where([data-theme="midnight"] *)); When a custom variant needs multiple nested rules, they can be nested within each other, e.g.: @custom-variant any-hover { @media (any-hover: hover) { &:hover { @slot; } } }

Reduced motion variants with animation

For users who prefer reduced motion, you can conditionally apply animations using the `motion-safe` and `motion-reduce` variants, e.g. `motion-safe:animate-spin` only applies the spin animation when the user has not requested reduced motion.

dark variant default behavior

By default, the dark variant in Tailwind uses the prefers-color-scheme CSS media feature to apply dark mode styles based on the operating system's dark mode setting.

Override dark variant with CSS class selector

To make dark mode driven by a CSS selector instead of prefers-color-scheme, use @custom-variant dark (&:where(.dark, .dark *)); in your app.css. This applies dark:* utilities whenever the dark class is present earlier in the HTML tree.

Override dark variant with data attribute

To use a data attribute to activate dark mode instead of a class, override the dark variant with @custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *)); in your app.css. Dark mode utilities will then be applied whenever the data-theme attribute is set to dark somewhere up the tree.

dark:* utility class syntax

Use the dark: prefix on any utility class to apply that style only when dark mode is enabled. For example, dark:bg-gray-800 applies a gray-800 background in dark mode, and dark:text-white applies white text in dark mode.

Implementing system theme support with manual dark mode toggle

To support light mode, dark mode, and system theme preference, use the window.matchMedia() API to detect the system theme. On page load, toggle the dark class based on localStorage.theme or the system preference: document.documentElement.classList.toggle('dark', localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)). Update localStorage.theme to 'light' or 'dark' when the user explicitly chooses, and removeItem('theme') to respect the OS preference.

@variant directive for applying variants

Use the @variant directive to apply a Tailwind variant to styles in your CSS. Variants like dark can be applied to selectors within this directive.

@custom-variant directive for custom variants

Use the @custom-variant directive to add a custom variant in your project. Example: @custom-variant theme-midnight (&:where([data-theme="midnight"] *)); This lets you write utilities like theme-midnight:bg-black and theme-midnight:text-white.

Variants allow conditional application of utilities

Every utility class in Tailwind can be applied conditionally by adding a variant to the beginning of the class name that describes the condition you want to target. For example, hover:bg-sky-700 applies bg-sky-700 only on hover. Unlike traditional CSS where a single class name does different things based on state, Tailwind uses separate classes for different states.

Variants can be stacked together

Multiple variants can be combined to target more specific situations. For example, dark:md:hover:bg-fuchsia-600 targets an element in dark mode, at the medium breakpoint, on hover.

Pseudo-class variants available

Tailwind includes variants for pseudo-classes including: hover, focus, active, visited, focus-within, focus-visible, first, last, odd, even, nth-*, nth-last-*, nth-of-type-*, nth-last-of-type-*, only-child, first-of-type, empty, required, invalid, disabled, read-only, indeterminate, checked, and :has().

hover, focus, and active variants

The hover, focus, and active variants apply styles to elements on hover, focus, and active states respectively. Example: class="bg-violet-500 hover:bg-violet-600 focus:outline-2 active:bg-violet-700"

first and last child variants

The first and last variants style elements when they are the first-child or last-child. For example, first:pt-0 removes top padding from the first child, and last:pb-0 removes bottom padding from the last child.

odd and even child variants

The odd and even variants style elements based on whether they are odd or even children. Example: odd:bg-white even:bg-gray-50 alternates background colors in a table.

nth-* variants for positional styling

The nth-3, nth-last-5, nth-of-type-4, and nth-last-of-type-6 variants style children based on their position in the list. Any number can be passed by default, and arbitrary values like nth-[2n+1_of_li] are supported for more complex expressions.

Form state variants

Tailwind includes variants for form element states: required, invalid, disabled, read-only, indeterminate, and checked. These reduce conditional logic in templates by letting the browser apply the right styles based on input state.

has-* variant for styling based on descendant state

The has-* variant styles an element based on the state or content of its descendants. For example, has-checked:bg-indigo-50 applies background color when a descendant input is checked. Can be used with pseudo-classes like has-[:focus] or element selectors like has-[img].

group/{name} for differentiating nested groups

When nesting groups, give a parent group a unique name using group/{name} class, and include that name in variants using classes like group-hover/{name}. For example, group/item and group-hover/item:visible targets the specific parent group.

Arbitrary group-* variants with square brackets

Create one-off group-* variants by providing a custom selector as an arbitrary value between square brackets. Example: group-[.is-published]:block. Use the & character to control where .group ends up relative to the selector provided.

in-* variant as implicit group

The in-* variant works similarly to group but does not require adding the group class to the parent element. Example: in-focus:opacity-100 responds to state changes in any parent, but for fine-grained control, use group instead.

peer class for sibling state styling

Mark a sibling element with the peer class, then use peer-* variants like peer-invalid to style target elements based on sibling state. Example: peer-invalid:visible shows an error message when a peer input is invalid.

peer only works on previous siblings

The peer marker can only be used on previous siblings because of how the subsequent-sibling combinator works in CSS. You cannot mark a sibling that comes after the styled element.

peer/{name} for differentiating multiple peers

When using multiple peers, give each peer a unique name using peer/{name} class, and include that name in variants using classes like peer-checked/{name}. Example: peer/draft and peer-checked/draft:text-sky-500.

Arbitrary peer-* variants with square brackets

Create one-off peer-* variants by providing a custom selector as an arbitrary value between square brackets. Example: peer-[.is-dirty]:peer-required:block. Use the & character for more control over where .peer appears in the selector.

not-* variant for negation

Use the not- variant to style an element when a condition is not true. For example, hover:not-focus:bg-indigo-700 applies hover styles only when the element is not focused. Also works with media query variants like not-supports-[display:grid]:flex.

group-has-* variant for group descendants

Use group-has-* variant to style an element based on the descendants of a parent element marked with group class. Example: group-has-[a]:block shows an SVG only when the group contains an anchor element.

peer-has-* variant for peer descendants

Use peer-has-* variant to style an element based on the descendants of a sibling element marked with peer class. Example: peer-has-checked:hidden hides an element when a peer checkbox is checked.

before and after pseudo-element variants

Style ::before and ::after pseudo-elements using the before and after variants. Tailwind automatically adds content: '' by default, so you only need to specify it if you want a different value. Example: after:content-['*'] adds an asterisk after an element.

Pseudo-element variants available

Tailwind includes variants for pseudo-elements including: ::before, ::after, ::placeholder, and ::selection.

Media and feature query variants available

Tailwind includes variants for media and feature queries including responsive breakpoints, dark mode, and prefers-reduced-motion.

Attribute selector variants available

Tailwind includes variants for attribute selectors such as [dir="rtl"] and [open].

Child selector variants available

Tailwind includes variants for child selectors such as & > * and & *.

custom @supports variants using @custom-variant

You can configure shortcuts for common @supports rules by creating a new variant in the supports-* namespace using @custom-variant. For example: @custom-variant supports-grid { @supports (display: grid) { @slot; } }. You can then use supports-grid:grid in your project.

custom ARIA variants using @custom-variant

You can customize which aria-* variants are available by creating new variants. For example: @custom-variant aria-asc (&[aria-sort="ascending"]); and @custom-variant aria-desc (&[aria-sort="descending"]);

custom data-* variants using @custom-variant

You can configure shortcuts for common data attributes by creating a new variant in the data-* namespace. For example: @custom-variant data-checked (&[data-ui~="checked"]); allows you to use data-checked:underline in your project.

* variant for styling direct children

The * variant styles all direct children of an element using the selector :is(& > *). This is useful when you need to style direct children that you don't have control over. For example, *:rounded-full applies rounded-full to all direct children.

** variant for styling all descendants

The ** variant styles all descendants (not just direct children) of an element using the selector :is(& *). It is especially useful when combined with another variant for narrowing the selection, such as **:data-avatar:size-12 to select all elements with a data-avatar attribute.

Children cannot override parent * variant styles

When using the * variant to style children, overriding that style with a utility class directly on the child element will not work because children rules are generated after regular ones and have the same specificity. For example, if a parent has *:bg-sky-50, adding bg-red-50 to a child will not override the parent's style.

Arbitrary variants with square brackets syntax

Arbitrary variants are format strings representing a selector, wrapped in square brackets. For example, [&.is-dragging]:cursor-grabbing changes the cursor to grabbing when the element has the is-dragging class. The generated CSS is .[&\.is-dragging\]:cursor-grabbing { &.is-dragging { cursor: grabbing; } }.

Arbitrary variants with descendant selectors use underscore for spaces

When creating arbitrary variants that select descendants with spaces in the selector, use an underscore. For example, [&_p]:mt-4 selects all p elements within the element and generates .[&_p\]:mt-4 { & p { margin-top: calc(var(--spacing) * 4); } }.

Arbitrary variants can use at-rules like @media and @supports

Arbitrary variants support at-rules such as @media and @supports. For example, [@supports(display:grid)]:grid applies display: grid within an @supports rule. The & placeholder is not necessary with at-rule variants, unlike when nesting with a preprocessor.

Arbitrary variants can be stacked with built-in variants

Arbitrary variants can be stacked with built-in variants or with each other, just like other variants in Tailwind. For example, [&.is-dragging]:active:cursor-grabbing combines an arbitrary variant with the active built-in variant.

@custom-variant directive for reusable custom variants

If you find yourself using the same arbitrary variant multiple times, you can create a custom variant using the @custom-variant directive. For example, @custom-variant theme-midnight (&:where([data-theme="midnight"] *)); creates a reusable theme-midnight variant that can then be used as theme-midnight:bg-black in HTML.

Complete variant quick reference table

Tailwind v4 includes the following variants with their CSS equivalents: 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), user-valid (&:user-valid), user-invalid (&:user-invalid), in-range (&:in-range), out-of-range (&:out-of-range), placeholder-shown (&:placeholder-shown), details-content (&:details-content), 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).

Responsive and container query variants quick reference

Tailwind v4 includes responsive breakpoint variants: 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 < ...)). Container query variants include @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 < ...)).

Media and accessibility variants quick reference

Tailwind v4 includes the following media and accessibility variants: dark (@media (prefers-color-scheme: dark)), 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)), forced-colors (@media (forced-colors: active)), inverted-colors (@media (inverted-colors: inverted)), pointer-fine (@media (pointer: fine)), pointer-coarse (@media (pointer: coarse)), pointer-none (@media (pointer: none)), any-pointer-fine (@media (any-pointer: fine)), any-pointer-coarse (@media (any-pointer: coarse)), any-pointer-none (@media (any-pointer: none)), portrait (@media (orientation: portrait)), landscape (@media (orientation: landscape)), noscript (@media (scripting: none)), 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)), starting (@starting-style).

disabled variant for input styling

Use the disabled variant to style an input when it is disabled. Example: disabled:opacity-75 applies opacity-75 to a disabled input element.

enabled variant for input styling

Use the enabled variant to style an input when it is enabled. This is most helpful when you only want to apply another style when an element is not disabled. Example: enabled:hover:border-gray-400 applies border-gray-400 on hover only when the input is enabled.

checked variant for checkboxes and radio buttons

Use the checked variant to style a checkbox or radio button when it is checked. Example: checked:bg-blue-500 applies a blue background when the checkbox is checked.

indeterminate variant for checkboxes and radio buttons

Use the indeterminate variant to style a checkbox or radio button in an indeterminate state. Example: indeterminate:bg-gray-300 applies a gray background when the checkbox is in an indeterminate state.

default variant for form elements

Use the default variant to style an option, checkbox, or radio button that was the default value when the page initially loaded. Example: default:outline-2 applies a 2px outline to the default checkbox.

optional variant for input styling

Use the optional variant to style an input when it is optional. Example: optional:border-red-500 applies a red border to an optional input.

required variant for input styling

Use the required variant to style an input when it is required. Example: required:border-red-500 applies a red border to a required input element.

valid variant for input styling

Use the valid variant to style an input when it is valid. Example: valid:border-green-500 applies a green border to a valid input.

invalid variant for input styling

Use the invalid variant to style an input when it is invalid. Example: invalid:border-red-500 applies a red border to an invalid input.

user-valid variant for input styling

Use the user-valid variant to style an input when it is valid and the user has interacted with it. Example: user-valid:border-green-500 applies a green border to a user-validated input.

user-invalid variant for input styling

Use the user-invalid variant to style an input when it is invalid and the user has interacted with it. Example: user-invalid:border-red-500 applies a red border to an invalid input after user interaction.

in-range variant for input styling

Use the in-range variant to style an input when its value is within a specified range limit. Example: in-range:border-green-500 applies a green border to an input with a value within its min/max range.

out-of-range variant for input styling

Use the out-of-range variant to style an input when its value is outside of a specified range limit. Example: out-of-range:border-red-500 applies a red border to an input with a value outside its min/max range.

Give your agent this brain