rainbowindex 0.4.0 → 0.5.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 +163 -0
- package/README.md +5 -4
- package/dist/browser.mjs +1 -1
- package/dist/{chunk-TLR6RP5L.mjs → chunk-CMB6BHVE.mjs} +27 -10
- package/dist/{chunk-4SVFFDS2.mjs → chunk-DT5HYIM3.mjs} +240 -65
- package/dist/{chunk-5Y7EXXLS.mjs → chunk-KRZL4IDK.mjs} +18 -5
- package/dist/{chunk-YLB6FGIG.mjs → chunk-NXJZX6KI.mjs} +2 -2
- package/dist/{chunk-6DAHUFNU.mjs → chunk-ZI5ZYNSU.mjs} +917 -630
- package/dist/cli.mjs +93 -38
- package/dist/editor.d.ts +2 -2
- package/dist/editor.mjs +4 -4
- package/dist/{index-BB2HoeMj.d.ts → index-Dp6i5TSv.d.ts} +15 -11
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +4 -4
- package/dist/vite.mjs +36 -60
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,169 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.5.0] - 2026-08-27
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **`rainbowindex scan <file|glob…>`** — prints every class candidate the
|
|
13
|
+
scanner extracts from each file, with scanner warnings on stderr. Answers
|
|
14
|
+
"why doesn't my class generate?" directly: a class missing from the list
|
|
15
|
+
was never seen by the scanner (check the markup), while a class listed
|
|
16
|
+
there that still produces no rule fails later (check the build warnings).
|
|
17
|
+
- **RI-1411 — skipped over-long lines are now reported.** The scanner drops
|
|
18
|
+
lines above the minified-input guard, previously in silence. It now warns
|
|
19
|
+
once per file with the path and line count. Suppressed for `node_modules`
|
|
20
|
+
paths, where minified dists are the guard's intended target.
|
|
21
|
+
- **RI-1038 — uppercase `@utility` names warn.** The markup scanner only
|
|
22
|
+
matches lowercase tokens, so `@utility cardHeader` could never trigger
|
|
23
|
+
from `class="cardHeader"`. The utility still works through `@a`/`@apply`
|
|
24
|
+
and inline `@source`, so it is kept — but no longer silently unreachable.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- **Fallback stacks moved from preflight into `@font`.** The
|
|
29
|
+
`--sans-fallback` / `--serif-fallback` / `--mono-fallback` variables were
|
|
30
|
+
emitted into every project and referenced by nothing. A manual `@font`
|
|
31
|
+
slot that declares no fallbacks now gets the default system stack appended
|
|
32
|
+
to its `--font-<slot>` value instead (`sans: "Chartwell";` →
|
|
33
|
+
`"Chartwell", ui-sans-serif, system-ui, …`); a slot that declares its own
|
|
34
|
+
fallbacks is untouched. Font tokens no longer depend on a preflight
|
|
35
|
+
category that users can switch off.
|
|
36
|
+
- **Focus ring uses literal lengths.** `:focus-visible` drew its outline at
|
|
37
|
+
`var(--spacing)` (4px by default) and offset at half that, so changing the
|
|
38
|
+
spacing scale silently resized every focus ring. Now a flat 2px width and
|
|
39
|
+
2px offset.
|
|
40
|
+
- **Placeholder color follows the text color** —
|
|
41
|
+
`color-mix(in oklab, currentColor 48%, transparent)` instead of a
|
|
42
|
+
hardcoded gray that ignored the theme in dark mode.
|
|
43
|
+
- **Scanner line-length guard raised from 2,000 to 10,000 characters.** Real
|
|
44
|
+
minified files run far above this, while hand-written long lines (inline
|
|
45
|
+
SVG path data, long attribute stacks) sit below it. `MAX_LINE_LENGTH` is
|
|
46
|
+
exported so tooling and tests derive from it instead of hardcoding.
|
|
47
|
+
- **Default source patterns scan every root HTML file** (`*.html`, not just
|
|
48
|
+
`index.html`), so Vite multi-page apps are covered without an explicit
|
|
49
|
+
`@source`. `dist`, `build`, and `public` remain excluded.
|
|
50
|
+
|
|
51
|
+
### Removed
|
|
52
|
+
|
|
53
|
+
- **Preflight `select-reset`.** It styled rather than reset, and shipped
|
|
54
|
+
three defects: its chevron was a `currentColor` SVG in a `background-image`,
|
|
55
|
+
which resolves to black and disappears on dark backgrounds; the bare
|
|
56
|
+
`select` selector also hit `<select multiple>`, giving listboxes a floating
|
|
57
|
+
chevron and 2.5rem of padding; and `appearance: none` stripped the native
|
|
58
|
+
control on every platform. Style selects in your own CSS.
|
|
59
|
+
- **Preflight `:focus:not(:focus-visible) { outline: none }`** — no current
|
|
60
|
+
browser draws an outline for plain `:focus`, so the rule was dead.
|
|
61
|
+
|
|
62
|
+
### Fixed
|
|
63
|
+
|
|
64
|
+
- **Quoted class attributes on over-long lines are no longer lost.** A
|
|
65
|
+
`className="…"` sharing a line with multi-KB inline SVG path data
|
|
66
|
+
generated nothing: the whole-file scan skipped the line for length, and
|
|
67
|
+
the attribute collector stripped the quotes and then searched only for
|
|
68
|
+
string literals *nested inside* the value, of which a plain quoted
|
|
69
|
+
attribute has none. Quoted attribute values are now tokenized directly, in
|
|
70
|
+
the one shared collector — so JSX, HTML, Vue, Svelte, and object-literal
|
|
71
|
+
(`{ className: "…" }`) syntax are all fixed together. Non-quoted
|
|
72
|
+
expression values keep their existing semantics.
|
|
73
|
+
- **List margins are reset.** `list-reset` removed bullets and padding while
|
|
74
|
+
`ol`, `ul`, and `menu` kept the browser's `margin-block: 1em`, leaving
|
|
75
|
+
unexplained gaps around navigation and menus.
|
|
76
|
+
- **`fieldset` and `legend` are reset** — their default margin and padding
|
|
77
|
+
survived while every other form control was flush.
|
|
78
|
+
|
|
79
|
+
## [0.4.1] - 2026-08-25
|
|
80
|
+
|
|
81
|
+
### Added
|
|
82
|
+
|
|
83
|
+
- **Automatic zero-CLS font fallbacks.** Any `@font` slot whose family is in
|
|
84
|
+
the built-in metrics table (~100 common Google + system families, generated
|
|
85
|
+
from `@capsizecss/metrics` at development time — no new runtime dependency)
|
|
86
|
+
now gets a metrics-adjusted local fallback `@font-face` automatically. The
|
|
87
|
+
fallback font is picked from the slot's stack, or by the web font's
|
|
88
|
+
category (Arial / Times New Roman / Courier New). Opt out with
|
|
89
|
+
`metrics: none;`, pick the matched local font with `metrics: "Segoe UI";`,
|
|
90
|
+
or keep full manual control with
|
|
91
|
+
`metrics: "Arial" <size-adjust> <ascent> <descent> <line-gap>;`.
|
|
92
|
+
- **`face:` entries** — local font files are now declared as repeatable
|
|
93
|
+
`face: <src> [{ overrides }]` entries, one grammar for every face:
|
|
94
|
+
`display: "Satoshi" { face: /Satoshi.woff2; face: /Satoshi-Italic.woff2 { style: italic; } }`.
|
|
95
|
+
- **Fallback stacks work with `from google`** —
|
|
96
|
+
`sans: "Inter", ui-sans-serif, sans-serif from google;` now parses as a
|
|
97
|
+
google slot with fallbacks (previously it silently became a manual stack).
|
|
98
|
+
- **@font now warns instead of ignoring silently**: unknown option keys
|
|
99
|
+
(RI-1217), `preload` on non-local slots where it can have no effect
|
|
100
|
+
(RI-1219), and partial or invalid `metrics` values (RI-1220).
|
|
101
|
+
|
|
102
|
+
### Changed
|
|
103
|
+
|
|
104
|
+
- The `@font` parser was rebuilt on the shared entry scanner: one pass, no
|
|
105
|
+
regex preambles, no re-serialization. Public API (`parseFontBody`,
|
|
106
|
+
`parseNestedFontBlock`) is unchanged.
|
|
107
|
+
- **Incremental rebuilds only re-do changed work** — the scanner keeps a
|
|
108
|
+
per-file cache keyed on mtime + size, so a CLI watch or Vite HMR rebuild
|
|
109
|
+
re-reads and re-extracts only the files that actually changed instead of
|
|
110
|
+
the whole project. The CSS entry analysis is memoized on the entry text
|
|
111
|
+
(stable theme identity across rebuilds), Google-font resolution keeps its
|
|
112
|
+
identity when metadata is unchanged, and per-class compilation results
|
|
113
|
+
(rule, warnings, token usage) are replayed across rebuilds from a
|
|
114
|
+
theme-keyed cache. A one-file edit in a large project now costs
|
|
115
|
+
O(changed files) instead of O(project).
|
|
116
|
+
- **`ri()` conditional args hit the cache fast path** — falsy arguments
|
|
117
|
+
(`ri("flex", isActive && "bg-blue-500")`) no longer force re-tokenization
|
|
118
|
+
on every call: the raw-key cache skips falsy primitives, making cached
|
|
119
|
+
conditional-pattern calls ~4x faster.
|
|
120
|
+
- **Cheaper compile passes** — duplicate class names across `@apply` rules
|
|
121
|
+
resolve once per pass, the `@slot`/`@apply` walks no longer traverse the
|
|
122
|
+
generated utility CSS, custom `@utility` bodies parse once per body text
|
|
123
|
+
instead of once per variant form, and the editor candidate collector's
|
|
124
|
+
context assignment went from quadratic to a sorted sweep. As part of the
|
|
125
|
+
compile-result replay, engine-level compilation warnings are now
|
|
126
|
+
deduplicated (final project output already was).
|
|
127
|
+
- **Internal restructuring (behavior-preserving)** — the directive body
|
|
128
|
+
parsers were split by grammar family: `@color` and `@font` now own their
|
|
129
|
+
own modules, and the generic key-value/entry grammars sit together in the
|
|
130
|
+
directives foundation. The `parsers` import surface is unchanged.
|
|
131
|
+
|
|
132
|
+
### Deprecated
|
|
133
|
+
|
|
134
|
+
- The older `@font` forms still parse and desugar into the new model, but
|
|
135
|
+
warn (RI-1218) and will be removed in a future release: `@face { src: …; }`
|
|
136
|
+
blocks and the `italic:` shorthand (use `face:`), `from "<path>"` (use
|
|
137
|
+
`face:`), `from system` (use bare `system`), the `fallback:` key (list
|
|
138
|
+
fallbacks in the slot preamble), the five-key metrics cluster
|
|
139
|
+
`metricsFallback`/`sizeAdjust`/`ascent`/`descent`/`lineGap` (use
|
|
140
|
+
`metrics:`), and the `unicodeRange` spelling (use `unicode-range`).
|
|
141
|
+
|
|
142
|
+
### Removed
|
|
143
|
+
|
|
144
|
+
- **`@font` `subset:`** — the Google css2 API takes no subset hint, so the
|
|
145
|
+
key never affected the emitted URL. It now warns (RI-1218) and is ignored.
|
|
146
|
+
- **`FontSlot` / `FontFace` fields** — the five slot-level metrics fields
|
|
147
|
+
(`metricsFallback`, `sizeAdjust`, `ascent`, `descent`, `lineGap`) are
|
|
148
|
+
replaced by a single `metrics?: FontMetricsConfig | null`; slot-level
|
|
149
|
+
`preload` moved onto the faces; `FontFace.subset` is gone. Stylesheets are
|
|
150
|
+
unaffected — this only touches code importing those types directly.
|
|
151
|
+
|
|
152
|
+
### Fixed
|
|
153
|
+
|
|
154
|
+
- **`ri()` stroke-width vs stroke-color conflict — for real this time.** The
|
|
155
|
+
v0.4.0 changelog described this fix, but the implementation did not ship in
|
|
156
|
+
that release: `ri("stroke-2 stroke-red-500")` still dropped `stroke-2`. The
|
|
157
|
+
`stroke` prefix is now actually width-vs-color dual-mode (mirroring
|
|
158
|
+
`border`/`outline`): decimal and non-color arbitrary values claim
|
|
159
|
+
`stroke-width`, color values claim `stroke`. Same-property conflicts
|
|
160
|
+
(`stroke-2` vs `stroke-4`, `stroke-red-500` vs `stroke-blue-500`) still
|
|
161
|
+
merge as before. The emission↔claim parity suite no longer carries a
|
|
162
|
+
stroke exception, and regression tests pin the merged output.
|
|
163
|
+
- **Directive rewriter no longer duplicates unclosed blocks** — an
|
|
164
|
+
unterminated `{` inside a `@color` body made the Vite-plugin pre-pass
|
|
165
|
+
re-emit the text before it (`@color { accent: 0.18 330 { inline` came out
|
|
166
|
+
with the entry doubled). The rewriter's two hand-rolled brace walks were
|
|
167
|
+
replaced by one shared walker built on the same brace matcher the directive
|
|
168
|
+
scan already uses, which also makes quotes and comments inside blocks
|
|
169
|
+
behave consistently across the rewrite passes.
|
|
170
|
+
|
|
8
171
|
## [0.4.0] - 2026-08-20
|
|
9
172
|
|
|
10
173
|
### Added
|
package/README.md
CHANGED
|
@@ -211,7 +211,7 @@ Customization happens in your CSS input, not a JS config. The engine recognizes:
|
|
|
211
211
|
| `@color` | Define color tokens. Supports generative (`chroma hue`), explicit (`oklch(...)`, `#rrggbb`), light/dark pairs, and aliases. |
|
|
212
212
|
| `@spacing` | Set the spacing base unit. |
|
|
213
213
|
| `@text` | Define text size tokens (`size, line-height`). |
|
|
214
|
-
| `@font`, `@font-face` | Register font families inside a single `@font { … }` block.
|
|
214
|
+
| `@font`, `@font-face` | 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). |
|
|
215
215
|
| `@rounded` | Border-radius tokens; modifier sets corner shape (`round`, `squircle`, `superellipse(N)`, etc). |
|
|
216
216
|
| `@fluid` | Configure fluid type/spacing range. |
|
|
217
217
|
| `@animate` | Register named animations with inline `@keyframes`. |
|
|
@@ -242,10 +242,11 @@ Example:
|
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
@font {
|
|
245
|
-
sans: "Inter" from google { weight: 400 700; }
|
|
246
|
-
display: "Satoshi"
|
|
245
|
+
sans: "Inter", ui-sans-serif, sans-serif from google { weight: 400 700; }
|
|
246
|
+
display: "Satoshi" {
|
|
247
247
|
weight: 300 900;
|
|
248
|
-
|
|
248
|
+
face: /fonts/Satoshi.woff2;
|
|
249
|
+
face: /fonts/Satoshi-Italic.woff2 { style: italic; }
|
|
249
250
|
}
|
|
250
251
|
}
|
|
251
252
|
|
package/dist/browser.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
compileScannedProject
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-DT5HYIM3.mjs";
|
|
4
4
|
import {
|
|
5
5
|
APPLY_ALIASES,
|
|
6
6
|
DIRECTIVE_NAMES_SET,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
resolveUtilityDeclarations,
|
|
21
21
|
resolveVariant,
|
|
22
22
|
splitSelectorList
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-ZI5ZYNSU.mjs";
|
|
24
24
|
|
|
25
25
|
// src/integrations/postcss/index.ts
|
|
26
26
|
import postcss2 from "postcss";
|
|
@@ -102,6 +102,7 @@ function processApply(root, theme, warnings) {
|
|
|
102
102
|
}
|
|
103
103
|
const customVariantMap = new Map(theme.customVariants.map((cv) => [cv.name, cv]));
|
|
104
104
|
const checkedApplyRoots = /* @__PURE__ */ new Set();
|
|
105
|
+
const resolveCache = /* @__PURE__ */ new Map();
|
|
105
106
|
for (let depth = 0; depth < MAX_APPLY_DEPTH; depth++) {
|
|
106
107
|
const applyNodes = [];
|
|
107
108
|
const classListByNode = /* @__PURE__ */ new Map();
|
|
@@ -127,7 +128,8 @@ function processApply(root, theme, warnings) {
|
|
|
127
128
|
warnings,
|
|
128
129
|
customVariantMap,
|
|
129
130
|
groupRoots,
|
|
130
|
-
checkedApplyRoots
|
|
131
|
+
checkedApplyRoots,
|
|
132
|
+
resolveCache
|
|
131
133
|
);
|
|
132
134
|
}
|
|
133
135
|
}
|
|
@@ -144,7 +146,7 @@ function processApply(root, theme, warnings) {
|
|
|
144
146
|
}
|
|
145
147
|
}
|
|
146
148
|
}
|
|
147
|
-
function expandApply(atRule, classNames, theme, warnings, customVariantMap, groupRoots, checkedApplyRoots) {
|
|
149
|
+
function expandApply(atRule, classNames, theme, warnings, customVariantMap, groupRoots, checkedApplyRoots, resolveCache) {
|
|
148
150
|
const parentRule = atRule.parent;
|
|
149
151
|
if (parentRule?.type !== "rule") {
|
|
150
152
|
warnings.push("[RI-1006] @apply must be used inside a CSS rule, not at the top level.");
|
|
@@ -166,8 +168,23 @@ function expandApply(atRule, classNames, theme, warnings, customVariantMap, grou
|
|
|
166
168
|
}
|
|
167
169
|
const resolved = [];
|
|
168
170
|
for (const className of classNames) {
|
|
169
|
-
|
|
170
|
-
if (
|
|
171
|
+
let entry = resolveCache.get(className);
|
|
172
|
+
if (!entry) {
|
|
173
|
+
const entryWarnings = [];
|
|
174
|
+
entry = {
|
|
175
|
+
result: resolveClassName(
|
|
176
|
+
className,
|
|
177
|
+
theme,
|
|
178
|
+
entryWarnings,
|
|
179
|
+
customVariantMap,
|
|
180
|
+
checkedApplyRoots
|
|
181
|
+
),
|
|
182
|
+
warnings: entryWarnings
|
|
183
|
+
};
|
|
184
|
+
resolveCache.set(className, entry);
|
|
185
|
+
}
|
|
186
|
+
warnings.push(...entry.warnings);
|
|
187
|
+
if (entry.result) resolved.push(entry.result);
|
|
171
188
|
}
|
|
172
189
|
const baseGroups = [];
|
|
173
190
|
const baseNestedBlocks = [];
|
|
@@ -556,16 +573,16 @@ var rainbowindex = (options = {}) => {
|
|
|
556
573
|
const compilationWarnings = compiled.warnings;
|
|
557
574
|
const from = root.source?.input?.file ?? root.source?.input?.id ?? root.source?.input?.from ?? "<rainbowindex>";
|
|
558
575
|
stripRIDirectiveNodes(root);
|
|
559
|
-
if (compiled.sections.length > 0) {
|
|
560
|
-
const generatedRoot = postcss2.parse(compiled.sections.join("\n\n"), { from });
|
|
561
|
-
root.prepend(generatedRoot.nodes);
|
|
562
|
-
}
|
|
563
576
|
const slotWarnings = [];
|
|
564
577
|
warnStandaloneSlots(root, slotWarnings);
|
|
565
578
|
pushWarningsDeduped(compilationWarnings, slotWarnings, warningSeen);
|
|
566
579
|
const applyWarnings = [];
|
|
567
580
|
processApply(root, compiled.theme, applyWarnings);
|
|
568
581
|
pushWarningsDeduped(compilationWarnings, applyWarnings, warningSeen);
|
|
582
|
+
if (compiled.sections.length > 0) {
|
|
583
|
+
const generatedRoot = postcss2.parse(compiled.sections.join("\n\n"), { from });
|
|
584
|
+
root.prepend(generatedRoot.nodes);
|
|
585
|
+
}
|
|
569
586
|
const cssFnWarnings = [];
|
|
570
587
|
processCSSFunctions(root, compiled.theme, cssFnWarnings);
|
|
571
588
|
pushWarningsDeduped(compilationWarnings, cssFnWarnings, warningSeen);
|