• Public
  • Public/Protected
  • All

Tailwind provides a comprehensive list of variants and directives. We have added some additional features that we found useful to have.


Custom syntax for grouping directives and variants

Having control over the interpreter affords us the possibility of defining terse syntax for grouping responsive and pseudo variants as well as directives with common prefixes. This massively reduces repetition and improves comprehension.

// Before directive grouping
tw`border-2 border-black border-opacity-50 border-dashed`
// After directive grouping
tw`border(2 black opacity-50 dashed)`

// With variants
tw`sm:(border(2 black opacity-50 hover:dashed))`
// => sm:border-2 sm:border-black sm:border-opacity-50 sm:hover:border-dashed

tw`w(1/2 sm:1/3 lg:1/6) p-2`
// => w-1/2 sm:w-1/3 lg:w-1/6 p-2

Using exclamation point (!) after a directive to override any other declarations

Directives may end with exclamation point (text-center!) to be marked as important:

<div class="children:text-red-500">
  <p>And here is a red paragraph!</p>
  <p class="text-purple-500">And this is a red paragraph!</p>
  <p class="text-purple-500!">This is not purple!</p>

🚀 live and interactive demo


Every variant can be applied to every directive

Because Twind is generating CSS during runtime there is no to need restrict the usage of variants.

Dark mode is always available

Please see Setup for details.

Most pseudo classes can be uses as variant or group-* variant

Unknown variants (not listed in core variants) are assumed to be pseudo classes.

Advanced pseudo classes (those that take parameters like :is(header)) are not supported out of the box as they use (...) which is parsed as a variant or directive grouping. You can define an alias for those during setup:

  variants: {
    'is-header': '&:is(header)',

// => .is-header\:font-bold:is(header) { ... }

🙋 If you have an idea how we could support these within the parser please open an issue for discussions.

Named groups to support nested groups

Named groups allow to nest groups within each other and target specific groups by their name. The group names are ad-hoc meaning there is no special configuration required.

Here is an example using the shim:

<div class="group-x bg-white hover:bg-blue-500 ...">
  <p class="text-gray-900 group-x-hover:text-white ...">New Project</p>
  <div class="group-y bg-gray-100 hover:bg-green-500 ...">
    <p class="text-gray-500 group-y-hover:text-white ...">
      Create a new project from a variety of starting templates.

Pseudo Elements are supported using double colon

Pseudo Elements can be used and are identified by a double colon:

<p class="first-line::(uppercase text-blue-500)">
  Styles will only be applied to the first line of this paragraph. After that, all text will be
  styled like normal. See what I mean?

::before and ::after are often used together with content property. The @twind/content extension helps in these cases:

import { content } from '@twind/content'

// => .tw-xxxx { content: "✅" }

// => .tw-xxxx::before { content: "✅" }

// => .tw-xxxx::before { content: attr(data-content) }

tw`after::${content('" (" attr(href) " )"')}`
// => .tw-xxxx::after { content: " (" attr(href) " )" }

💡 Please a look at the documentation of @twind/content for more examples.

siblings:* - General sibling combinator (& ~ *)

Matches elements that are following the element this is applied on (though not necessarily immediately), and are children of the same parent element (MDN - General sibling combinator).

<p>This is not red.</p>
<p class="siblings:text-red-500">Here is a paragraph.</p>
<p>And here is a red paragraph!</p>
<p>And this is a red paragraph!</p>

🚀 live and interactive demo

sibling:* - Adjacent sibling combinator (& + *)

Matches the element that immediately follows the element this is applied on, and is a children of the same parent element (MDN - Adjacent sibling combinator).

<p>This is not red.</p>
<p class="sibling:text-red-500">Here is a paragraph.</p>
<p>And here is a red paragraph!</p>
<p>This is not red!</p>

🚀 live and interactive demo

children:* - Child combinator (& > *)

Matches direct children of the element this is applied on (MDN - Child combinator).

<div class="children:(border my-2)">
  <p>This paragraph has <em>emphasized text</em> in it.</p>
  <p>This paragraph has <em>emphasized text</em> in it.</p>

🚀 live and interactive demo

Please note that some CSS properties are inherited and therefore all children will have those styles applied. Here is an (incomplete) list of directives that use inherited CSS properties where the style would be inherited by all children and not only the direct children:

  • border-collapse
  • border-separate
  • cursor-*
  • font-*
  • invisible
  • leading-*
  • list-*
  • text-*
  • tracking-*
  • visible
  • whitespace-*

🙋 If you find any incorrect or missing directive then please open an issue.

override:* - Increase the specificity of rules

When using components that have some default styles it happens that one wants to override a rule. Consider the following example:

const shared = tw`text(xl center blue-600) underline`
const special = tw`${shared} text-purple-600 no-underline`
// => text-xl text-center text-blue-600 underline text-purple-600 no-underline

One can not be sure that the text-purple-600 would be correctly applied as the order of classes does not matter. Only the specificity.

To support these cases Twind includes the override variant which uses a little trick to increase the specificity: .class-name.class-name is more specific than just .class-name

The above example should be re-written to:

const shared = tw`text(xl center blue-600) underline`
const special = tw`${shared} override:(text-purple-600 no-underline)`

🚀 live and interactive demo


Some directives support all CSS values

text-underline, text-uppercase, ...

This allows grouping of text directives: text(lg red-500 capitalize underline)

  • text-underline
  • text-no-underline
  • text-line-through
  • text-uppercase
  • text-lowercase
  • text-capitalize

font-italic and font-no-italic

This allows grouping of font directives: font(sans italic bold)

  • font-italic
  • font-no-italic

bg-gradient-to-* is built-in

Every permutation of top, rrigh, left, and bottom is handled by twind (like bg-gradient-to-tr). You can add new gradients but they should not use one of those keys:

  theme: {
    extend: {
      backgroundImage: (theme) => ({
        // Use a own gradient
        'gradient-radial': `radial-gradient(${theme('colors.blue.500')}, ${theme(
        // Integrate with gradient colors stops (from-*, via-*, to-*)
          'linear-gradient(.15turn, var(--tw-gradient-stops,var(--tw-gradient-from,transparent),var(--tw-gradient-to,transparent)))',


tw`bg-gradient-15 from-green-400 to-blue-500`

🚀 live and interactive demo

border and divide allow to combine positions

Every permutation of top, rrigh, left, and bottom is allowed:

  • tr - top & right
  • brl - bottom, right and left

💡 x and y can not be combined.

rotate, scale , skew and translate provide a fallback for IE 11

Please note that transform rotate-45 works but when using transform rotate-45 scale-150 only one of both is applied.

Theme values are automatically negated

There is no need to provided negated values in the theme. As soon as Twind detects a negated directive like -mx-2 it negates the theme value.

Extension Packages

  • @twind/aspect-ratio: a composable API for giving elements a fixed aspect ratio
  • @twind/content: a CSS content property directive
  • @twind/forms: a basic reset for form styles that makes form elements easy to override with utilities
  • @twind/line-clamp: utilities for visually truncating text after a fixed number of lines
  • @twind/typography: a set of prose classes you can use to add beautiful typographic defaults to any vanilla HTML you don't control (like HTML rendered from Markdown, or pulled from a CMS).

Continue to CSS in JS

Generated using TypeDoc