Skip to content

Custom rules

A rule is a function that takes (token, theme) and returns either a style object or null. The library exposes three ways to register one — all three end up appended to a list of “custom rules” that’s tried before the built-ins.

Function form (most general)

chai.extend((token, theme) => {
if (token === 'spin') {
return { animation: 'spin 1s linear infinite' };
}
return null;
});
<div class="chai-spin"></div>

The returned object uses camelCase property names; the engine kebab-cases them at write time.

Static-rule form

For exact-match utilities, this is more compact:

chai.extend({
test: 'spin',
style: { animation: 'spin 1s linear infinite' }
});

Pattern-rule form

For families of utilities driven by a regex:

chai.extend({
pattern: /^rotate-(\d+)$/,
build: (m) => ({ transform: `rotate(${m[1]}deg)` })
});
<div class="chai-rotate-45"></div>
<div class="chai-rotate-90"></div>

Theme-aware rules

Rules receive the merged theme as the second argument, so you can read scales from it:

chai.extend({
pattern: /^elevation-(\d+)$/,
build: (m, theme) => ({
boxShadow: theme.shadow[m[1]] || `0 ${m[1]}px ${m[1] * 2}px rgba(0,0,0,0.1)`
})
});

Order matters

Rules are stored in registration order. Within the merged list (custom rules first, then built-ins), the first match wins. If you want to override a built-in utility, register your rule before the built-in runs:

const chai = new Chai({
rules: [
// overrides chai-text-center to also apply text-align: justify
(token) => token === 'text-center'
? { textAlign: 'center', textJustify: 'inter-word' }
: null
]
});

Bulk registration

Pass rules: [...] to the constructor to register many at once. This is the recommended pattern when the rule set is part of your design system.

new Chai({
rules: [
{ pattern: /^rotate-(-?\d+)$/, build: (m) => ({ transform: `rotate(${m[1]}deg)` }) },
{ pattern: /^scale-(\d+)$/, build: (m) => ({ transform: `scale(${m[1] / 100})` }) },
{ test: 'no-tap', style: { WebkitTapHighlightColor: 'transparent' } }
]
});

Keeping it tidy

For anything beyond a handful of rules, give them a home of their own:

theme/extras.js
export const transformRules = [
{ pattern: /^rotate-(-?\d+)$/, build: (m) => ({ transform: `rotate(${m[1]}deg)` }) },
{ pattern: /^scale-(\d+)$/, build: (m) => ({ transform: `scale(${m[1] / 100})` }) }
];
// main.js
import { Chai } from 'chaitailwind';
import { transformRules } from './theme/extras.js';
new Chai({ rules: transformRules });

Made by Saad · @developedbysaad on X