Start here

@hulla/style

Define class variants once and infer the component props from their keys, in any framework.

On this page

@hulla/style combines class names and gives components named choices such as small or large. It works with your existing CSS and framework.

Edit the source. One processor drives all three previews.

style.tsEditable
import { style } from '@hulla/style'
import { cn as mergeClasses } from 'cn'

const { cn, vn } = style({ composer: mergeClasses })

const $tone = vn({
  accent: 'text-primary-text',
  muted: 'text-muted-foreground',
})
const $space = vn({ compact: ['px-2', 'px-4'] })

export const compose = (extra = '') =>
  cn('py-2', $tone('accent'), $space('compact'), extra)

export const preview = {
  notice: compose(),
  activity: compose('font-semibold'),
  cell: compose('px-6'),
}

export const trace = {
  variant: $space('compact'),
  override: compose('px-6'),
}

Live preview

Up to date

Notice

Your deployment is ready.

Activity

Production updated by Ada

Table cell · local px-6 override

Release v2.0 · healthy

variantpx-4

overridepy-2 text-primary-text px-6

Uses the installed @hulla/style package and this site’s CSS.

Create the helpers once, then pass classes and conditions to cn():

TypeScript
import { style } from '@hulla/style'

const { cn } = style()
const isActive = true

cn('button', { active: isActive }) // 'button active'

Use vn() to map names to classes. For Tailwind conflict resolution, configure a custom composer.

If you need to…Start here
Install the package and create shared helpersQuick start
Type a component prop from a finite set of stylesVariants
Combine shared classes, variants, and caller classesComposition
Choose exact-token deduplication or Tailwind-aware mergingCustom merging

For accepted inputs, signatures, and serializer behavior, see the API reference.