Skip to content

Class parsing

Every utility class flows through the same three-step pipeline.

1 — Strip the prefix

The default prefix is chai-. Anything that doesn’t start with the prefix is left alone — it’s not chaiTailwind’s business.

"chai-px-4" → prefix: "chai-", token: "px-4"
"my-class" → ignored

The prefix is configurable: data-chai-prefix="cw-" or new Chai({ prefix: 'cw-' }).

2 — Try rules in order

The token (px-4) is fed to each rule until one matches. Rule order matters — more-specific rules come first so they aren’t shadowed.

// from src/chai.js (paraphrased)
staticRule('flex', { display: 'flex' }),
staticRule('flex-col', { flexDirection: 'column' }), // more specific, but the
// static-rule check is exact-match,
// so order is just hygiene.
patternRule(/^p-(.+)$/, (m) => ({ padding: parseLength(m[1]) })),
patternRule(/^px-(.+)$/, (m) => ({
paddingLeft: parseLength(m[1]),
paddingRight: parseLength(m[1])
})),

A rule returns either a style object (camelCase keys, string values) or null. The first non-null result wins.

3 — Apply the styles

The matched style object is merged with results from this element’s other chai-* classes, then each property is written via element.style.setProperty(...).

CamelCase keys are converted to kebab-case at write time (paddingLeftpadding-left).

Value parsers

Three helpers handle the right-hand side of every utility:

  • parseLength(val) — accepts integers (treated as px), values with explicit units (1.5rem, 50%, 100vh), and the keyword shortcuts auto, full (100%), half (50%), screen-w, screen-h.
  • parseNumber(val) — bare numeric values for z-index and similar.
  • parsePercent01(val)0–100 integers, divided by 100. Used by opacity-*.
  • parseColor(val, theme) — names from the palette, or arbitrary values.

All four also accept arbitrary values wrapped in [...].

Arbitrary values

Anything wrapped in square brackets is passed through (with _ decoded as a space, since classes can’t contain whitespace).

<div class="chai-p-[1.5rem]
chai-bg-[#ffeedd]
chai-w-[calc(100%-2rem)]
chai-shadow-[0_4px_24px_rgba(0,0,0,.15)]">
</div>

Underscores are an escape hatch: chai-shadow-[0_4px_24px_rgba(0,0,0,.15)]0 4px 24px rgba(0,0,0,.15).

What happens on a no-match

If no rule matches the token, it’s simply ignored — the class stays on the element (because we only remove matched classes), and chaiTailwind moves on to the next class. With data-chai-warn="true", you’ll see a console warning so you can spot typos.

Conflict resolution

When two chai-* classes set the same property, the later one in the class list wins. There’s no “important” or specificity layer — this is just plain object-merge order.

<!-- background-color is red-500, not blue-500 -->
<div class="chai-bg-blue-500 chai-bg-red-500"></div>

Made by Saad · @developedbysaad on X