npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

jd-wind

v1.0.1

Published

Lightweight runtime Tailwind-style CSS engine — write utility classes, get CSS on the fly.

Downloads

230

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}   ← stacked

Pseudo-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:focus

Dark 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: 12px

Layout / Display

flex  inline-flex  inline-block  inline  block  grid  hidden

Flexbox

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-end

Gap

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-0

Colors

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-transparent

Shades 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     ← 0

Typography

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-normal

Overflow

overflow-hidden  overflow-auto  overflow-scroll  overflow-visible
overflow-x-auto  overflow-y-auto

Transition

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 effect

Transform

scale-{n}         ← n/100  (scale-50 → 0.5)
rotate-{n}        ← {n}deg  (negative: rotate--45)
translate-x-{n}   ← {n}px
translate-y-{n}   ← {n}px

Cursor

cursor-pointer  cursor-default  cursor-not-allowed  cursor-wait  cursor-grab

Misc

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-only

Arbitrary 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

  1. 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"], utility bg-indigo-500.
  2. 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.
  3. Variant tokens wrap the rule: pseudo-class variants append to the selector (:hover), dark prepends .chai-dark, breakpoints wrap in @media.
  4. The CSS-escaped original class name is used as the selector, so the class you write in HTML matches exactly.
  5. Duplicate class names across the input array are deduplicated before processing.

License

MIT