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
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 name | Selector |
|---|---|
| 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>
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>
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>
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-collapseborder-separatecursor-*font-*invisibleleading-*list-*text-*tracking-*visiblewhitespace-*
🙋 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)`
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
align-*- vertical-alignappearance-*- appearanceclear-*- clearcursor-*- cursorfloat-*- floatlist-*- list-style-typeobject-*- object-position; using a dash as separator:object-right-toporigin-*- transform-origin; using a dash as separator:origin-top-leftoverflow-*- overflowpointer-events-*- pointer-eventsselect-*- user-selectwhitespace-*- white-space
text-underline, text-uppercase, ...
This allows grouping of text directives: text(lg red-500 capitalize underline)
text-underlinetext-no-underlinetext-line-throughtext-uppercasetext-lowercasetext-capitalize
font-italic and font-no-italic
This allows grouping of font directives: font(sans italic bold)
font-italicfont-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`
border and divide allow to combine positions
Every permutation of top, rrigh, left, and bottom is allowed:
tr-top&rightbrl-bottom,rightandleft
💡
xandycan 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
proseclasses 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.