Skip to content

Recipes & Composites

Recipes are plain functions that combine utilities into reusable design patterns. No new API to learn — just functions.

import { tw } from 'typewritingclass'
const card = tw
.p(6)
.bg.white
.rounded.lg
.shadow.md
.textColor.gray900
// Usage:
<div className={card}>Content</div>
import { tw } from 'typewritingclass'
type ButtonVariant = 'primary' | 'secondary' | 'danger'
type ButtonSize = 'sm' | 'md' | 'lg'
const base = tw
.rounded.lg
.font.semibold
.cursor.pointer
const sizes = {
sm: tw.px(3).py(1).text.sm,
md: tw.px(4).py(2).text.base,
lg: tw.px(6).py(3).text.lg,
} as const
const variants = {
primary: tw
.bg.blue500
.textColor.white
.hover(tw.bg.blue600),
secondary: tw
.bg.white
.textColor.gray700
.border().borderColor.gray300
.hover(tw.bg.gray50),
danger: tw
.bg.red500
.textColor.white
.hover(tw.bg.red600),
} as const
export function button(variant: ButtonVariant = 'primary', size: ButtonSize = 'md') {
return `${base} ${sizes[size]} ${variants[variant]}`
}
<button className={button('primary', 'lg')}>Submit</button>
<button className={button('secondary', 'sm')}>Cancel</button>

Return an object of class strings for components with multiple styled elements:

const dialog = {
overlay: tw
.fixed.inset(0)
.flex.items.center.justify.center
.bg('black/50'),
panel: tw
.bg.white
.rounded.xl
.maxW('32rem').w('full')
.shadow.xl,
header: tw
.px(6).py(4)
.borderB().borderColor.gray200
.textColor.gray900
.font.semibold,
body: tw
.p(6)
.textColor.gray700,
footer: tw
.px(6).py(4)
.borderT().borderColor.gray200
.flex.justify.end.gap(2),
}
<div className={dialog.overlay}>
<div className={dialog.panel}>
<div className={dialog.header}>Title</div>
<div className={dialog.body}>Content</div>
<div className={dialog.footer}>Actions</div>
</div>
</div>

Accept additional styles from the consumer:

import { cx, p, bg, rounded, shadow, textColor } from 'typewritingclass'
export function card(...extra: (StyleRule | string)[]) {
return cx(p(6), bg.white, rounded.lg, shadow.sm, textColor.gray900, ...extra)
}
// Consumer overrides:
<div className={card(when(hover)(shadow.lg))}>Enhanced</div>

Use dcx() and dynamic() for runtime values:

import { dcx, bg, textColor, p, rounded, dynamic } from 'typewritingclass'
export function colorSwatch(color: string) {
return dcx(bg(dynamic(color)), textColor.white, p(4), rounded.lg)
}
function Swatch({ color }: { color: string }) {
const { className, style } = colorSwatch(color)
return <div className={className} style={style}>{color}</div>
}
PatternWhen to use
Simple recipe (returns string)Single styled element
Multi-element (returns object)Multiple styled children
With variants (enum params)Visual variants (primary, secondary)
With ...extra (accepts rules)Consumers need to extend
Dynamic (uses dcx)Runtime-determined values