Core Concepts
Reference
Lang
-
comments like in CSS:
/* ... */
-
group~{name}-{modifier}
andpeer~{name}-{modifier}
Name can contain any characters except whitespace, parenthesis (
(
and)
), colon (:
), dash (-
), and opening bracket ([
).html<div class="group~project bg-white hover:bg-blue-500 ..."> <p class="text-gray-900 group~project-hover:text-white ...">New Project</p> <div class="group~create bg-gray-100 hover:bg-green-500 ..."> <p class="text-gray-500 group~create-hover:text-white ..."> Create a new project from a variety of starting templates. </p> </div> </div>
-
attribute selector as modifier for groups and peers
html<div class="group bg-white hover:bg-blue-500 ..."> <p class="text-gray-900 group[disabled]:text-gray-200 ...">Project Name</p> </div>
With named groups/peers:
html<div class="group~project bg-white hover:bg-blue-500 ..."> <p class="text-gray-900 group~project[disabled]:text-gray-200 ...">Project Name</p> </div>
CSS (strings and objects)
&
: for nested selectors (CSS Nesting Module)label
: for a more readable class names Emotion › Labelstheme(...)
: access theme values using dot notation; can be used in arbitrary values as well (Tailwind CSS › Functions & Directives › theme())@layer ...
: tell Twind which bucket a set of custom styles belong to (Tailwind CSS › Functions & Directives › layer & Cascade Layers (CSS @layer) spec)- The following layer exist in the given order:
defaults
,base
,components
,shortcuts
,utilities
,overrides
- The following layer exist in the given order:
@apply
: inline any existing utility classes (Tailwind CSS › Functions & Directives › apply)@media screen(...)
: create media queries that reference breakpoints by name instead of duplicating their values (Tailwind CSS › Functions & Directives › screen())
API
The following functions are all exports from twind
.
If you are using the script
tag these methods are available via the twind
global object (eg twind.setup
).
If you have used Tailwind or other CSS-in-JS solutions, then most of the API should feel very familiar.
Shim Mode
Observe class attributes to inject styles
install(config, isProduction): Twind
: configures the globaltw
instance, observes all class attributes and inject the styles into the DOM; returns a twind instancesetup(config, sheet?, target?): Twind
: configures the globaltw
instance, observes all class attributes and inject the styles into the DOM; returns a twind instancetw
: the global twind instance updated by eachsetup
call
Library Mode
twind(config)
: create a twind instanceobserve(tw, target?)
: observes all class attributes and injects the styles into the DOM
Twind instance — tw
-
global twind instance:
import { tw } from '@twind/core'
-
tw(className)
: injects a className string into the sheet and return the resulting class names -
tw.config
: access the current config -
tw.theme(section?, key?, defaultValue?)
: access the current themetw.theme()
: returns the whole themetw.theme(section)
: returns the whole sectiontw.theme(dottedKey, defaultValue?)
: returns the current valuetw.theme(section?, key?, defaultValue?)
: returns the theme value
jstw.theme('colors.blue.500', 'blue')
-
tw.target
: the sheet target of this instance (string[]
,HTMLStyleSheet
,CSSStyleSheet
) -
tw.clear()
: clears all CSS rules from the sheet -
tw.destroy()
: remove the sheet from the document
Utilities
-
defineConfig(config)
: define a configuration object forsetup
ortwind
-
tx(...args)
: creates a class name from the given arguments and injects the styles (liketw(cx(...args))
)tx.bind(tw)
: binds thetx
function to a custom twind instance; returns a newtx
function
jsimport { tx } from '@twind/core' const className = tx`underline bg-red-200 text-red-900`
-
injectGlobal(...args)
: injects the given styles into the base layerinjectGlobal.bind(tw)
: binds theinjectGlobal
function to a custom twind instance; returns a newinjectGlobal
function
jsimport { injectGlobal } from '@twind/core' injectGlobal` @font-face { font-family: "Operator Mono"; src: url("../fonts/Operator-Mono.ttf"); } body { margin: 0; } `
-
keyframes(...args)
: lazily injects the keyframes into the sheet and return a unique namekeyframes.Name(...args)
: lazily injects the named keyframes into the sheet and return a unique namekeyframes.bind(tw)
: binds thekeyframes
function to a custom twind instance; returns a newkeyframes
function
jsimport { keyframes, css, tx } from '@twind/core' const fadeIn = keyframes` 0% { opacity: 0; } 100% { opacity: 1; } ` // using CSS object notation const fadeIn = keyframes({ '0%': { opacity: 0 }, '100%': { opacity: 1 }, }) // within a arbitrary value el.className = `animate-[1s_${fadeIn}_ease-out]` // within CSS const fadeInClass = css` animation: 1s ${fadeIn} ease-out; `
-
animation(animation: string | CSSProperties, waypoints: StringLike)
: lazily injects the animation into the sheet and return a unique nameanimation.Name(animation: string | CSSProperties, waypoints: StringLike)
: lazily injects the named animation into the sheet and return a unique name
jsimport { animation, keyframes } from '@twind/core' const fadeIn = animation( '1s ease-out', keyframes` 0% { opacity: 0; } 100% { opacity: 1; } `, )
Helper functions
These generate class names but do not inject styles.
These can be used to generate class names that are then
a) set the class attribute on an element (Shim Mode)
b) used with tw
to inject styles and return a class name (Library Mode)
-
cx(...args)
: creates a class name from the given arguments; no styles injectedjsimport { cx } from '@twind/core' // Set a className element.className = cx` underline /* multi line comment */ hover:focus:!{ sm:{italic why} lg:-{px} -mx-1 } // Position !top-1 !-bottom-2 text-{xl black} `
-
css(...args)
: creates a class name from the given arguments; no styles injected -
style(options)
: creates a stitches like helper; returns astyle
functionstyle(props)
: creates a class name from the given props; no styles injected
Mostly server side
Used to update an html string with styles.
inline(html, tw? | {tw, minfiy}?)
: updates all class attributes in html string and inject there styles into the head as style element; returns the updated html stringextract(html, tw?)
: updates all class attributes from html string; returns the updated html string and the CSSconsume(html, tw?)
: updates all class attributes from html string; returns the updated html string and injects all styles intotw
Sheets
getSheet(useDOMSheet?: boolean, disableResume?: boolean)
: returns aSheet
for the current environment —virtual
on server, eitherdom
orcssom
in browsersvirtual(includeResumeData?: boolean)
: collect styles into an arraycssom(element?: CSSStyleSheet | Element | null | false)
: uses a fast DOM sheet — bad for debuggingdom(element?: Element | null | false)
: uses a slow DOM sheet — great for debuggingstringify(target)
: returns the CSS string of a sheet target
Config
Dark Mode
defineConfig({
// using media strategy with `@media (prefers-color-scheme:dark)`
darkMode: 'media',
// using class strategy with `.dark`
darkMode: 'class',
// custom selectors
darkMode: '.dark-mode',
darkMode: '[theme=dark]',
})
Auto Dark Colors
If enabled, automatic dark colors are generated for each light color (eg no dark:
variant is present). This feature is opt-in and twind provides a builtin function that works with tailwind color palettes (50
, 100
, 200
, ..., 800
, 900
).
import { autoDarkColor } from '@twind/core'
defineConfig({
// for tailwind color palettes: 50 -> 900, 100 -> 800, ..., 800 -> 100, 900 -> 50
darkColor: autoDarkColor,
// other possible implementations
darkColor: (section, key, { theme }) => theme(`${section}.${key}-dark`) as ColorValue,
darkColor: (section, key, { theme }) => theme(`dark.${section}.${key}`) as ColorValue,
darkColor: (section, key, { theme }) => theme(`${section}.dark.${key}`) as ColorValue,
darkColor: (section, key, context, lightColor) => generateDarkColor(lightColor),
})
Example css for text-gray-900
:
.text-gray-900 {
--tw-text-opacity: 1;
color: rgba(15, 23, 42, var(--tw-text-opacity));
}
@media (prefers-color-scheme: dark) {
.text-gray-900 {
--tw-text-opacity: 1;
color: rgba(248, 250, 252, var(--tw-text-opacity));
}
}
The auto-generated dark color can be overridden by the usual dark:...
variant: text-gray-900 dark:text-gray-100
.
.text-gray-900 {
--tw-text-opacity: 1;
color: rgba(15, 23, 42, var(--tw-text-opacity));
}
@media (prefers-color-scheme: dark) {
.text-gray-900 {
--tw-text-opacity: 1;
color: rgba(248, 250, 252, var(--tw-text-opacity));
}
}
@media (prefers-color-scheme: dark) {
.dark\\:text-gray-100 {
--tw-text-opacity: 1;
color: rgba(241, 245, 249, var(--tw-text-opacity));
}
}
Rules
based on ideas from UnoCSS
// defineConfig is optional but helps with type inference
defineConfig({
rules: [
// Some rules
['hidden', { display: 'none' }],
// Table Layout
// .table-auto { table-layout: auto }
// .table-fixed { table-layout: fixed }
['table-(auto|fixed)', 'tableLayout'],
// dynamic
['table-', (match, context) => /* ... */],
// Some aliases
// shortcut: styles are generated as defined by twind — same as if they where used alone
// shortcut to multiple utilities
['card', 'py-2 px-4 font-semibold rounded-lg shadow-md'],
// dynamic shortcut — `$$` is everything after the match eg `btn-red` -> `red`
['card-', ({ $$ }) => `bg-${$$}-400 text-${$$}-100 py-2 px-4 rounded-lg`],
// single utility alias — need to use `~(...)` as it would be otherwise recognized as a CSS property
['red', '~(text-red-100)'],
// apply: styles are generated in order they are declared
// apply to multiple utilities
['btn-green', '@(bg-green-500 hover:bg-green-700 text-white)'],
// dynamic apply
['btn-', ({ $$ }) => `@(bg-${$$}-400 text-${$$}-100 py-2 px-4 rounded-lg)`],
// Using cx
['highlight(-rounded)?', ({ 1: rounded }) => cx({ 'bg-yellow-200': true, rounded })],
// Using css
[
'target-new-tab',
css`
target-name: new;
target-new: tab;
`,
],
// dynamic
[
'target-new-(tab|window)',
({ 1: $1 }) => css`
target-name: new;
target-new: ${$1};
`,
],
// Using style
// `box?color=coral` -> `.box\\?color\\=coral{background-color:coral}`
// `box?rounded` -> `.box\\?rounded{border-radius:0.25rem}`
// `box?color=coral&rounded` -> `.box\\?color\\=coral\\&rounded{background-color:coral;border-radius:0.25rem}`
// `box?color=purple&rounded=md` -> `.box\\?color\\=purple\\&rounded\\=md{background-color:purple;border-radius:0.375rem}`
[
'box\\?(.+)',
style({
props: {
color: {
coral: css({
backgroundColor: 'coral',
}),
purple: css`
background-color: purple;
`,
},
rounded: {
'': 'rounded',
md: 'rounded-md',
},
},
}),
],
],
})
Hash
hash all shortcuts and apply
defineConfig({
hash(className, defaultHash) {
if (/^[~@]\(/.test(className)) {
// a shortcut like `~(...)`
// an apply like `@(...)`
return defaultHash(className)
}
return className
},
})
add namespace/scope to all classes
defineConfig({
hash(className) {
return `.scoped ${className}`
},
})
Browser Support
In general, Twind is designed for and tested on the latest stable versions of Chrome, Firefox, Edge, and Safari. It does not support any version of IE, including IE 11.
For automatic vendor prefixing include the @twind/preset-autoprefix preset.
For more details see Tailwind CSS › Browser Support.
The following JS APIs may need polyfills:
- Array.flatMap
- Edge<79, Firefox<62, Chrome<69, Safari<12, Opera<56
- polyfill
When using style()
within config.rules
:
- Object.fromEntries
- Edge<79, Firefox<63, Chrome<73, Safari<12.2, Opera<60
- polyfill or @ungap/from-entries
- URLSearchParams
- Edge<17, Firefox<44, Chrome<49, Safari<10.3, Opera<36
- polyfill or @ungap/url-search-params