Getting Started
Migration
TODO with file diffs
from tailwindcss
from twind v0.16
Breaking Changes
-
the main package is @twind/core
-
@twind/core does not include any core utilities — use one or more of the presets
-
no more
twind/shim
—install()
(recommended) andsetup()
automatically observe allclass
attributes -
tw
: only accepts a single string argumenttw('... class names ...')
— for the v0.16 behavior usetx
instead -
css
: only accepts a single CSS object or can be used as tagged template literal- no more
@global
— you must use&
for nested selectors (this follows the CSS Nesting Module) - no more string support for nested selectors — use
@apply
within the CSS object instead (see example on twind.run)
- no more
-
no more important suffix:
rule!
->!rule
-
no more
@screen sm
-> use the tailwindcss syntax@media screen(sm)
-
strict tailwindcss v3 compatibility
- no IE 11 fallbacks (color, box-shadow, ...)
- no more
font-*
andtext-*
shortcuts - no
border-tr
butborder-[xytrbl]*
still exists - no
bg-origin-*
- droped IE 11 support
-
config theme section function has a changed signature
difftheme: { extend: { - fill: (theme) => ({ + fill: ({ theme }) => ({ gray: theme('colors.gray') }) } }
Notable Changes
See reference for a complete list of all available features until we have documentation for all of them.
-
API
- new function
install
to simplify setup setup
can be called as many times as you want.- classes are returned in order they are applied by the browser - last one wins
tw
:- the theme:
tw.theme(...)
- the target sheet:
tw.target
- allows to reset twind (start clean):
tw.clear()
- allows to remove twind (remove the associated style element):
tw.destroy()
- the theme:
apply
finally works — styles are generated in order they are declaredshortcut
— styles are generated as defined by twind — same as if they where used alone- with support for creating named shortcuts:
shortcut.PrimaryButton`bg-red-500 text-white`
->PrimaryButton#<hash>
- with support for creating named shortcuts:
- new
cx
function to create class names- grouped rules are ungrouped
- new
tx
function to create class names and inject styles — like usingtw(cx(...))
style
— stitches like component definitions- creates readable class names like
style#1hvn013 style--variant-gray#1hvn013 style--size-sm#1hvn013 style--outlined-@sm-true#1hvn013
- with label:
style({ label: 'button', ... })
button#p8xtwh button--color-orange#p8xtwh button--size-small#p8xtwh button--color-orange_outlined-true$0#p8xtwh
- creates readable class names like
- new function
-
- allow trailing dash before parentheses for utilities ->
border-(md:{2 black opacity-50 hover:dashed}}
- shortcuts:
~
to apply/merge utilities ->~(text(5xl,red-700),bg-red-100)
- anonymous shortcuts:
~(!text-(3xl center) !underline italic focus:not-italic)
- support comma-separated shortcuts — this prevents different classNames errors during hydration:
hover:~(!text-(3xl,center),!underline,italic,focus:not-italic)
cx()
converts space-separated to comma-separated
- support comma-separated shortcuts — this prevents different classNames errors during hydration:
- named shortcuts:
PrimaryButton~(bg-red-500 text-white)
->PrimaryButton#<hash>
shortcut()
is a helper to simplify creation of shortcuts (works likeapply()
in twind v0.16); it supports creating named shortcuts:shortcut.PrimaryButton`bg-red-500 text-white`
->PrimaryButton#<hash>
- anonymous shortcuts:
- allow trailing dash before parentheses for utilities ->
-
config
-
presets are executed in order they are defined
-
presets can currently not contain other presets — a work-around may by to use
defineConfig()
within the preset -
defineConfig()
helper for typing -
preset merging:
preflight
— last one winstheme
andtheme.extend
are shallow merged — last one winsrules
,variants
, andignorelist
— first one winsdarkMode
,hash
andstringify
are overridden if defined by the preset — last one wins
-
user config merging
preflight
— applied lasttheme
andtheme.extend
are shallow merged — applied lastrules
,variants
, andignorelist
— applied firstdarkMode
,hash
andstringify
are overridden if defined by the preset — applied first
-
darkMode can be selector string
{ darkMode: '.dark-mode &' }
or{ darkMode: 'html[data-theme="dark"] &
}` -
rules based on ideas from UnoCSS
js// 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'], // Some aliases // shortcut to multiple utilities ['card', 'py-2 px-4 font-semibold rounded-lg shadow-md'], // dynamic shortcut ['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 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)`], ], })
There are lots of things possible. See preset-tailwind/rules and preset-ext/rules for more examples.
-
ignorelist: can be used ignore certain rules
This following example matches class names from common libraries:
jsdefineConfig({ // emotion: `css-` // stitches: `c-` // styled-components: `sc-`and `-sc- // svelte: `svelte-` // vanilla-extract: sprinkles_ // goober: `go1234567890` // DO NOT IGNORE rules starting with `^[~#]`, `^css#`, or `^style[~#-]` — these may have been generated by `css()` or `style()`, or are hashed ignorelist: /^((css|s?c|svelte)-|(sprinkles)?_|go\d)|-sc-/, })
-
no implicit ordering within preflight
-
-
comments (single and multiline)
-
styles (the generated CSS rules) are sorted predictably and stable — no matter in which order the rules are injected
-
support
label
for a more readable class names (https://emotion.sh/docs/labels) -
support theme(...) in property and arbitrary values
-
@apply finally works as expected
-
full support for color functions:
primary: ({ opacityVariable, opacityValue }) => ...
-
new
@layer
directive following the Cascade Layers (CSS @layer) specThe following layer exist in the given order:
defaults
,base
,components
,shortcuts
,utilities
,overrides
jsimport { injectGlobal } from '@twind/core' injectGlobal` /* rules with base are not sorted */ h1 { @apply text-2xl; } h2 { @apply text-xl; } /* ... */ @layer components { .select2-dropdown { @apply rounded-b-lg shadow-md; } .select2-search { @apply border border-gray-300 rounded; } .select2-results__group { @apply text-lg font-bold text-gray-900; } /* ... */ } `