Reference

Lang

  • grouping variants

    • hover:(underline font-bold) -> hover:underline hover:font-bold
  • grouping utilities

    • text-(sm green-500) -> text-sm text-green-500
  • grouping important

    • !(text-(sm green-500)) -> !text-sm !text-green-500
  • inline apply: styles are generated in order they are declared

    • @(underline font-bold) -> @(underline,font-bold)
    • [email protected](underline font-bold) -> Link#12345
  • inline shortcut: styles are generated as defined by twind — same as if they where used alone

    • ~(underline font-bold) -> ~(underline,font-bold)
    • Link~(underline font-bold) -> Link#abcdef
  • comments like in CSS: /* ... */

  • group~{name}-{modifier} and peer~{name}-{modifier}

    Name can contain any characters except whitespace, parenthesis (( and )), colon (:), dash (-), and opening bracket ([).


    <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


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


    <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)

API

The following functions are all exports from twind.

Shim Mode

Observe class attributes to inject styles

  • setup(config, sheet?, target?): Twind: configures the global tw instance, observes all class attributes and inject the styles into the DOM; returns a twind instance
  • tw: the global twind instance updated by each setup call

Library Mode

use tw or tx to inject styles

  • twind(config, sheet?): Twind: creates a custom twind instance (tw)

    Recommended custom twind pattern:


    import {
    twind,
    getSheet,
    virtual,
    tx as tx$,
    injectGlobal as injectGlobal$,
    keyframes as keyframes$,
    } from 'twind'
    import config from './twind.config'
    export const tw = /* #__PURE__ */ twind(
    config,
    // IS_DEV: `proces.env.NODE_ENV != 'production'` or `import.meta.env.DEV` (vite)
    getSheet(IS_DEV),
    )
    export const tx = /* #__PURE__ */ tx$.bind(tw)
    export const injectGlobal = /* #__PURE__ */ injectGlobal$.bind(tw)
    export const keyframes = /* #__PURE__ */ keyframes$.bind(tw)

  • observe(tw, target?): observes all class attributes and injects the styles into the DOM

Twind instance — tw

  • global twind instance: import { tw } from 'twind'
  • 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 theme
    • tw.theme(): returns the whole theme
    • tw.theme(section): returns the whole section
    • tw.theme(dottedKey, defaultValue?): returns the current value
    • tw.theme(section?, key?, defaultValue?): returns the theme value
  • 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 for setup or twind

  • tx(...args): creates a class name from the given arguments and injects the styles (like tw(cx(...args)))

    • tx.bind(tw): binds the tx function to a custom twind instance; returns a new tx function

    import { tx } from 'twind'
    const className = tx`underline bg-red-200 text-red-900`

  • injectGlobal(...args): injects the given styles into the base layer

    • injectGlobal.bind(tw): binds the injectGlobal function to a custom twind instance; returns a new injectGlobal function

    import { injectGlobal } from 'twind'
    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 name

    • keyframes.Name(...args): lazily injects the named keyframes into the sheet and return a unique name
    • keyframes.bind(tw): binds the keyframes function to a custom twind instance; returns a new keyframes function

    import { keyframes, css, tx } from 'twind'
    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 name

    • animation.Name(animation: string | CSSProperties, waypoints: StringLike): lazily injects the named animation into the sheet and return a unique name

    import { animation, keyframes } from 'twind'
    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 injected
  • css(...args): creates a class name from the given arguments; no styles injected
  • shortcut(...args): creates a class name from the given arguments; order of styles determined by twind; no styles injected
    • `shortcut.Button(...args): creates a named class name from the given arguments; order of styles determined by twind; no styles injected
    • ~(...) or Button~(...): within a token
  • apply(...args): creates a class name from the given arguments; order of styles determined by order in args; no styles injected
    • `apply.Button(...args): creates a named class name from the given arguments; order of styles determined by order in args; no styles injected
    • @(...) or [email protected](...): within a token
    • @apply ... or { '@apply': '...' }: within CSS string or object
  • style(options): creates a stitches like helper; returns a style function
    • style(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 string
  • extract(html, tw?): updates all class attributes from html string; returns the updated html string and the CSS
  • consume(html, tw?): updates all class attributes from html string; returns the updated html string and injects all styles into tw

Sheets

  • getSheet(useDOMSheet?: boolean, disableResume?: boolean): returns a Sheet for the current environment — virtual on server, either dom or cssom in browsers
  • virtual(includeResumeData?: boolean): collect styles into an array
  • cssom(element?: CSSStyleSheet | Element | null | false): uses a fast DOM sheet — bad for debugging
  • dom(element?: Element | null | false): uses a slow DOM sheet — great for debugging
  • stringify(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'
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 (/^[[email protected]]\(/.test(className)) {
// a shortcut like `~(...)`
// an apply like `@(...)`
return defaultHash(className)
}
return 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:

When using style() within config.rules: