Skip to content

Modifiers

Modifiers wrap a StyleRule with a pseudo-class selector or media query. Use them as tw chain properties or with when().

// tw chain syntax
tw.hover.bg('blue-600')
tw.md.p(8)
tw.hover(tw.bg('blue-600').textColor('white'))
// when() syntax
cx(when(hover)(bg('blue-600')))
cx(when(hover, md)(bg('blue-700'))) // stacked: hover inside md

ModifierSelectorCommon use
hover:hoverButton/link color changes
focus:focusInput focus states
active:activeButton press effect
disabled:disabledDisabled form controls
focusVisible:focus-visibleKeyboard-only focus rings
focusWithin:focus-withinParent highlight on child focus
firstChild:first-childRemove top margin/border on first item
lastChild:last-childRemove bottom margin/border on last item
// tw syntax
tw.bg('blue-500')
.hover.bg('blue-600')
.active.bg('blue-700')
.focusVisible.ring('2px', '#3b82f6')
.disabled.opacity(0.5)
// cx syntax
cx(
bg('blue-500'),
when(hover)(bg('blue-600')),
when(disabled)(opacity(0.5), cursor('not-allowed')),
when(focusVisible)(ring('2px', '#3b82f6')),
)

Mobile-first min-width approach:

ModifierBreakpointMedia query
sm640px@media (min-width: 640px)
md768px@media (min-width: 768px)
lg1024px@media (min-width: 1024px)
xl1280px@media (min-width: 1280px)
_2xl1536px@media (min-width: 1536px)

Desktop-first max-width approach:

ModifierBreakpointMedia query
maxSm640px@media (max-width: 639px)
maxMd768px@media (max-width: 767px)
maxLg1024px@media (max-width: 1023px)
maxXl1280px@media (max-width: 1279px)
max2xl1536px@media (max-width: 1535px)
// tw syntax -- progressive enhancement
tw.flexCol.gap(4).p(4)
.sm.gap(6)
.md.flexRow
.lg.p(8)
// cx syntax
cx(
flexCol(), gap(4), p(4),
when(sm)(gap(6)),
when(md)(flexRow()),
when(lg)(p(8)),
)
tw.grid(1).gap(4)
.sm.grid(2)
.md.grid(3).md.gap(6)
.lg.grid(4).lg.gap(8)

ModifierMedia query
dark@media (prefers-color-scheme: dark)

Responds to the user’s system preference. For manual theme toggling, use the Theme API.

tw.bg('white').textColor('gray-900')
.dark(tw.bg('slate-900').textColor('slate-100'))
// With hover in dark mode (cx syntax):
cx(
when(hover)(bg('gray-100')),
when(dark, hover)(bg('slate-700')),
)

Pass multiple modifiers to when() — they apply right-to-left:

when(hover, md)(bg('blue-700'))
// @media (min-width: 768px) { .cls:hover { background-color: ... } }
when(dark, md)(bg('slate-800'))
// @media (min-width: 768px) { @media (prefers-color-scheme: dark) { ... } }

Since Modifier is (rule: StyleRule) => StyleRule, you can create your own:

import { wrapWithSelector, wrapWithMediaQuery } from 'typewritingclass/rule'
const checked: Modifier = (rule) => wrapWithSelector(rule, ':checked')
const print: Modifier = (rule) => wrapWithMediaQuery(rule, 'print')
const reducedMotion: Modifier = (rule) =>
wrapWithMediaQuery(rule, '(prefers-reduced-motion: reduce)')

Custom modifiers compose with when() and all built-in modifiers. See Writing Modifiers for full details.