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" → ignoredThe 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 (paddingLeft → padding-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 shortcutsauto,full(100%),half(50%),screen-w,screen-h.parseNumber(val)— bare numeric values forz-indexand similar.parsePercent01(val)—0–100integers, divided by 100. Used byopacity-*.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