rainbowindex 0.5.1 → 0.7.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +876 -0
  2. package/LICENSE +16 -17
  3. package/NOTICE.md +106 -0
  4. package/README.md +225 -69
  5. package/dist/browser.d.ts +4 -2
  6. package/dist/browser.mjs +12 -6
  7. package/dist/chunk-2T7V5XLK.mjs +912 -0
  8. package/dist/chunk-6OORICWF.mjs +16 -0
  9. package/dist/{chunk-PDORZSQX.mjs → chunk-FJOZJIKB.mjs} +7814 -5174
  10. package/dist/chunk-L56IRO7A.mjs +491 -0
  11. package/dist/chunk-PZDVDEZJ.mjs +196 -0
  12. package/dist/{chunk-4CTJLMYM.mjs → chunk-RC6DDE4L.mjs} +37 -23
  13. package/dist/chunk-TQJYVQPE.mjs +217 -0
  14. package/dist/chunk-W756NVYI.mjs +33 -0
  15. package/dist/chunk-WBESS2ZD.mjs +598 -0
  16. package/dist/{chunk-3HRMFZGE.mjs → chunk-X66Z2YHT.mjs} +2 -1
  17. package/dist/{chunk-4UKFK2GE.mjs → chunk-XQGSG2HK.mjs} +213 -756
  18. package/dist/cli.mjs +1101 -125
  19. package/dist/{context-B9yhJxd5.d.ts → context-DcBtnnan.d.ts} +55 -108
  20. package/dist/editor.d.ts +82 -421
  21. package/dist/editor.mjs +68 -363
  22. package/dist/eslint.d.ts +16 -0
  23. package/dist/eslint.mjs +32 -0
  24. package/dist/{index-Dx-NpFFx.d.ts → imports-C9esHd5Q.d.ts} +98 -81
  25. package/dist/index-CNqdL5U0.d.ts +56 -0
  26. package/dist/index-Czx-EUwh.d.ts +138 -0
  27. package/dist/index-DW8YSxTz.d.ts +104 -0
  28. package/dist/index.d.ts +49 -5
  29. package/dist/index.mjs +40 -13
  30. package/dist/oxlint.d.ts +21 -3
  31. package/dist/oxlint.mjs +19 -1
  32. package/dist/recipe.d.ts +111 -0
  33. package/dist/recipe.mjs +71 -0
  34. package/dist/safelist-CH3_PywB.d.ts +43 -0
  35. package/dist/session-CMaskdB7.d.ts +543 -0
  36. package/dist/tailwind.css +644 -0
  37. package/dist/theme-CIZiGlce.d.ts +115 -0
  38. package/dist/vite.d.ts +10 -1
  39. package/dist/vite.mjs +273 -113
  40. package/package.json +27 -5
  41. package/dist/chunk-F4VCBISU.mjs +0 -1866
  42. package/dist/chunk-RU4756NG.mjs +0 -243
  43. package/dist/safelist-DAkKuxCk.d.ts +0 -96
package/LICENSE CHANGED
@@ -1,22 +1,21 @@
1
1
  MIT License
2
2
 
3
- This project is a fork of Tailwind CSS v4. Portions of this software are derived from Tailwind CSS: Copyright (c) Tailwind Labs, Inc.
3
+ Copyright (c) 2026 Milo Tech Forecast
4
4
 
5
- The class merging algorithm is derived from tailwind-merge: Copyright (c) 2021 Dany Castillo
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
6
11
 
7
- The animation system is inspired by tw-animate-css: Copyright (c) 2025 Wombosvideo (Luca Bosin)
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
8
14
 
9
- Rainbow Index Copyright (c) 2026 Milo Tech Forecast
10
-
11
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
12
- documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
13
- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
14
- persons to whom the Software is furnished to do so, subject to the following conditions:
15
-
16
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
17
- Software.
18
-
19
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
20
- WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
21
- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
22
- OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/NOTICE.md ADDED
@@ -0,0 +1,106 @@
1
+ # Notices
2
+
3
+ Rainbow Index is licensed under the MIT License — see [LICENSE](LICENSE).
4
+
5
+ It incorporates work from the projects below. Each is MIT-licensed, and MIT's
6
+ notice clause requires their copyright notices and permission notices to travel
7
+ with any copy or substantial portion of that work, so their full license texts
8
+ are reproduced here.
9
+
10
+ ## Tailwind CSS
11
+
12
+ Rainbow Index began as a fork of Tailwind CSS v4; portions of this software are
13
+ derived from it.
14
+
15
+ In addition, `src/presets/tailwind.css` — shipped in the package as
16
+ `rainbowindex/tailwind.css` — is a transcription of Tailwind v4's default
17
+ theme. Its color, breakpoint, font-size, line-height, font-weight,
18
+ letter-spacing, radius, shadow, blur, easing, animation, and shadow-family
19
+ values are copied from Tailwind's `packages/tailwindcss/theme.css` and
20
+ rewritten as Rainbow Index directives by
21
+ `scripts/generate-tailwind-preset.mjs`. The values are Tailwind's; only the
22
+ syntax is ours.
23
+
24
+ <https://github.com/tailwindlabs/tailwindcss>
25
+
26
+ ```
27
+ MIT License
28
+
29
+ Copyright (c) Tailwind Labs, Inc.
30
+
31
+ Permission is hereby granted, free of charge, to any person obtaining a copy
32
+ of this software and associated documentation files (the "Software"), to deal
33
+ in the Software without restriction, including without limitation the rights
34
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
35
+ copies of the Software, and to permit persons to whom the Software is
36
+ furnished to do so, subject to the following conditions:
37
+
38
+ The above copyright notice and this permission notice shall be included in all
39
+ copies or substantial portions of the Software.
40
+
41
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
43
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
44
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
45
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
46
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
47
+ SOFTWARE.
48
+ ```
49
+
50
+ ## tailwind-merge
51
+
52
+ The class merging algorithm in `src/merge/` is derived from tailwind-merge.
53
+ <https://github.com/dcastil/tailwind-merge>
54
+
55
+ ```
56
+ MIT License
57
+
58
+ Copyright (c) 2021 Dany Castillo
59
+
60
+ Permission is hereby granted, free of charge, to any person obtaining a copy
61
+ of this software and associated documentation files (the "Software"), to deal
62
+ in the Software without restriction, including without limitation the rights
63
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
64
+ copies of the Software, and to permit persons to whom the Software is
65
+ furnished to do so, subject to the following conditions:
66
+
67
+ The above copyright notice and this permission notice shall be included in all
68
+ copies or substantial portions of the Software.
69
+
70
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
71
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
72
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
73
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
74
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
75
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
76
+ SOFTWARE.
77
+ ```
78
+
79
+ ## tw-animate-css
80
+
81
+ The animation system is inspired by tw-animate-css.
82
+ <https://github.com/Wombosvideo/tw-animate-css>
83
+
84
+ ```
85
+ MIT License
86
+
87
+ Copyright (c) 2025 Wombosvideo
88
+
89
+ Permission is hereby granted, free of charge, to any person obtaining a copy
90
+ of this software and associated documentation files (the "Software"), to deal
91
+ in the Software without restriction, including without limitation the rights
92
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
93
+ copies of the Software, and to permit persons to whom the Software is
94
+ furnished to do so, subject to the following conditions:
95
+
96
+ The above copyright notice and this permission notice shall be included in all
97
+ copies or substantial portions of the Software.
98
+
99
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
100
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
101
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
102
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
103
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
104
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
105
+ SOFTWARE.
106
+ ```
package/README.md CHANGED
@@ -1,26 +1,46 @@
1
1
  <p align="center">
2
2
  <a href="https://rainbowindex.dev" target="_blank">
3
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%;">
4
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/rainbowindex/rainbowindex/HEAD/.github/assets/logo-dark.svg">
5
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/rainbowindex/rainbowindex/HEAD/.github/assets/logo-light.svg">
6
+ <img alt="Rainbow Index" src="https://raw.githubusercontent.com/rainbowindex/rainbowindex/HEAD/.github/assets/logo-light.svg" width="161" height="41" style="max-width: 100%;">
7
7
  </picture>
8
8
  </a>
9
9
  </p>
10
10
 
11
- **Rainbow Index** is a CSS-first system for building and maintaining consistent user interfaces.
12
-
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.
14
-
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.
16
-
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.
11
+ <p align="center">
12
+ <a href="https://github.com/rainbowindex/rainbowindex/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/rainbowindex/rainbowindex/actions/workflows/ci.yml/badge.svg?branch=main"></a>
13
+ <a href="https://www.npmjs.com/package/rainbowindex"><img alt="npm" src="https://img.shields.io/npm/v/rainbowindex.svg"></a>
14
+ <a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/npm/l/rainbowindex.svg"></a>
15
+ </p>
22
16
 
23
- Full consumer documentation lives in [docs/](docs/README.md).
17
+ **Rainbow Index** compiles a design system out of CSS. Your tokens live in CSS
18
+ directives rather than a JavaScript config, and the compiler turns them into
19
+ utilities, a typed contract for your editor, and diagnostics when something is
20
+ wrong.
21
+
22
+ - **The theme is CSS.** `@color { brand: 0.18 330; }` declares a generative
23
+ OKLCH palette; `@text`, `@breakpoint`, `@shadow` and the rest work the same
24
+ way. No config file to keep in sync.
25
+ - **Nothing ships until you name it.** `text-lg` and `bg-blue-500` do not exist
26
+ until a directive defines them, so the output is your system rather than a
27
+ filtered copy of someone else's. One import brings Tailwind v4's scales.
28
+ - **It tells you when you are wrong.** Numbered diagnostics for a color that
29
+ fails contrast, a class that can never match, a token that resolves to
30
+ nothing.
31
+ - **Your editor knows your theme.** `rainbowindex/editor` enumerates ~3,900
32
+ completions from your own CSS, and says what a class compiles to and why a
33
+ merge dropped one.
34
+
35
+ It compiles [100% of Tailwind v4's class surface](#tailwind-class-coverage), so
36
+ moving over is mostly copy-paste — and
37
+ `rainbowindex migrate tailwind` translates the theme for you, then tells you
38
+ which of your classes stop resolving ([docs/migrating.md](docs/migrating.md)). [Why Rainbow Index?](docs/why.md) compares it
39
+ against Tailwind, UnoCSS, Panda and StyleX — losses included.
40
+
41
+ The documentation is in [docs/](docs/README.md).
42
+ [docs/frameworks.md](docs/frameworks.md) has copy-paste setup for Vite, React
43
+ Router, SvelteKit, Astro and Next.js.
24
44
 
25
45
  ## Install
26
46
 
@@ -30,27 +50,9 @@ pnpm add rainbowindex
30
50
 
31
51
  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.
32
52
 
33
- ### From GitHub Packages
34
-
35
- Every release is also published to [GitHub Packages](https://github.com/rainbowindex/rainbowindex/pkgs/npm/rainbowindex) as `@rainbowindex/rainbowindex` — the same build under an owner-scoped name, which that registry requires. Point the scope at the registry in your project's `.npmrc`:
36
-
37
- ```ini
38
- @rainbowindex:registry=https://npm.pkg.github.com
39
- ```
40
-
41
- GitHub Packages authenticates every read, including public ones, so put a [personal access token (classic)](https://github.com/settings/tokens) with the `read:packages` scope in your `~/.npmrc`:
42
-
43
- ```ini
44
- //npm.pkg.github.com/:_authToken=YOUR_TOKEN
45
- ```
46
-
47
- Then install under the alias `rainbowindex`, so every import, `@import`, and CLI invocation in the docs below works unchanged:
48
-
49
- ```sh
50
- pnpm add rainbowindex@npm:@rainbowindex/rainbowindex
51
- ```
52
-
53
- Without the alias — `pnpm add @rainbowindex/rainbowindex` — the package resolves under its scoped name, and specifiers become `@rainbowindex/rainbowindex`, `@rainbowindex/rainbowindex/vite`, and so on.
53
+ Every release is also published to GitHub Packages under an owner-scoped name.
54
+ That registry authenticates every read, so it needs two lines of `.npmrc` — see
55
+ [Install](docs/getting-started.md#from-github-packages).
54
56
 
55
57
  ## Quick start (Vite)
56
58
 
@@ -66,6 +68,25 @@ export default defineConfig({
66
68
  });
67
69
  ```
68
70
 
71
+ Then pick a starting theme.
72
+
73
+ **Tailwind-familiar.** One extra import brings Tailwind v4's default scales — 26 color families, the `sm` through `2xl` breakpoints, `text-xs` through `text-9xl`, and the weight, leading, tracking, radius, shadow, blur, easing, and animation names.
74
+
75
+ ```css
76
+ /* src/styles.css */
77
+ @import "rainbowindex";
78
+ @import "rainbowindex/tailwind.css";
79
+ ```
80
+
81
+ ```tsx
82
+ // src/App.tsx
83
+ export default function App() {
84
+ return <div className="sm:flex gap-4 px-6 py-3 text-lg font-bold rounded-lg shadow-md bg-blue-600 text-white">Hello</div>;
85
+ }
86
+ ```
87
+
88
+ **From scratch.** Name the tokens your design system has, and nothing else.
89
+
69
90
  ```css
70
91
  /* src/styles.css */
71
92
  @import "rainbowindex";
@@ -73,16 +94,25 @@ export default defineConfig({
73
94
  @color {
74
95
  brand: 0.18 330;
75
96
  }
97
+
98
+ @text { body: 1rem, 1.5; }
99
+ @breakpoint { sm: 40rem; }
76
100
  ```
77
101
 
78
102
  ```tsx
79
103
  // src/App.tsx
80
104
  export default function App() {
81
- return <div className="flex gap-4 px-6 py-3 bg-brand-500 text-white">Hello</div>;
105
+ return <div className="sm:flex gap-4 px-6 py-3 text-body bg-brand-500 text-white">Hello</div>;
82
106
  }
83
107
  ```
84
108
 
85
- The default palette ships only the neutral `theme` color (plus `black`, `white`, `paper`, `ink`, `transparent`, `current`, `inherit`) — declare your palette with `@color`.
109
+ ### What does not ship by default
110
+
111
+ The package ships two scales: the neutral `theme` color and the `0.25rem` spacing base. Breakpoints, text sizes, weights, leading, tracking, radii, shadows, blur, easing, animations, and fluid ranges all start empty, so `sm:flex`, `text-lg`, `font-bold`, `shadow-md`, and `rounded-lg` render nothing until a directive names them. The fixed color names `black`, `white`, `paper`, `ink`, `transparent`, `current`, and `inherit` always work; `blue-500` does not exist until you declare it.
112
+
113
+ The forms that compute rather than look up need no theme at all: `p-4`, `rounded-4`, `font-600`, `z-10`, `text-[18px]`, `blur-none`.
114
+
115
+ `rainbowindex/tailwind.css` is the escape hatch, not the default. It is a plain directive file — read it, copy the blocks you want, or import it whole. Import it after the package, and override any token by declaring it again below. Importing it costs only what you use: every token is pruned to what the build actually references, so a page that names four colors emits four.
86
116
 
87
117
  To scaffold a fresh app instead, use the CLI:
88
118
 
@@ -94,13 +124,14 @@ pnpm dlx rainbowindex init
94
124
 
95
125
  ## CLI
96
126
 
97
- The `rainbowindex` binary exposes six subcommands. The default is `build`.
127
+ The `rainbowindex` binary exposes seven subcommands. The default is `build`.
98
128
 
99
129
  ```
100
130
  rainbowindex <glob> [options] Generate CSS from source files
101
131
  rainbowindex init Wire Rainbow Index into the current Vite app
102
132
  rainbowindex create <dir> Scaffold a Vite app with Rainbow Index ready
103
133
  rainbowindex generate-types Generate TypeScript types for ri() autocomplete
134
+ rainbowindex generate-snapshot Generate the theme snapshot that makes client ri() theme-aware
104
135
  rainbowindex preload-fonts Print <link rel="preload"> tags for local faces marked preload
105
136
  rainbowindex scan <glob> Print the class names the scanner extracts from files
106
137
  ```
@@ -152,27 +183,76 @@ Auto-detects your CSS entry, injects a PostCSS config if none exists, supports H
152
183
 
153
184
  ## Vite+
154
185
 
155
- [Vite+](https://viteplus.dev) works out of the box. Directive syntax is not valid CSS, so Oxfmt — the formatter behind `vp fmt` and `vp check` — cannot parse a stylesheet that holds directives, and the whole check fails before it lints or type checks. The Vite plugin prevents that: it adds every activated stylesheet to `fmt.ignorePatterns`, and leaves the rest of the project formatted. Without the Vite plugin, add the pattern yourself.
186
+ [Vite+](https://viteplus.dev) works out of the box. Four deprecated directive spellings are not valid CSS, so Oxfmt — the formatter behind `vp fmt` and `vp check` — cannot parse a stylesheet that still uses them. The Vite plugin adds only those files to `fmt.ignorePatterns`; a stylesheet written the canonical way is hidden from nothing.
187
+
188
+ See [docs/vite-plus.md](docs/vite-plus.md).
189
+
190
+ ## Component variants
191
+
192
+ `recipe()` is a typed variant layer whose output is ordinary class names — the
193
+ `cva`/`tv` shape, merged through `ri()` so conflicts resolve against your
194
+ compiled theme instead of a Tailwind utility table.
195
+
196
+ ```ts
197
+ import { recipe } from "rainbowindex/recipe";
198
+
199
+ const button = recipe({
200
+ base: "inline-flex items-center rounded-card font-medium",
201
+ variants: {
202
+ tone: { solid: "bg-brand-600 text-white", quiet: "text-brand-700" },
203
+ size: { sm: "h-8 px-3 text-sm", md: "h-10 px-4" },
204
+ },
205
+ defaultVariants: { tone: "solid", size: "md" },
206
+ });
207
+
208
+ button({ size: "sm" }); // → "inline-flex … bg-brand-600 text-white h-8 px-3 text-sm"
209
+ button({ size: "xl" }); // ✗ Type error: "xl" is not a size
210
+ ```
211
+
212
+ The classes live in the config, where the scanner already reads them. See
213
+ [docs/recipe.md](docs/recipe.md).
156
214
 
157
- One more integration is opt-in:
215
+ ## Editor support
216
+
217
+ **Rainbow Index for VS Code**, on the Marketplace and thin over
218
+ `rainbowindex/editor`: completions from *your* theme (a `@color` you added five
219
+ seconds ago completes), hover showing the generated rule and the colour's light
220
+ and dark hex, diagnostics on a class that compiles to nothing, colour chips you
221
+ can drag, an element tree and a theme explorer, go-to-definition, rename, and a
222
+ sort command that matches the order the stylesheet emits. It loads the copy of
223
+ the package your workspace installed, so upgrading the package upgrades what
224
+ your editor knows. Source at https://github.com/miloag/extension — see [docs/editor.md](docs/editor.md).
225
+
226
+ ## Lint rules
227
+
228
+ Three opt-in rules, for Oxlint and ESLint. Two of them read your compiled theme,
229
+ so they can tell you a class is wrong rather than merely unfamiliar:
230
+ `no-unknown-class` reports a class that compiles to nothing (with the typo
231
+ suggestion as an editor fix), and `no-conflicting-classes` reports a class that
232
+ another class in the same string erases. `prefer-ri` reports an import of
233
+ `clsx`, `classnames` or `tailwind-merge`.
158
234
 
159
235
  ```ts
160
- // vite.config.ts — lint rule: merge classes with ri(), not clsx/tailwind-merge
236
+ // vite.config.ts
161
237
  export default defineConfig({
162
238
  lint: {
163
239
  jsPlugins: [{ name: "rainbowindex", specifier: "rainbowindex/oxlint" }],
164
- rules: { "rainbowindex/prefer-ri": "error" },
240
+ rules: {
241
+ "rainbowindex/no-unknown-class": "error",
242
+ "rainbowindex/no-conflicting-classes": "warn",
243
+ "rainbowindex/prefer-ri": "error",
244
+ },
165
245
  },
166
246
  });
167
247
  ```
168
248
 
169
- See [docs/vite-plus.md](docs/vite-plus.md).
249
+ See [docs/lint.md](docs/lint.md) for the ESLint flat-config form and the options.
170
250
 
171
251
  ## Class syntax
172
252
 
173
253
  ### Utilities
174
254
 
175
- 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.
255
+ The same utility families as Tailwind spacing, sizing, typography, color, layout, borders, effects, animations, and SVG — but not the same named tokens. `text-lg` and `shadow-md` resolve only once a directive names them, or once you import the preset (see [What does not ship by default](#what-does-not-ship-by-default)). The numeric, keyword, and arbitrary forms need no theme: `p-4`, `rounded-4`, `font-600`, `text-[18px]`, `blur-none`. Use `rainbowindex generate-types` for autocomplete in your editor.
176
256
 
177
257
  ### Variants
178
258
 
@@ -187,8 +267,8 @@ Supported variants:
187
267
  - **Pseudo-classes** — `hover`, `focus`, `focus-visible`, `active`, `visited`, `disabled`, `enabled`, `checked`, `empty`, `first`, `last`, `odd`, `even`, `only`
188
268
  - **Pseudo-elements** — `before`, `after`, `placeholder`, `file`, `marker`, `selection`, `first-line`, `first-letter`, `backdrop`
189
269
  - **Media** — `dark`, `print`, `portrait`, `landscape`, `motion-safe`, `motion-reduce`, `starting`
190
- - **Breakpoints** — `sm`, `md`, `lg`, `xl` (theme-driven, customizable via `@breakpoint`)
191
- - **Container queries** — `@sm`, `@md`,
270
+ - **Breakpoints** — every name from `@breakpoint`; none ships
271
+ - **Container queries** — the same names with an `@` prefix
192
272
  - **Attribute selectors** — `data-[state=open]`, `aria-[pressed=true]`
193
273
  - **Arbitrary** — `[selector]`, `[@media(...)]`
194
274
 
@@ -238,7 +318,7 @@ Customization happens in your CSS input, not a JS config. The engine recognizes:
238
318
  | `@spacing` | Set the spacing base unit. |
239
319
  | `@text` | Define text size tokens (`size, line-height`). |
240
320
  | `@font` | Register font families inside a single `@font { … }` block. Local files are repeatable `face:` entries (e.g. upright + italic). Known families get an automatic zero-CLS metrics fallback (`metrics: none` opts out). Plain `@font-face` rules are standard CSS and pass through untouched. |
241
- | `@rounded` | Corner shape (`round`, `squircle`, `superellipse(N)`, etc). Radii are spacing multiples, so there are no radius tokens. |
321
+ | `@rounded` | Corner shape (`round`, `squircle`, `superellipse(N)`, etc), and named radii. Unnamed radii are spacing multiples: `rounded-4`. |
242
322
  | `@fluid` | Configure fluid type/spacing range. |
243
323
  | `@animate` | Register named animations with inline `@keyframes`. |
244
324
  | `@utility` | Define a custom utility (static or functional `name-*`). |
@@ -247,11 +327,13 @@ Customization happens in your CSS input, not a JS config. The engine recognizes:
247
327
  | `@slot` | Slot marker inside `@custom` block form. |
248
328
  | `@source` | Declare additional source globs from CSS. Supports `not "..."` and `inline("...")`. |
249
329
  | `@preflight` | Toggle preflight base styles. |
250
- | `@breakpoint`, `@shadow`, `@weight`, `@ease`, `@blur`, `@z`, `@leading`, `@tracking`, `@opacity`, `@duration` | Key-value token scales; `!key;` removes a token. |
330
+ | `@breakpoint`, `@shadow`, `@weight`, `@ease`, `@blur`, `@z`, `@leading`, `@tracking`, `@opacity`, `@duration` | Key-value token scales; `key: initial;` removes a token. |
251
331
  | `@register` | Emit CSS `@property` registrations. |
252
332
  | `@layer` | Place the generated output in cascade layers (intercepted, own grammar). |
253
333
  | `@media`, `@import`, other standard at-rules | Standard CSS — passed through untouched. |
254
334
 
335
+ A named scale can also hold utilities. A `name { … }` block with no colon before it defines a utility in that scale's class family, so `@shadow { lifted-* { … } }` makes `shadow-lifted-*`. See [theming.md](docs/theming.md#utility-blocks).
336
+
255
337
  Example:
256
338
 
257
339
  ```css
@@ -283,7 +365,9 @@ Example:
283
365
  @source not "src/**/legacy/*";
284
366
  ```
285
367
 
286
- Default theme scales: `colors`, `spacing`, `text`, `breakpoints`, `shadows`, `weights`, `easing`, `fluid`, `animations`, `blur`, `leading`, `tracking`. The `z`, `opacity`, and `duration` scales ship empty numeric class forms are computed, and their directives add named tokens.
368
+ The package ships two defaults: the `colors` palette and the `spacing` base. Every other scale — `text`, `leading`, `tracking`, `shadows`, `radii`, `breakpoints`, `weights`, `easing`, `blur`, `animations`, `fluid`, `z`, `opacity`, `duration` starts empty, and its directive defines the named tokens. Numeric and keyword class forms are computed, so they work with no theme at all.
369
+
370
+ That is the default because a token you did not define is a token nobody has to reason about. When you want Tailwind's names instead, `@import "rainbowindex/tailwind.css";` after the package import supplies them, written in the same directives documented above — so you can open the file and copy the blocks you want rather than importing all of it.
287
371
 
288
372
  ## `ri()` — runtime class merger
289
373
 
@@ -307,20 +391,34 @@ Conflict resolution understands shorthands: `p-4` claims all four padding sides,
307
391
 
308
392
  ### `ri()` vs `createRi()` — which one do I use?
309
393
 
310
- | Situation | Use |
311
- | --- | --- |
312
- | Browser bundle / client components | **`ri()`** |
313
- | Vite build / PostCSS one-shot | **`ri()`** |
314
- | Single Node compile that exits | **`ri()`** |
315
- | Concurrent SSR (one server, many requests) | **`createRi(snapshot)`** |
316
- | Multi-tenant compile (different themes in the same process) | **`createRi(snapshot)`** |
317
- | Edge / serverless functions sharing module state across invocations | **`createRi(snapshot)`** |
318
-
319
- `ri()` reads module-level state published by the most recent compile. That's
320
- fast and ergonomic in any environment where there is exactly one compile
321
- per process. If two requests can be merging classes against two different
322
- themes in the same Node process, that shared state will leak use
323
- `createRi(snapshot)` to bind each request to its own frozen snapshot.
394
+ | Situation | Use | What you have to do |
395
+ | --- | --- | --- |
396
+ | Vite client, SSR, build | **`ri()`** | Nothing. |
397
+ | Any other bundler (Next.js, Webpack, Rspack, esbuild) | **`ri()`** | `rainbowindex generate-snapshot`, then import it once. |
398
+ | Single Node compile that exits | **`ri()`** | Nothing. |
399
+ | Many themes in one process (multi-tenant, per-request themes) | **`createRi(snapshot)`** | One merger per theme. |
400
+
401
+ `ri()` answers by asking the *published theme* what properties a class sets.
402
+ Since 0.6.0 every text size, weight, font slot, and color name is
403
+ project-defined, so a client with no theme published reads `text-lg` as a color
404
+ and returns just `text-white` for `ri("text-lg text-white")`.
405
+
406
+ A compile publishes a theme. A browser bundle never compilesso the Vite
407
+ plugin publishes one for it automatically, and every other bundler gets the
408
+ same result from a generated module:
409
+
410
+ ```sh
411
+ rainbowindex generate-snapshot # writes rainbowindex-snapshot.ts
412
+ ```
413
+
414
+ ```ts
415
+ // app entry, before anything that calls ri()
416
+ import "./rainbowindex-snapshot";
417
+ ```
418
+
419
+ If two requests can be merging classes against two different themes in the same
420
+ process, bind each to its own frozen snapshot with `createRi(snapshot)` — the
421
+ generated module exports one.
324
422
 
325
423
  ```ts
326
424
  // Anywhere ri() is single-compile-safe (browser, Vite, PostCSS):
@@ -345,9 +443,10 @@ function render(req, res) {
345
443
  }
346
444
  ```
347
445
 
348
- In any Node/SSR process, the default `ri()` emits a throttled `[RI-2004]`
349
- warning (at most once per 60 s) to flag its shared module state. Switching to
350
- `createRi(snapshot)` silences it.
446
+ The default `ri()` warns `[RI-2004]` once per process when it merges a
447
+ theme-dependent class with no theme published, naming the class it had to guess
448
+ about. Publishing any theme silences it — a single-theme app that publishes at
449
+ startup is correct, and is not warned at.
351
450
 
352
451
  ## Editor tooling API
353
452
 
@@ -366,7 +465,7 @@ const session = createEditorSession({ css: themeCss });
366
465
  session.diagnostics; // positioned problems in the CSS input
367
466
  session.inspector.validate("felx"); // { ok: false, reason: "unknown-utility", suggestion: "flex" }
368
467
  session.inspector.explain("sm:px-4"); // parsed structure + generated CSS + sort key
369
- session.enumerate(); // ~3,400 probe-verified completions + templates
468
+ session.enumerate(); // ~3,900 probe-verified completions + templates
370
469
  session.analyzeMerge(["px-2", "px-4"]); // which classes ri() drops, and who overrode them
371
470
  session.swatch("brand", 500); // light/dark oklch + hex for completions
372
471
  session.extractCandidates(source, path); // class tokens with exact source spans
@@ -411,4 +510,61 @@ Warnings carry `RI-NNNN` codes. Ranges:
411
510
 
412
511
  See [docs/diagnostics.md](docs/diagnostics.md) for the full code → cause → fix table.
413
512
 
414
- Warnings are deduplicated and capped at 200 per compile, with 20 slots reserved for high-severity errors.
513
+ Warnings are deduplicated and capped at 200 per compile, with 20 slots reserved for high-severity errors.
514
+
515
+ ## Benchmarks
516
+
517
+ Measured 2026-09-06 on an Apple M4 (10 cores), Node 24.20.0, against a generated
518
+ 10,000-file codebase (12.75 MB of source, 13,507 distinct classes) with
519
+ rainbowindex loading its Tailwind preset so all three engines compile the same
520
+ strings.
521
+
522
+ | Engine | Cold build | Rebuild (unchanged) | Rebuild (1 file) | Scan only | Output | Peak RSS |
523
+ | --- | --- | --- | --- | --- | --- | --- |
524
+ | rainbowindex 0.6.0 | 845 ms | 583 ms | 585 ms | 539 ms | **977 KB** | **354 MB** |
525
+ | Tailwind CSS 4.3.3 | **330 ms** | **40 ms** | **90 ms** | **39 ms** | 1.01 MB | 391 MB |
526
+ | UnoCSS 66.8.1 | 1.29 s | 503 ms | 502 ms | 440 ms | 1.00 MB | 427 MB |
527
+
528
+ Read that honestly: **Rainbow Index produces the smallest stylesheet of the
529
+ three and is the slowest to rebuild.** Almost all of the gap is the scanner —
530
+ `539 ms` of a `585 ms` rebuild — because Tailwind's is compiled Rust
531
+ (`@tailwindcss/oxide`) and this one is JavaScript. On a cold build, where the
532
+ scan is a smaller share of the work, the gap to Tailwind closes to about 2.6×.
533
+
534
+ Against UnoCSS it is three scenarios each: Rainbow Index wins cold build, output
535
+ size and peak memory; UnoCSS wins both rebuilds and the scan.
536
+
537
+ ### Tailwind class coverage
538
+
539
+ ```bash
540
+ pnpm bench:parity
541
+ ```
542
+
543
+ This asks Tailwind v4 for its own complete class list — the 23,289 names its
544
+ IntelliSense uses — renders every one, and reports which Rainbow Index does not
545
+ implement.
546
+
547
+ **100.00%.** Every one of the 23,289 renders. The last 297 outstanding were
548
+ `ring-offset-*` (296) and `ring-inset`; both are implemented, and the sweep is
549
+ what says so — it is recomputed on demand rather than quoted from a table
550
+ someone maintains by hand. What still differs is how a handful of them compile,
551
+ not whether they do: see
552
+ [differences from Tailwind](docs/utilities.md#differences-from-tailwind).
553
+
554
+ The harness, the method, and where the comparison stops being fair are all in
555
+ [bench/README.md](bench/README.md); full results, including the 1,000-file tree,
556
+ are in [bench/results/](bench/results/). Reproduce with:
557
+
558
+ ```bash
559
+ pnpm bench --sizes=1k,10k
560
+ ```
561
+
562
+ ## Contributing
563
+
564
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, the test layout, how to add a utility, and how `RI-NNNN` codes are allocated. Security reports go through [SECURITY.md](SECURITY.md).
565
+
566
+ [docs/stability.md](docs/stability.md) says what is already treated as a contract before 1.0, what warning a change gives you, and which runtimes are supported.
567
+
568
+ ## License
569
+
570
+ MIT — see [LICENSE](LICENSE). Rainbow Index began as a fork of Tailwind CSS v4 and carries work derived from tailwind-merge and tw-animate-css; their notices are in [NOTICE.md](NOTICE.md).
package/dist/browser.d.ts CHANGED
@@ -1,5 +1,7 @@
1
- export { C as ColorDefinition, a as CompilationContext, b as CompilationSnapshot, D as DEFAULT_TEXT_SIZES, F as FluidConfig, T as TextSize, c as Theme, d as createCompilationContext, e as defaultTheme, f as finalizeCompilationContext, r as registerColorNames, g as registerCustomFontFamilies, h as registerCustomTextSizes, i as registerCustomUtility } from './context-B9yhJxd5.js';
2
- export { c as createRi, r as ri, s as safelist } from './safelist-DAkKuxCk.js';
1
+ export { C as CompilationContext, a as CompilationSnapshot, S as SerializedSnapshot, c as createCompilationContext, f as finalizeCompilationContext, h as hydrateSnapshot, p as publishSnapshot, r as registerColorNames, b as registerCustomFontFamilies, d as registerCustomTextSizes, e as registerCustomUtility, s as serializeSnapshot } from './context-DcBtnnan.js';
2
+ export { c as createRi, r as ri } from './index-CNqdL5U0.js';
3
+ export { s as safelist } from './safelist-CH3_PywB.js';
4
+ export { C as ColorDefinition, F as FluidConfig, T as TextSize, a as Theme, d as defaultTheme } from './index-Czx-EUwh.js';
3
5
 
4
6
  declare function browserEntryUnavailable(): never;
5
7
 
package/dist/browser.mjs CHANGED
@@ -2,18 +2,22 @@ import {
2
2
  safelist
3
3
  } from "./chunk-PD4ZXGJ6.mjs";
4
4
  import {
5
- DEFAULT_TEXT_SIZES,
5
+ defaultTheme
6
+ } from "./chunk-L56IRO7A.mjs";
7
+ import {
6
8
  createCompilationContext,
7
9
  createRi,
8
- defaultTheme,
9
10
  devWarn,
10
11
  finalizeCompilationContext,
12
+ hydrateSnapshot,
13
+ publishSnapshot,
11
14
  registerColorNames,
12
15
  registerCustomFontFamilies,
13
16
  registerCustomTextSizes,
14
17
  registerCustomUtility,
15
- ri
16
- } from "./chunk-4UKFK2GE.mjs";
18
+ ri,
19
+ serializeSnapshot
20
+ } from "./chunk-XQGSG2HK.mjs";
17
21
 
18
22
  // src/entries/browser.ts
19
23
  function browserEntryUnavailable() {
@@ -26,16 +30,18 @@ function browserEntryUnavailable() {
26
30
  }
27
31
  var browser_default = browserEntryUnavailable;
28
32
  export {
29
- DEFAULT_TEXT_SIZES,
30
33
  createCompilationContext,
31
34
  createRi,
32
35
  browser_default as default,
33
36
  defaultTheme,
34
37
  finalizeCompilationContext,
38
+ hydrateSnapshot,
39
+ publishSnapshot,
35
40
  registerColorNames,
36
41
  registerCustomFontFamilies,
37
42
  registerCustomTextSizes,
38
43
  registerCustomUtility,
39
44
  ri,
40
- safelist
45
+ safelist,
46
+ serializeSnapshot
41
47
  };