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
group-*
variant
Most pseudo classes can be used as variant or 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.
not-
pseudo-class prefix.
Negating styles with the 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-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)`
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-top
origin-*
- transform-origin; using a dash as separator:origin-top-left
overflow-*
- 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-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 t
op, r
ight, l
eft, and b
ottom 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 t
op, r
righ, l
eft, and b
ottom is allowed:
tr
-top
&right
brl
-bottom
,right
andleft
💡
x
andy
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.