rainbowindex 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,122 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.2.0] - 2026-07-16
9
+
10
+ Initial public release. Rainbow Index began as a fork of Tailwind CSS v4; the
11
+ entries below list the capabilities this release adds **on top of** what
12
+ Tailwind CSS v4 provides. Shared functionality (utility classes, variants,
13
+ `@apply`, CSS-first theming, `@source`, arbitrary values, the Vite/PostCSS
14
+ plugins as such) is not repeated here.
15
+
16
+ ### Added — runtime & programmatic API
17
+
18
+ - **`ri()` class merger** — built-in class composition with right-most-wins
19
+ conflict resolution, replacing both `clsx` (conditional composition) and
20
+ `tailwind-merge` (conflict resolution). Understands shorthand property
21
+ claims (`p-4` owns all four padding sides), variant-order canonicalization
22
+ (`sm:hover:` ≡ `hover:sm:`), `!important`, negative utilities, and
23
+ arbitrary properties; bracket-aware tokenization with LRU caching.
24
+ - **`createRi(snapshot)`** — SSR- and multi-tenant-safe merger bound to a
25
+ frozen compilation snapshot, for servers where concurrent requests may
26
+ target different themes in one process. `ri()` emits a throttled
27
+ `[RI-2004]` warning when it detects that situation.
28
+ - **Programmatic compilation API** — `compileProject()`, `createCompiler()`,
29
+ `createCompilationContext()`, `finalizeCompilationContext()`, and
30
+ registration helpers (`registerCustomUtility`, `registerCustomTextSizes`,
31
+ `registerCustomFontFamilies`, `registerColorNames`) as a stable library
32
+ surface for driving compilation and feeding the merge engine.
33
+ - **`safelist()` helper** — a runtime identity-join whose static string
34
+ arguments are extracted by the scanner at build time, so component/icon
35
+ libraries can ship class names that are always emitted.
36
+ - **Package-based safelist auto-discovery** — installed dependencies can
37
+ declare `rainbowindex.safelistSources` globs in their own `package.json`
38
+ to opt their published files into scanning, with no manual `@source`.
39
+ - **Browser entry guard** — importing the PostCSS-plugin default export in a
40
+ browser bundle throws `[RI-2003]` with guidance to use named imports.
41
+
42
+ ### Added — CLI
43
+
44
+ - **`rainbowindex generate-types`** — generates a `rainbowindex-env.d.ts`
45
+ with a `RainbowClass` TypeScript union (colors × stops, spacing, text
46
+ sizes, weights, variants, custom utilities) for editor autocomplete;
47
+ `--strict` drops the string escape hatch; hand-edited files are backed up
48
+ before overwrite.
49
+ - **`rainbowindex preload-fonts`** — emits `<link rel="preload" as="font">`
50
+ tags for resolved font faces.
51
+ - **`rainbowindex init`** — wires Rainbow Index into an existing Vite app:
52
+ detects the package manager, installs the dependency, patches
53
+ `vite.config.*`, and creates or updates the CSS entry.
54
+ - **`rainbowindex create <dir>`** — scaffolds a fresh Vite app (default
55
+ template `react-ts`) with Rainbow Index pre-wired.
56
+ - **`--optimize`** — LightningCSS pass with browser-fallback down-leveling
57
+ in addition to minification.
58
+ - **Atomic output writes** — CSS output is written via temp-file + rename so
59
+ interrupted builds never leave partial files.
60
+
61
+ ### Added — theming directives
62
+
63
+ - **Generative color system (`@color`)** — declare a color as two numbers
64
+ (`brand: 0.18 330;` — chroma + hue) and get a full 19-stop OKLCH palette
65
+ with automatic dark mode via ramp mirroring, tuned for even perceived
66
+ contrast. Also supports explicit values, light/dark pairs, aliases,
67
+ per-color dark-override strategies, and contrast warnings.
68
+ - **Font system (`@font`)** — register font slots from Google Fonts, local
69
+ files, or URLs in CSS; multi-face slots (upright + italic via an
70
+ `italic:` shorthand), system/manual stacks, and metrics-adjusted fallback
71
+ `@font-face` blocks (size-adjust/ascent/descent overrides) for zero
72
+ cumulative layout shift. On-disk font metadata cache governed by
73
+ `RI_OFFLINE`, `RI_FETCH_FONTS`, `RI_CACHE_DIR`, and `RI_FONT_CACHE_TTL`.
74
+ - **`@fluid`** — configurable fluid type/spacing range driving
75
+ `text-fluid-*` and `*-fluid` spacing/inset utilities with curve keywords.
76
+ - **`@rounded` corner shapes** — border-radius tokens with corner-shape
77
+ modifiers and `corner-round/scoop/bevel/notch/square/squircle` (plus
78
+ `corner-[superellipse(N)]`) utilities mapping to the CSS `corner-shape`
79
+ property.
80
+ - **`@animate`** — register named animations with inline `@keyframes`, plus
81
+ compositional enter/exit utilities (`animate-in`/`animate-out` with
82
+ `fade-`, `zoom-`, `spin-`, `blur-`, and `slide-` parts, each driving
83
+ independent CSS variables) and preset accordion/collapsible keyframes.
84
+ - **`@register`** — declare typed custom properties (`@property`-style
85
+ registrations with syntax and initial value) directly from CSS.
86
+ - **`@preflight`** — toggle preflight base styles from CSS.
87
+
88
+ ### Added — class-string syntax
89
+
90
+ - **Variant groups** — `hover:{text-red-500 bg-blue-100 underline}` expands
91
+ at scan time to the repeated-prefix form; groups stack and nest
92
+ (`sm:hover:{…}`, `data-[state=open]:{…}`) with depth and output caps, and
93
+ are also expanded inside `@apply` bodies.
94
+ - **Helper-call-aware scanner** — class literals are extracted from
95
+ `clsx`/`cn`/`classnames`/`cx`/`twJoin`/`twMerge`/`cva`/`tv`/`classMap`/
96
+ `safelist` call sites rather than treating source files as flat text.
97
+ - **Math-operator value parsing** — CSS math functions (`calc`, `min`,
98
+ `max`, `clamp`, `mod`, trigonometric functions, `pow`, `round`, …) are
99
+ recognized inside utility values.
100
+
101
+ ### Added — integrations
102
+
103
+ - **Vite plugin extras** — auto-injects a PostCSS config only when none
104
+ exists, auto-discovers the CSS entry on first dev-server listen (warning
105
+ `[RI-1602]` if Rainbow Index never activates), tracks per-file versions
106
+ for HMR, and pre-rewrites Rainbow-Index-specific directive syntax into
107
+ PostCSS-parseable form before PostCSS runs.
108
+ - **PostCSS plugin `sources` option** — declare scan globs from
109
+ `postcss.config.js` in addition to CSS `@source`, merged with
110
+ package-discovered safelist sources.
111
+
112
+ ### Added — diagnostics
113
+
114
+ - **`RI-NNNN` error-code system** — every warning carries a namespaced code
115
+ (10xx compilation, 11xx color, 12xx fonts, 13xx merge, 14xx scanner, 15xx
116
+ typography, 16xx integrations, 20xx CSS functions, 21xx runtime), with a
117
+ parity test enforcing that every emitted code is documented.
118
+ - **Warning deduplication and severity budgeting** — warnings are deduped
119
+ and capped per compile, with reserved slots so high-severity errors are
120
+ never drowned out by informational warnings.
121
+ - **"Did you mean" suggestions** — optimal-string-alignment matching powers
122
+ typo hints for unknown utilities, variants, and theme variables.
@@ -1,12 +1,12 @@
1
1
  MIT License
2
2
 
3
- This project is a fork of Tailwind CSS v4.
3
+ This project is a fork of Tailwind CSS v4. Portions of this software are derived from Tailwind CSS: Copyright (c) Tailwind Labs, Inc.
4
4
 
5
- Portions of this software are derived from Tailwind CSS: Copyright (c) Tailwind Labs, Inc.
5
+ The class merging algorithm is derived from tailwind-merge: Copyright (c) 2021 Dany Castillo
6
6
 
7
7
  The animation system is inspired by tw-animate-css: Copyright (c) 2025 Wombosvideo (Luca Bosin)
8
8
 
9
- Rainbow Index modifications and additions: Copyright (c) 2026 Milo Tech Forecast
9
+ Rainbow Index Copyright (c) 2026 Milo Tech Forecast
10
10
 
11
11
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
12
12
  documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
package/README.md CHANGED
@@ -1,192 +1,329 @@
1
- # rainbowindex
1
+ <p align="center">
2
+ <a href="https://rainbowindex.dev" target="_blank">
3
+ <picture>
4
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/rainbowindex/rainbowindex/HEAD/.github/logo-dark.svg">
5
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/rainbowindex/rainbowindex/HEAD/.github/logo-light.svg">
6
+ <img alt="Rainbow Index" src="https://raw.githubusercontent.com/rainbowindex/rainbowindex/HEAD/.github/logo-light.svg" width="144" height="41" style="max-width: 100%;">
7
+ </picture>
8
+ </a>
9
+ </p>
2
10
 
3
- The core CSS compiler for Rainbow Index. This package handles parsing, transformation, and generation—everything needed
4
- to turn utility classes into CSS.
11
+ **Rainbow Index** is a CSS-first system for building and maintaining consistent user interfaces.
5
12
 
6
- The compiler has zero runtime dependencies and uses a custom CSS parser. Most projects will interact with Rainbow Index
7
- through the [Vite plugin](../vite) or [CLI](../cli), but direct access to the compiler is useful for building custom
8
- tooling or understanding how the system works.
13
+ The project began as a fork of [Tailwind CSS](https://github.com/tailwindlabs/tailwindcss), then diverged with clear intent: to treat CSS as the primary source of truth for styling decisions and to provide tooling that translates design system definitions into predictable, inspectable output. Styling behavior is explicit and traceable, allowing developers to understand not only what is happening, but why.
9
14
 
10
- ## Installation
15
+ We built the system around composable primitives rather than finished components. Instead of prescribing layout patterns or UI components, it focuses on utilities, directives, and tokens that can be combined without accumulating configuration debt.
11
16
 
12
- ```bash
13
- npm install rainbowindex
14
- ```
17
+ Rainbow Index challenges several assumptions common in modern styling workflows. Configuration does not default to JavaScript. Flexibility is not achieved through layered indirection. Utility-based systems are not assumed to be verbose, opaque, or fragile.
18
+
19
+ The product is not a visual design tool, a component library, or a framework abstraction layer. It assumes familiarity with core CSS concepts and expects users to engage with the underlying model.
20
+
21
+ When tradeoffs arise, we consistently prioritize composability, user control, predictable performance, explicit behavior, and correctness over convenience or familiarity.
15
22
 
16
- ## Usage
23
+ ## Install
17
24
 
18
- ### Compile Classes
25
+ ```sh
26
+ pnpm add rainbowindex
27
+ ```
28
+
29
+ Requires Node `>=20.19`. The package is **ESM-only** — there is no CommonJS build, so `require("rainbowindex")` is only supported on runtimes that can `require()` ES modules (Node 20.19+); use `import` otherwise (e.g. an ESM `postcss.config.js`/`.mjs`). `postcss` is a required peer dependency, `vite` an optional one; `lightningcss`, `chokidar`, and `tinyglobby` are bundled as direct deps.
19
30
 
20
- The most common operation: turn a list of utility class names into CSS.
31
+ ## Quick start (Vite)
21
32
 
22
- ```typescript
23
- import { compileClasses, createDesignSystem } from "rainbowindex";
33
+ The fastest path is the Vite plugin, which auto-injects PostCSS config and discovers your CSS entry on first dev-server listen.
24
34
 
25
- const designSystem = createDesignSystem();
35
+ ```ts
36
+ // vite.config.ts
37
+ import { defineConfig } from "vite";
38
+ import rainbowindex from "rainbowindex/vite";
26
39
 
27
- const result = compileClasses(["p-4", "bg-blue-500", "hover:bg-blue-600"], {
28
- designSystem,
40
+ export default defineConfig({
41
+ plugins: [rainbowindex()],
29
42
  });
43
+ ```
44
+
45
+ ```css
46
+ /* src/styles.css */
47
+ @import "rainbowindex";
48
+ ```
49
+
50
+ ```tsx
51
+ // src/App.tsx
52
+ export default function App() {
53
+ return <div className="flex gap-4 px-6 py-3 bg-blue-500 text-white">Hello</div>;
54
+ }
55
+ ```
56
+
57
+ To scaffold a fresh app instead, use the CLI:
30
58
 
31
- console.log(result.css);
32
- // .p-4 { padding: 1rem; }
33
- // .bg-blue-500 { background-color: oklch(55.3% 0.213 253.075); }
34
- // .hover\:bg-blue-600:hover { background-color: oklch(47.4% 0.195 251.802); }
59
+ ```sh
60
+ pnpm dlx rainbowindex create my-app --template react-ts
61
+ # or wire into an existing Vite app
62
+ pnpm dlx rainbowindex init
35
63
  ```
36
64
 
37
- ### Parse and Print CSS
65
+ ## CLI
38
66
 
39
- The parser produces an AST that can be inspected, transformed, or printed back to a string. This is useful for tools
40
- that need to analyze or modify CSS programmatically.
67
+ The `rainbowindex` binary exposes five subcommands. The default is `build`.
41
68
 
42
- ```typescript
43
- import { parse, print } from "rainbowindex";
69
+ ```
70
+ rainbowindex <glob> [options] Generate CSS from source files
71
+ rainbowindex init Wire Rainbow Index into the current Vite app
72
+ rainbowindex create <dir> Scaffold a Vite app with Rainbow Index ready
73
+ rainbowindex generate-types Generate TypeScript types for ri() autocomplete
74
+ rainbowindex preload-fonts Generate <link rel="preload"> tags for resolved fonts
75
+ ```
76
+
77
+ Common flags:
44
78
 
45
- const { ast } = parse(`
46
- .button {
47
- padding: 1rem;
48
- background: blue;
49
- }
50
- `);
79
+ | Flag | Description |
80
+ | --- | --- |
81
+ | `-o`, `--output <file>` | Output CSS file path. Required with `--watch`. |
82
+ | `--watch` | Re-run on source-file changes (chokidar). |
83
+ | `--minify` | Minify output via LightningCSS. |
84
+ | `--optimize` | Minification + browser-fallback passes. |
85
+ | `--css <file>` | CSS input with directives. Auto-detected if omitted. |
86
+ | `--strict` | Drop the string escape hatch in generated types. |
87
+ | `--template <name>` | Vite template to scaffold (default: `react-ts`). |
51
88
 
52
- const output = print(ast);
89
+ Example:
90
+
91
+ ```sh
92
+ rainbowindex "src/**/*.{ts,tsx}" -o dist/styles.css --watch
53
93
  ```
54
94
 
55
- ### Transform CSS with Theme
95
+ ## PostCSS plugin
96
+
97
+ The package's default Node export is a PostCSS plugin.
56
98
 
57
- When processing CSS that contains Rainbow Index directives (`@theme`, `@apply`, etc.), use `transformCSS` to expand
58
- them.
99
+ ```js
100
+ // postcss.config.js
101
+ import rainbowindex from "rainbowindex";
59
102
 
60
- ```typescript
61
- import { parse, transformCSS, createDesignSystem, print } from "rainbowindex";
103
+ export default {
104
+ plugins: [rainbowindex({ sources: ["src/**/*.{ts,tsx,html}"] })],
105
+ };
106
+ ```
62
107
 
63
- const designSystem = createDesignSystem();
108
+ Options:
64
109
 
65
- const { ast } = parse(`
66
- @theme {
67
- --color-primary: oklch(0.6 0.2 250);
68
- }
110
+ | Option | Type | Description |
111
+ | --- | --- | --- |
112
+ | `sources` | `string[]` | Glob patterns for files to scan. Can also be declared via `@source` in CSS. |
113
+ | `cwd` | `string` | Working directory. Defaults to `process.cwd()`. |
69
114
 
70
- .button {
71
- background: var(--color-primary);
72
- }
73
- `);
115
+ ## Vite plugin
74
116
 
75
- const transformed = transformCSS(ast, { designSystem });
76
- const output = print(transformed);
117
+ ```ts
118
+ import rainbowindex from "rainbowindex/vite";
77
119
  ```
78
120
 
79
- ## Directive Handling
121
+ Auto-detects your CSS entry, injects a PostCSS config if none exists, supports HMR with file versioning. No options required for the common case.
80
122
 
81
- Rainbow Index processes `@rainbowindex` directives as injection points for generated styles. Unknown at-rules (including
82
- `@tailwind`) are preserved and pass through unchanged, rather than being treated as migration inputs.
123
+ ## Class syntax
83
124
 
84
- ## API Reference
125
+ ### Utilities
85
126
 
86
- ### `compileClasses(candidates, options)`
127
+ Roughly the same surface area as Tailwind: spacing, sizing, typography, color, layout, borders, effects, animations, and SVG. Use `rainbowindex generate-types` for autocomplete in your editor.
87
128
 
88
- Compile utility class names into CSS.
129
+ ### Variants
89
130
 
90
- ```typescript
91
- interface CompileOptions {
92
- designSystem: DesignSystem;
93
- }
131
+ Prefix any utility with one or more variants, separated by `:`.
94
132
 
95
- interface CompileResult {
96
- css: string;
97
- classes: string[];
98
- }
133
+ ```html
134
+ <button class="bg-blue-500 hover:bg-blue-600 dark:bg-blue-400 sm:px-6">…</button>
99
135
  ```
100
136
 
101
- ### `createDesignSystem()`
137
+ Supported variants:
102
138
 
103
- Create a design system instance with the default theme, utilities, and variants. The design system is the central
104
- registry that maps class names to CSS output.
139
+ - **Pseudo-classes** `hover`, `focus`, `focus-visible`, `active`, `visited`, `disabled`, `enabled`, `checked`, `empty`, `first`, `last`, `odd`, `even`, `only`
140
+ - **Pseudo-elements** `before`, `after`, `placeholder`, `file`, `marker`, `selection`, `first-line`, `first-letter`, `backdrop`
141
+ - **Media** — `dark`, `print`, `portrait`, `landscape`, `motion-safe`, `motion-reduce`, `starting`
142
+ - **Breakpoints** — `sm`, `md`, `lg`, `xl` (theme-driven, customizable via `@breakpoint`)
143
+ - **Container queries** — `@sm`, `@md`, …
144
+ - **Attribute selectors** — `data-[state=open]`, `aria-[pressed=true]`
145
+ - **Arbitrary** — `[selector]`, `[@media(...)]`
105
146
 
106
- ```typescript
107
- interface DesignSystem {
108
- theme: Theme;
109
- utilities: Utilities;
110
- variants: Variants;
111
- parseCandidate(candidate: string): Candidate[];
112
- compileAstNodes(candidate: Candidate): AstNode[];
113
- getClassOrder(classes: string[]): [string, bigint | null][];
114
- }
147
+ ### Arbitrary values
148
+
149
+ ```html
150
+ <div class="w-[37rem] bg-[#1a73e8] data-[state=open]:opacity-100"></div>
115
151
  ```
116
152
 
117
- ### `parse(css, options?)`
153
+ CSS variable shorthand:
118
154
 
119
- Parse a CSS string into an AST.
155
+ ```html
156
+ <div class="bg-(--brand-color) text-(--brand-text)"></div>
157
+ ```
120
158
 
121
- ```typescript
122
- interface ParseOptions {
123
- from?: string; // Source file path for source maps
124
- }
159
+ ### Variant groups
125
160
 
126
- interface ParseResult {
127
- ast: AstNode[];
128
- }
161
+ When multiple utilities share the same variant prefix, group them with `{…}` instead of repeating the prefix:
162
+
163
+ ```html
164
+ <!-- These two lines are equivalent -->
165
+ <div class="hover:text-red-500 hover:bg-blue-100 hover:underline">…</div>
166
+ <div class="hover:{text-red-500 bg-blue-100 underline}">…</div>
129
167
  ```
130
168
 
131
- ### `print(ast, options?)`
169
+ Groups stack and nest:
132
170
 
133
- Convert an AST back to a CSS string.
171
+ ```html
172
+ <!-- Chained variants -->
173
+ <div class="sm:hover:{bg-gray-700 text-white}">…</div>
134
174
 
135
- ```typescript
136
- interface PrintOptions {
137
- minify?: boolean;
138
- indent?: string;
139
- }
175
+ <!-- Multiple groups in one class string -->
176
+ <div class="focus:{outline-2 outline-blue-500} disabled:{opacity-50 cursor-not-allowed}">…</div>
177
+
178
+ <!-- The data-attribute case -->
179
+ <div class="data-[active]:{relative px-2} data-[slot=sidebar]:{fixed leading-none}">…</div>
140
180
  ```
141
181
 
142
- ### `transformCSS(ast, options)`
182
+ Expansion happens at scan-time, so the runtime never sees the grouped form. Nesting is capped at depth 10; expanded output is capped at 1MB.
183
+
184
+ ## Theming with CSS directives
185
+
186
+ Customization happens in your CSS input, not a JS config. The engine recognizes:
187
+
188
+ | Directive | Purpose |
189
+ | --- | --- |
190
+ | `@color` | Define color tokens. Supports generative (`chroma hue`), explicit (`oklch(...)`, `#rrggbb`), light/dark pairs, and aliases. |
191
+ | `@spacing` | Set the spacing base unit. |
192
+ | `@text` | Define text size tokens (`size, line-height`). |
193
+ | `@font`, `@font-face` | Register font families inside a single `@font { … }` block. One slot can own multiple `@face` faces (e.g. upright + italic) or use the `italic:` shorthand. |
194
+ | `@rounded` | Border-radius tokens; modifier sets corner shape (`round`, `squircle`, `superellipse(N)`, etc). |
195
+ | `@fluid` | Configure fluid type/spacing range. |
196
+ | `@animate` | Register named animations with inline `@keyframes`. |
197
+ | `@utility` | Define a custom utility (static or functional `name-*`). |
198
+ | `@apply` | Compose utilities into a single rule. |
199
+ | `@custom` | Define a custom variant. |
200
+ | `@slot` | Slot marker inside `@custom` block form. |
201
+ | `@source` | Declare additional source globs from CSS. Supports `not "..."` and `inline("...")`. |
202
+ | `@preflight` | Toggle preflight base styles. |
203
+ | `@layer`, `@media`, `@custom-media`, `@import` | Standard CSS plus a few extensions. |
204
+
205
+ Example:
206
+
207
+ ```css
208
+ @import "rainbowindex";
209
+
210
+ @color {
211
+ brand: 0.18 330; /* generative: chroma hue */
212
+ brand-soft: oklch(0.92 0.04 330); /* explicit */
213
+ surface: oklch(0.98 0.01 260) / oklch(0.15 0.01 260); /* light/dark pair */
214
+ }
215
+
216
+ @spacing { base: 0.5rem; }
143
217
 
144
- Transform a CSS AST, processing `@theme`, `@apply`, and other directives.
218
+ @text {
219
+ display: 4rem, 1.05;
220
+ body: 1rem, 1.5;
221
+ }
145
222
 
146
- ```typescript
147
- interface TransformOptions {
148
- designSystem: DesignSystem;
223
+ @font {
224
+ sans: "Inter" from google { weight: 400 700; }
225
+ display: "Satoshi" from "/fonts/Satoshi.woff2" {
226
+ weight: 300 900;
227
+ italic: "/fonts/Satoshi-Italic.woff2"; /* second face, font-style: italic */
228
+ }
149
229
  }
230
+
231
+ @source "../emails/**/*.html";
232
+ @source not "../**/legacy/*";
150
233
  ```
151
234
 
152
- ## Default Theme
235
+ Default theme keys: `colors`, `spacing`, `text`, `breakpoints`, `rounded`, `shadows`, `weights`, `easing`, `fluid`, `animations`, `blur`, `z`.
236
+
237
+ ## `ri()` — runtime class merger
153
238
 
154
- The default theme provides a comprehensive set of design tokens:
239
+ `ri()` merges class strings with right-most-wins conflict resolution. It replaces both `clsx` (for conditional composition) and `tailwind-merge` (for conflict resolution).
155
240
 
156
- **Colors** — Full OKLCH palette: gray, red, orange, amber, yellow, lime, green, emerald, teal, cyan, sky, blue, indigo,
157
- violet, purple, fuchsia, pink, rose.
241
+ ```ts
242
+ import { ri } from "rainbowindex";
158
243
 
159
- **Spacing** — Scale from 0 to 96: 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 20, 24, 28, 32,
160
- 36, 40, 44, 48, 52, 56, 60, 64, 72, 80, 96.
244
+ ri("px-2 py-1", isActive && "bg-blue-500", "px-4");
245
+ // "py-1 bg-blue-500 px-4" (px-2 is dropped px-4 wins)
246
+ ```
247
+
248
+ Accepted inputs:
161
249
 
162
- **Typography** — Font sizes, weights, and line heights.
250
+ ```ts
251
+ type ClassInput = string | false | null | undefined | ClassInput[];
252
+ ri(...inputs: ClassInput[]): string;
253
+ ```
163
254
 
164
- **Breakpoints** Numeric (rem-based) and named.
255
+ Conflict resolution understands shorthands: `p-4` claims all four padding sides, but only drops if every side is overwritten by a class to its right.
256
+
257
+ ### `ri()` vs `createRi()` — which one do I use?
258
+
259
+ | Situation | Use |
260
+ | --- | --- |
261
+ | Browser bundle / client components | **`ri()`** |
262
+ | Vite build / PostCSS one-shot | **`ri()`** |
263
+ | Single Node compile that exits | **`ri()`** |
264
+ | Concurrent SSR (one server, many requests) | **`createRi(snapshot)`** |
265
+ | Multi-tenant compile (different themes in the same process) | **`createRi(snapshot)`** |
266
+ | Edge / serverless functions sharing module state across invocations | **`createRi(snapshot)`** |
267
+
268
+ `ri()` reads module-level state published by the most recent compile. That's
269
+ fast and ergonomic in any environment where there is exactly one compile
270
+ per process. If two requests can be merging classes against two different
271
+ themes in the same Node process, that shared state will leak — use
272
+ `createRi(snapshot)` to bind each request to its own frozen snapshot.
273
+
274
+ ```ts
275
+ // Anywhere ri() is single-compile-safe (browser, Vite, PostCSS):
276
+ import { ri } from "rainbowindex";
277
+
278
+ const className = ri("px-2 py-1", isActive && "bg-blue-500", "px-4");
279
+ ```
280
+
281
+ ```ts
282
+ // SSR / multi-tenant — capture the snapshot at compile time, bind per-request:
283
+ import { compileProject, createRi, finalizeCompilationContext } from "rainbowindex";
284
+
285
+ // At server startup (once per theme):
286
+ const ctx = await compileProject({ cwd, css });
287
+ const snapshot = finalizeCompilationContext(ctx);
288
+ const ri = createRi(snapshot);
289
+
290
+ // In your request handler:
291
+ function render(req, res) {
292
+ const html = `<div class="${ri("px-2 py-1", req.dark && "dark:bg-slate-900")}">…</div>`;
293
+ res.send(html);
294
+ }
295
+ ```
165
296
 
166
- ## Utilities
297
+ If you call `ri()` while a compilation is still in progress (e.g. inside a
298
+ server-rendering pass that triggers a fresh compile), the runtime emits a
299
+ throttled `[RI-2004]` warning. That's your signal to switch to `createRi()`.
167
300
 
168
- Core utilities included:
301
+ ## Environment variables
169
302
 
170
- - **Spacing**: `p-*`, `m-*`, `gap-*`, `space-*`
171
- - **Sizing**: `w-*`, `h-*`, `min-w-*`, `max-w-*`, `min-h-*`, `max-h-*`
172
- - **Colors**: `bg-*`, `text-*`, `border-*`
173
- - **Typography**: `font-*`, `text-*`, `leading-*`, `tracking-*`
174
- - **Layout**: `flex`, `grid`, `block`, `inline`, `hidden`
175
- - **Flexbox**: `flex-*`, `items-*`, `justify-*`, `grow-*`, `shrink-*`
176
- - **Grid**: `grid-cols-*`, `grid-rows-*`, `col-span-*`, `row-span-*`
177
- - **Effects**: `shadow-*`, `opacity-*`, `blur-*`
178
- - **Borders**: `rounded-*`, `border-*`
303
+ | Variable | Effect |
304
+ | --- | --- |
305
+ | `RI_DEBUG=1` | Enable debug logging. |
306
+ | `RI_OFFLINE=1` | Skip network calls; use cached font data only. |
307
+ | `RI_FETCH_FONTS=0` | Disable Google Fonts metadata requests. |
308
+ | `RI_CACHE_DIR` | Override font cache directory (default: `node_modules/.cache/rainbowindex`). |
309
+ | `RI_FONT_CACHE_TTL` | Font cache max age in seconds (default: `604800` — 7 days). |
179
310
 
180
- ## Variants
311
+ ## Diagnostics
181
312
 
182
- Built-in variants:
313
+ Warnings carry `RI-NNNN` codes. Ranges:
183
314
 
184
- - **Pseudo-classes**: `hover`, `focus`, `active`, `visited`, `disabled`, `first`, `last`, `odd`, `even`
185
- - **Pseudo-elements**: `before`, `after`, `placeholder`, `selection`
186
- - **Media**: `dark`, numeric breakpoints (`48:`, `64:`, etc.)
187
- - **Compound**: `group-*`, `peer-*`
188
- - **Data/Aria**: `data-*`, `aria-*`
315
+ | Range | Subsystem |
316
+ | --- | --- |
317
+ | 10xx | Compilation & directives |
318
+ | 11xx | Color directives |
319
+ | 12xx | Font system |
320
+ | 13xx | Merge / compilation context |
321
+ | 14xx | Source scanner |
322
+ | 15xx | Typography utilities |
323
+ | 16xx | Integration plugins (Vite, PostCSS, CLI wiring) |
324
+ | 20xx | CSS function processing |
325
+ | 21xx | `ri()` runtime |
189
326
 
190
- ## License
327
+ See the [diagnostics reference](https://rainbowindex.dev/docs/diagnostics) for the full code → cause → fix table.
191
328
 
192
- MIT
329
+ Warnings are deduplicated and capped at 200 per compile, with 20 slots reserved for high-severity errors.