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'
export function card() {
return tw.p(6).bg('white').rounded('lg').shadow('md').textColor('gray-900').toString()
}
// 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('blue-500').textColor('white').hover.bg('blue-600'),
secondary: tw.bg('white').textColor('gray-700').border().borderColor('gray-300').hover.bg('gray-50'),
danger: tw.bg('red-500').textColor('white').hover.bg('red-600'),
} 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:

export function dialog() {
return {
overlay: tw.fixed.inset(0).flex.items('center').justify('center')
.bg('black/50').toString(),
panel: tw.bg('white').rounded('xl').maxW('32rem').w('full')
.shadow('xl').toString(),
header: tw.px(6).py(4).borderB().borderColor('gray-200')
.textColor('gray-900').font('semibold').toString(),
body: tw.p(6).textColor('gray-700').toString(),
footer: tw.px(6).py(4).borderT().borderColor('gray-200')
.flex.justify('flex-end').gap(2).toString(),
}
}
const d = dialog()
<div className={d.overlay}>
<div className={d.panel}>
<div className={d.header}>Title</div>
<div className={d.body}>Content</div>
<div className={d.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('gray-900'), ...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