CSS in Twind
twind/css
provides a set of utilities that allow you to write arbitrary CSS within Twind with support for global styles, animations, and more.
TIP
It is recommended to try to stay within the constraints of the Twind rules when possible. Applying arbitrary CSS should be generally be viewed as an escape hatch.
css
function
The This function allows you to write CSS within Twind and provides support for global styling.
You can use the &
selector to target the current element much like in other CSS-in-JS libraries:
import { tw, css } from 'twind/css'
tw(
css({
'&::before': { content: '"🙁"' },
'&::after': { content: '"😊"' },
}),
)
// => tw-xxxx
For optimal performance, it is advised to extract your css
function call into a variable:
const styles = css({
'&::before': { content: '"🙁"' },
'&::after': { content: '"😊"' },
})
tw(styles)
// => tw-xxxx
Any variants or groupings that are active when the css
function is called will be respected by the return value. This means that you can scope a css
call with every variant:
tw`
sm:hover:${css({
'&::before': { content: '"🙁"' },
'&::after': { content: '"😊"' },
})}
`
// => sm:hover:tw-xxxx
Values within the CSS object can be functions which are called with the context and should return the value to be used:
import { css, theme } from 'twind/css'
const styles = css({
// .tw-xxx a
a: {
color: theme('colors.blue.500'),
// .tw-xxx a:hover
'&:hover': {
color: theme('colors.blue.700'),
},
},
})
Tagged template literal syntax works like in emotion, goober or styled-components:
import { css, apply, theme } from 'twind/css'
const style = css`
color: rebeccapurple;
background-color: ${theme('colors.gray.500')};
&:hover {
${apply`text-purple-700`}
}
`
❗ Please note that the template literal syntax has a little performance impact as Twind needs to parse the CSS. For optimal performance use the object notation.
Variadic arguments and arrays (nested as deep as you like) are supported as well:
const style = css(
{
backgroundColor: 'hotpink',
'&:hover': {
color: 'darkgreen',
},
},
{
color: 'red',
},
)
const style = css([
{
backgroundColor: 'hotpink',
'&:hover': {
color: 'darkgreen',
},
},
{
color: 'red',
},
])
apply
can be used within css
:
import { css, apply } from 'twind/css'
css(apply`text-gray(700 dark:300)`, {
p: apply`my-5`,
h1: apply`text(black dark:white hover:purple-500)`,
})
// Or using template literals
css`
${apply`text-gray(700 dark:300)`}
p {
${apply('my-5')}
}
h1 {
${apply`text(black dark:white hover:purple-500)`}
}
`
For convenience, Twind provides the @apply
CSS rule:
css`
@apply text-gray(700 dark:300);
p {
@apply my-5;
}
h1 {
@apply text(black dark:white hover:purple-500);
}
`
css`
@apply ${['font-bold', false && 'underline', 'py-2 px-4']};
color: fuchsia;
`
@apply
can be used in the object notation as well:
css({
'@apply': 'font-bold py-2 px-4 underline',
// '@apply': ['font-bold py-2 px-4', false && underline'],
color: 'fuchsia',
transform: 'translateY(-1px)',
})
theme
function
The This function can be used to access theme values inside of a css
function call.
import { css, theme } from 'twind/css'
css({
color: theme('colors.blue.500'),
'&:hover': {
color: theme('colors.blue.700'),
},
})
css({
a: {
color: theme('colors.blue.500'),
// .tw-xxx a:hover
'&:hover': {
color: theme('colors.blue.700'),
},
},
})
screen
function
The This function allows you to create media queries that reference your Twind breakpoints by name (sm
,md
, etc.).
For example, say you have a sm
breakpoint at 640px
and you need to write some custom CSS that references this breakpoint.
Instead of writing a raw media query that duplicates that value like this:
css`
@media (min-width: 640px) {
/* ... */
}
`
...you can use the screen
function and reference the breakpoint by name:
import { css, screen, apply } from 'twind/css'
// With template literal
css`
${screen('sm')} {
/* ... */
}
${screen('md', css` /* ... */ `)}
${screen('lg', css({ /* ... */ }))}
${screen('xl', { /* ... */ })}
${screen('2xl', apply` ... `)}
`
// With object notation
css(
screen('md', css` /* ... */ `),
screen('lg', css({ /* ... */ })),
screen('xl', { /* ... */ }),
screen('2xl', apply` ... `),
)
`
For convenience, Twind provides the @screen
CSS rule:
import { css, screen, apply } from 'twind/css'
// With template literal
css`
@screen sm {
/* ... */
}
`
// With object notation
css({
'@screen md': {
/* ... */
}
})
`
animation
function
The This function provides a simplified abstraction for creating custom animations within a css
function call.
Custom animations are difficult to configure in Tailwind. During setup
you need to add to the theme.animation
section and the theme.keyframes
section. This means all animations must known before hand and you can not use "one-off" animations.
With the animation
exports, this task is greatly simplified:
import { animation } from 'twind/css'
const bounce = animation('1s ease infinite', {
'from, 20%, 53%, 80%, to': {
transform: 'translate3d(0,0,0)',
},
'40%, 43%': {
transform: 'translate3d(0, -30px, 0)',
},
'70%': {
transform: 'translate3d(0, -15px, 0)',
},
'90%': {
transform: 'translate3d(0, -4px, 0)',
},
})
tw`hover:${bounce}`
Template literal syntax is supported as well:
const bounce = animation('1s ease infinite')`
from, 20%, 53%, 80%, to {
${apply`transform-gpu translate-x-0`}
}
40%, 43% {
${apply`transform-gpu -translate-x-7`}
}
70% {
${apply`transform-gpu -translate-x-3.5`}
},
90% {
${apply`transform-gpu -translate-x-1`}
}
`
The first argument can be a animation shorthand CSS string, an object of CSS animation properties or a function which is passed the context to return the shorthand CSS:
const slidein = animation(
({ theme }) => `${theme('durations.500')} ${theme('transitionTimingFunction.in-out')}`,
{
from: {
transform: 'translateX(0%)',
},
to: {
transform: 'translateX(100%)',
},
},
)
import { theme } from 'twind'
const bounce = animation(
{
animationDuration: '1s',
animationTimingFunction: theme('transitionTimingFunction.in-out'),
animationIterationCount: 'infinite',
},
{
/* keyframes */
},
)
The second parameter are the waypoints of a @keyframes at-rule in CSS object format. The keyframes helper can be used the create waypoints.
The result of animation
can be used within css
:
css(bounce, {
/* other properties */
})
css`
${bounce}
`
keyframes
function
The This function provides a simple way to define keyframes for use within an animation
function call.
import { keyframes } from 'twind/css'
const bounce = keyframes({
'from, 20%, 53%, 80%, to': {
transform: 'translate3d(0,0,0)',
},
'40%, 43%': {
transform: 'translate3d(0, -30px, 0)',
},
'70%': {
transform: 'translate3d(0, -15px, 0)',
},
'90%': {
transform: 'translate3d(0, -4px, 0)',
},
})
Template literal syntax is supported as well:
const bounce = keyframes`
from, 20%, 53%, 80%, to {
${apply`transform-gpu translate-x-0`}
}
40%, 43% {
${apply`transform-gpu -translate-x-7`}
}
70% {
${apply`transform-gpu -translate-x-3.5`}
},
90% {
${apply`transform-gpu -translate-x-1`}
}
`
The returned values can be used like this:
// As second parameter for animation
animation('1s ease infinite', bounce)
// Within CSS functions
css({
animation: '1s ease infinite',
animationName: bounce,
})
Global styles
The css
function provided by the twind/css
module provides an easy way to inject global styles into your app using the :global
selector.
import { tw } from 'twind'
import { css } from 'twind/css'
const globalStyles = css({
':global': {
a: {
color: '#333',
},
},
})
document.getElementById('app').innerHTML = `
<div class=${tw(globalStyles)}>
<h1>Hello Twind!</h1>
<p>
Look
<a href="https://twind.dev">here</a>
for more info about Twind.
</p>
</div>
`
You can use the theme
function to apply theme values, the apply
function to apply Twind classes, and much more.
import { tw } from 'twind'
import { css, apply, theme } from 'twind/css'
const globalStyles = css({
':global': {
a: {
color: theme('colors.blue.500'),
'&:hover': apply`text-blue-700`,
},
},
})
document.getElementById('app').innerHTML = `
<div class=${tw(globalStyles)}>
<h1>Hello Twind!</h1>
<p>s
Look
<a href="https://twind.dev">here</a>
for more info about Twind.
</p>
</div>
`
You have the full power of the twind/css
module when writing global styles. This approach also allows you to inject global styles anywhere in your app, which is useful for multi-page apps.