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.

React
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.