Writing styles
The class-string grammar is Beam's public API. It's small and regular: utilities, variant groups, utility groups, and four kinds of values.
Variant grouping
The signature feature. Factor any repeated variant prefix out of a class string with variant:(utilities). Both forms compile to identical atomic CSS - the group is author-time sugar that never reaches the browser.
<!-- without grouping -->
<nav class="hover:bg-accent hover:text-on-accent hover:scale-105 hover:shadow-lg">
<!-- with grouping -->
<nav class="hover:(bg-accent text-on-accent scale-105 shadow-lg)">Stacking
Chain variants with : - all conditions must hold. Read outer → inner.
<!-- at tablet breakpoint AND on hover -->
<div class="tablet:hover:(bg-surface scale-105)">
<!-- dark mode AND focused -->
<input class="dark:focus:(bg-surface border-accent)">Nesting
Groups can contain further groups:
<section class="tablet:(
direction-row
justify-between
align-center
hover:(bg-surface+8 scale-[1.02])
)">Unfolds to tablet:direction-row, tablet:justify-between, tablet:align-center, tablet:hover:bg-surface+8, tablet:hover:scale-[1.02].
Responsive & arbitrary selectors
Breakpoint names from tokens.screens become variant prefixes. Any CSS selector works too, written in square brackets with & as the subject.
<div class="direction-column tablet:(direction-row gap-8) desktop:gap-12">
<!-- target child SVGs -->
<span class="[&>svg]:(w-[1rem] h-[1rem] text-muted)">
<!-- style based on a sibling checkbox state -->
<label class="[input:checked~&]:(text-accent font-bold)">Utility grouping
Reduce noise for related multi-part declarations. Here the prefix is a CSS property family name, not a variant condition.
<div class="padding:(16 top:24 bottom:24 x:8)"> <!-- p-16 pt-24 pb-24 px-8 -->
<h1 class="text:(xl bold center)"> <!-- text-xl font-bold text-center -->
<div class="border:(1 solid accent)"> <!-- border-1 border-solid border-accent -->Side keys: top/t, right/r, bottom/b, left/l, x, y. Text keys: size:, color:, weight:, align:, leading:, tracking:.
Values
Four kinds, in resolution order:
<!-- numeric: a bare number is always pixels (0 is unitless) -->
p-4 → padding: 4px
w-250 → width: 250px
text-16 → font-size: 16px
<!-- token: a non-numeric value resolves through the token map -->
gap-card → gap: var(--space-card)
bg-surface → background: var(--color-surface)
rounded-md → border-radius: var(--radius-md)
<!-- arbitrary: one-off escape hatch, spaces become underscores -->
w-[347px] h-[100dvh] cols-[200px_1fr] bg-[oklch(72%_0.14_240)]Dynamic values
Write utility-(--var-name) to read a CSS custom property at runtime. The compiled class is stable regardless of the runtime value - no safelists, no dynamic class generation.
import { vars } from 'beamcss'
// the class .w-(--w) is always the same; --w drives the value at runtime
<div className="w-(--w) h-(--h)" style={{ '--w': `${progress}%`, '--h': `${rowH}px` }} />
// vars() is a typed helper: vars({ w: '75%' }) -> { '--w': '75%' }
<div className="w-(--w) bg-accent" style={vars({ w: `${pct}%` })} />Color algebra
Adjust any token color inline. +N lightens, -N darkens (both via color-mix(in oklab, …)), and /N sets alpha.
bg-accent+12 → color-mix(in oklab, var(--color-accent), white 12%)
bg-accent-20 → color-mix(in oklab, var(--color-accent), black 20%)
bg-success/22 → color-mix(in oklab, var(--color-success) 22%, transparent)Next: the Utilities reference.