jd-wind
v1.0.1
Published
Lightweight runtime Tailwind-style CSS engine — write utility classes, get CSS on the fly.
Downloads
230
Maintainers
Readme
chai-Wind
A lightweight, zero-dependency utility-class CSS generator. Feed it a list of prefixed class names, get back a CSS string. No build step, no config file required.
Installation
# copy generate.js into your project, then import it directly
import { generateCSS } from "./generate.js";Quick start
import { generateCSS } from "./generate.js";
const css = generateCSS([
"chai-flex",
"chai-items-center",
"chai-gap-16",
"chai-p-24",
"chai-bg-indigo-600",
"chai-text-white",
"chai-rounded-8",
"chai-hover:bg-indigo-700",
"chai-md:flex-col",
]);
// inject into the page
const style = document.createElement("style");
style.textContent = css;
document.head.appendChild(style);Apply in HTML exactly as written:
<div
class="chai-flex chai-items-center chai-gap-16 chai-p-24
chai-bg-indigo-600 chai-text-white chai-rounded-8
chai-hover:bg-indigo-700 chai-md:flex-col"
>
Hello
</div>API
generateCSS(classes, options?)
| Parameter | Type | Default | Description |
| ---------------- | ---------- | --------- | ----------------------------------------- |
| classes | string[] | — | List of prefixed class names to compile |
| options.prefix | string | "chai-" | Class prefix to strip before resolving |
| options.minify | boolean | false | Collapse whitespace in output |
| options.theme | object | {} | Theme overrides (see Theming) |
Returns a string of CSS declarations. Duplicate class names are silently deduplicated.
Variants
Variants are colon-separated prefixes on any utility. They can be stacked in any order.
chai-{variant}:{utility}
chai-{variant}:{variant}:{utility} ← stackedPseudo-class variants
| Variant | Selector suffix |
| --------------- | ---------------- |
| hover | :hover |
| focus | :focus |
| focus-visible | :focus-visible |
| active | :active |
"chai-hover:bg-indigo-500"; // .chai-hover\:bg-indigo-500:hover
"chai-focus:border-blue-500"; // .chai-focus\:border-blue-500:focusDark mode
The dark variant wraps the rule inside .chai-dark. Toggle dark mode by adding chai-dark to any ancestor element (e.g. <html>).
"chai-dark:bg-gray-900";
"chai-dark:text-white";<html class="chai-dark">
<body class="chai-dark:bg-gray-900 chai-dark:text-white">
…
</body>
</html>Responsive variants
| Variant | Min-width |
| ------- | --------- |
| sm | 640px |
| md | 768px |
| lg | 1024px |
| xl | 1280px |
| 2xl | 1536px |
"chai-md:flex-col";
"chai-lg:grid-cols-3";Stacked variants
Pseudo, dark, and responsive can be combined freely:
"chai-dark:hover:bg-gray-700";
"chai-md:hover:text-indigo-400";
"chai-dark:md:bg-gray-950";Utilities reference
Spacing — p, m
Values are raw pixels.
p-{n} mt-{n} mx-{n} py-{n} pl-{n} pr-{n}"chai-p-16"; // padding: 16px
"chai-mt-8"; // margin-top: 8px
"chai-mx-24"; // margin-left: 24px; margin-right: 24px
"chai-py-12"; // padding-top: 12px; padding-bottom: 12pxLayout / Display
flex inline-flex inline-block inline block grid hiddenFlexbox
flex-row flex-col flex-row-reverse flex-col-reverse
flex-wrap flex-nowrap
flex-1 flex-auto flex-none flex-grow flex-shrink
items-start items-center items-end items-stretch
justify-start justify-center justify-end justify-between justify-around
self-start self-center self-endGap
gap-{n} gap-x-{n} gap-y-{n}Grid
grid-cols-{n} grid-rows-{n}
col-span-{n} row-span-{n}Sizing
w-{n} h-{n} ← raw pixels
w-full h-full ← 100%
w-screen h-screen ← 100vw / 100vh
w-1/2 w-1/3 … ← fractions
min-h-screen min-h-full
max-h-screen max-h-full
max-w-full max-w-none
min-w-{n} max-w-{n} min-h-{n} max-h-{n}Position
relative absolute fixed sticky
top-{n} right-{n} bottom-{n} left-{n}
top-full left-auto …
inset-0Colors
bg-{color}-{shade} text-{color}-{shade} border-{color}-{shade}
bg-{color} text-{color} ← shade 500
bg-white bg-black bg-transparent
text-white text-black text-transparentShades available on every color: 50 100 200 300 400 500 600 700 800 900 950
Built-in colors
| Group | Colors |
| -------- | --------------------------------------- |
| Reds | red orange |
| Yellows | amber yellow |
| Greens | lime green emerald |
| Teals | teal cyan |
| Blues | sky blue indigo |
| Purples | violet purple fuchsia |
| Pinks | pink rose |
| Neutrals | slate gray zinc neutral stone |
| Custom | taupe mauve mist olive |
"chai-bg-indigo-600";
"chai-text-gray-200";
"chai-border-red-400";
"chai-bg-emerald-500";
"chai-text-rose-300";
"chai-bg-slate-900";
"chai-border-violet-400";
"chai-bg-taupe-200";
"chai-text-mist-600";
"chai-bg-white";Border
border ← 1px solid
border-{n} ← {n}px solid
rounded ← 4px radius
rounded-{n} ← {n}px radius
rounded-full ← 9999px
rounded-none ← 0Typography
text-xs text-sm text-base text-lg text-xl text-2xl text-3xl text-4xl
font-light font-normal font-medium font-semibold font-bold
italic not-italic
underline no-underline line-through
uppercase lowercase capitalize normal-case
text-left text-center text-right text-justify
leading-none leading-tight leading-snug leading-normal leading-relaxed leading-loose
tracking-tight tracking-normal tracking-wide tracking-wider
truncate whitespace-nowrap whitespace-normalOverflow
overflow-hidden overflow-auto overflow-scroll overflow-visible
overflow-x-auto overflow-y-autoTransition
transition ← common properties, 150ms ease
transition-all
transition-colors
transition-opacity
transition-transform
transition-none
duration-{n} ← transition-duration: {n}ms
delay-{n} ← transition-delay: {n}ms
ease-linear ease-in ease-out ease-in-out"chai-transition";
"chai-duration-300";
"chai-hover:bg-indigo-700"; // combine with hover for smooth effectTransform
scale-{n} ← n/100 (scale-50 → 0.5)
rotate-{n} ← {n}deg (negative: rotate--45)
translate-x-{n} ← {n}px
translate-y-{n} ← {n}pxCursor
cursor-pointer cursor-default cursor-not-allowed cursor-wait cursor-grabMisc
opacity-{n} ← n/100 (opacity-50 → 0.5)
z-{n}
pointer-events-none
select-none select-text select-all
appearance-none
box-border box-content
sr-onlyArbitrary values
Any supported property accepts a bracket value:
"chai-w-[320px]";
"chai-h-[calc(100vh-64px)]";
"chai-bg-[#1a1a2e]";
"chai-text-[clamp(1rem,2vw,1.5rem)]";
"chai-top-[env(safe-area-inset-top)]";
"chai-gap-[1.5rem]";
"chai-rounded-[50%]";
"chai-opacity-[0.35]";
"chai-z-[999]";Supported arbitrary prefixes: w h p m bg text border gap rounded opacity z top right bottom left min-w max-w min-h max-h
Theming
Pass a theme object to extend or override any defaults. Existing keys are merged — you only need to supply what changes.
const css = generateCSS(classes, {
theme: {
palette: {
// add a new color
brand: {
50: "#fdf4ff",
500: "#a855f7",
900: "#581c87",
},
// override an existing shade
blue: {
...existingBlue,
500: "#0ea5e9",
},
},
fontSizes: {
"5xl": "48px",
"6xl": "60px",
},
breakpoints: {
// replace all breakpoints
tablet: "768px",
desktop: "1200px",
},
},
});After adding brand to the palette, all color utilities work automatically:
<div class="chai-bg-brand-500 chai-text-brand-50 chai-hover:bg-brand-900">
…
</div>Options reference
generateCSS(classes, {
prefix: "chai-", // strip this prefix before resolving each class
minify: false, // when true, output has no extra whitespace
theme: {}, // palette / fontSizes / breakpoints overrides
});Custom prefix
const css = generateCSS(["ui-flex", "ui-bg-indigo-500"], { prefix: "ui-" });Minified output
const css = generateCSS(classes, { minify: true });
// .chai-flex { display:flex; } .chai-bg-indigo-500 { background:#6366f1; } …How it works
- Each class name has its prefix stripped and any leading variant tokens split off at colons:
chai-dark:hover:bg-indigo-500→ variants["dark","hover"], utilitybg-indigo-500. - The utility is passed through a handler chain (arbitrary → spacing → layout → gap → grid → colors → border → typography → size → position → transition → misc). First match wins — a class can only ever produce one set of declarations.
- Variant tokens wrap the rule: pseudo-class variants append to the selector (
:hover),darkprepends.chai-dark, breakpoints wrap in@media. - The CSS-escaped original class name is used as the selector, so the class you write in HTML matches exactly.
- Duplicate class names across the input array are deduplicated before processing.
License
MIT
