Extended Functionality

Tailwind provides a comprehensive list of variants and utility classes. Twind supports all Tailwind variants/classes, and we've added a few more for your convenience.

Variants

Variants are special keywords recognized by the compiler used to apply styles to HTML element(s) based on pseudo states, viewport sizes, dark mode, or other states. Variants are used by prefixing class names or groupings and are denoted with a trailing colon. (e.g. md:text-blue-500 or md:(text-blue-500))

Tailwind provides a default configuration, and requires that you explicitly associate certain variants to styles as you need them. This is not the case with Twind. Twind support all Tailwind variants out of the box with no configuration required.

<p class="text-blue-500 md:text-red-500">Hello Twind!</p>

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

View docs for dark mode

Most pseudo classes can be used 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. You can define an alias for those in your configuration:

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

tw`is-header:font-bold`
// => .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.

Negating styles with the not- pseudo-class prefix.

Most Twind rules can be prefixed with the not-prefix, which represents a non-match to the rule. For instance, not-hover:uppercase would apply the uppercase style any time the element is not being hovered.

Here are some other examples using the not- prefix, with the derived CSS selector:

Class nameSelector
not-focus:invalid:border-red-500.not-focus\:invalid\:border-red-500:not(:focus):invalid
invalid:not-focus:border-red-500.invalid\:not-focus\:border-red-500:invalid:not(:focus)
not-disabled:focus:font-bold.not-disabled\:focus\:font-bold:not(:disabled):focus
not-last-child:mb-5.not-last-child\:mb-5:not(:last-child)

Core and user defined variants are not expanded and stay as is:

setup({
  variants: {
    'not-logged-in': 'body:not(.logged-in) &',
  },
})

tw`not-logged-in:hidden`
// => `body:not(.logged-in) .not-logged-in\\:hidden`

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.
    </p>
  </div>
</div>

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?
</p>

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

import { content } from '@twind/content'

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

tw`before::${content('"✅"')}`
// => .tw-xxxx::before { content: "✅" }

tw`before::${content('attr(data-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 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 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>
</div>
Live 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 Demo

Utilities

Arbitrary style values using square bracket syntax

While not generally recommended, there are times when you will need to break out of the Twind constraints for one-off styles like a slight rotation, relative positioning, custom font size, etc. Twind provides a square bracket syntax, which allows you to define these arbitrary styles without ever leaving your HTML:

<p class="relative -top-[-8px]">Hello Twind!</p>

TIP

Square bracket syntax will work almost anywhere that you could apply a theme value, including with variants: md:top-[-80px]

Here are some other examples of using the square bracket syntax to provide arbitrary CSS values:

bg-[#0f0]
bg-[#ff0000]
bg-[#0000ffcc]
bg-[hsl(0,100%,50%)]
bg-[hsla(0,100%,50%,0.3)]
bg-[rgb(123,123,123)]
bg-[rgba(123,123,123,var(--tw-bg-opacity))]
bg-opacity-[0.11]
border-[#f00]
border-[2.5px]
duration-[2s]
grid-cols-[200px,repeat(auto-fill,minmax(15%,100px)),300px]
grid-cols-[minmax(100px,max-content)repeat(auto-fill,200px)20%]
grid-cols-[repeat(auto-fit,minmax(150px,1fr))]
flex-[30%]
ring-[#1da1f2]
ring-[7px]
ring-offset-[#1da1f2]
ring-offset-[7px]
rotate-[0.5turn]
rotate-[23deg]
rotate-[2.3rad]
rotate-[401grad]
rotate-[1.5turn]
rounded-[33%]
scale-[2]
scale-x-[1.15]
skew-[30deg]
skew-x-[1.07rad]
space-x-[20cm]
space-x-[calc(20%-1cm)]
text-[#1da1f2]
text-[2.23rem]
text-[6px]
text-[calc(1vw+1vh+.5vmin)]
top-[-123px]
top-[123px]
transition-[font-size,color,width]
translate-[3in]
translate-y-[2px]
w-[3.23rem]
w-[calc(100%+1rem)]
w-[clamp(23ch,50%,46ch)]

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, right, 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:

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

tw`bg-gradient-radial`

tw`bg-gradient-15 from-green-400 to-blue-500`
Live 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).

While Twind strives to maintain feature parity with Tailwind, we've added several variants, directives, and utilities for your convenience. This document includes a complete list of all features beyond Tailwind that Twind has to offer with links to the corresponding documentation.

Syntax

  • Custom grouping syntax for directives and variants View Docs
  • Overwrite styles with the important! directive View Docs