rainbowindex 0.5.0 → 0.6.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/README.md CHANGED
@@ -20,6 +20,8 @@ The product is not a visual design tool, a component library, or a framework abs
20
20
 
21
21
  When tradeoffs arise, we consistently prioritize composability, user control, predictable performance, explicit behavior, and correctness over convenience or familiarity.
22
22
 
23
+ Full consumer documentation lives in [docs/](docs/README.md).
24
+
23
25
  ## Install
24
26
 
25
27
  ```sh
@@ -67,15 +69,21 @@ export default defineConfig({
67
69
  ```css
68
70
  /* src/styles.css */
69
71
  @import "rainbowindex";
72
+
73
+ @color {
74
+ brand: 0.18 330;
75
+ }
70
76
  ```
71
77
 
72
78
  ```tsx
73
79
  // src/App.tsx
74
80
  export default function App() {
75
- return <div className="flex gap-4 px-6 py-3 bg-blue-500 text-white">Hello</div>;
81
+ return <div className="flex gap-4 px-6 py-3 bg-brand-500 text-white">Hello</div>;
76
82
  }
77
83
  ```
78
84
 
85
+ The default palette ships only the neutral `theme` color (plus `black`, `white`, `paper`, `ink`, `transparent`, `current`, `inherit`) — declare your palette with `@color`.
86
+
79
87
  To scaffold a fresh app instead, use the CLI:
80
88
 
81
89
  ```sh
@@ -86,14 +94,15 @@ pnpm dlx rainbowindex init
86
94
 
87
95
  ## CLI
88
96
 
89
- The `rainbowindex` binary exposes five subcommands. The default is `build`.
97
+ The `rainbowindex` binary exposes six subcommands. The default is `build`.
90
98
 
91
99
  ```
92
100
  rainbowindex <glob> [options] Generate CSS from source files
93
101
  rainbowindex init Wire Rainbow Index into the current Vite app
94
102
  rainbowindex create <dir> Scaffold a Vite app with Rainbow Index ready
95
103
  rainbowindex generate-types Generate TypeScript types for ri() autocomplete
96
- rainbowindex preload-fonts Generate <link rel="preload"> tags for resolved fonts
104
+ rainbowindex preload-fonts Print <link rel="preload"> tags for local faces marked preload
105
+ rainbowindex scan <glob> Print the class names the scanner extracts from files
97
106
  ```
98
107
 
99
108
  Common flags:
@@ -103,7 +112,7 @@ Common flags:
103
112
  | `-o`, `--output <file>` | Output CSS file path. Required with `--watch`. |
104
113
  | `--watch` | Re-run on source-file changes (chokidar). |
105
114
  | `--minify` | Minification + browser-fallback passes via LightningCSS. `--optimize` is an accepted alias. |
106
- | `--css <file>` | CSS input with directives. Auto-detected if omitted. |
115
+ | `--css <file>` | CSS input with directives. Auto-detected if omitted. For `init`/`create`: the stylesheet to create or patch (default `src/index.css`). |
107
116
  | `--strict` | Drop the string escape hatch in generated types. |
108
117
  | `--template <name>` | Vite template to scaffold (default: `react-ts`). |
109
118
 
@@ -130,7 +139,7 @@ Options:
130
139
 
131
140
  | Option | Type | Description |
132
141
  | --- | --- | --- |
133
- | `sources` | `string[]` | Glob patterns for files to scan. Can also be declared via `@source` in CSS. |
142
+ | `sources` | `string[]` | Glob patterns for files to scan. Can also be declared via `@source` in CSS. Any positive glob (here or via `@source`) replaces the default scan patterns. |
134
143
  | `cwd` | `string` | Working directory. Defaults to `process.cwd()`. |
135
144
 
136
145
  ## Vite plugin
@@ -139,7 +148,25 @@ Options:
139
148
  import rainbowindex from "rainbowindex/vite";
140
149
  ```
141
150
 
142
- Auto-detects your CSS entry, injects a PostCSS config if none exists, supports HMR with file versioning. No options required for the common case.
151
+ Auto-detects your CSS entry, injects a PostCSS config if none exists, supports HMR with file versioning. The plugin takes no options to pass PostCSS options, create a `postcss.config.js` and register the PostCSS plugin there yourself.
152
+
153
+ ## Vite+
154
+
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.
156
+
157
+ One more integration is opt-in:
158
+
159
+ ```ts
160
+ // vite.config.ts — lint rule: merge classes with ri(), not clsx/tailwind-merge
161
+ export default defineConfig({
162
+ lint: {
163
+ jsPlugins: [{ name: "rainbowindex", specifier: "rainbowindex/oxlint" }],
164
+ rules: { "rainbowindex/prefer-ri": "error" },
165
+ },
166
+ });
167
+ ```
168
+
169
+ See [docs/vite-plus.md](docs/vite-plus.md).
143
170
 
144
171
  ## Class syntax
145
172
 
@@ -152,7 +179,7 @@ Roughly the same surface area as Tailwind: spacing, sizing, typography, color, l
152
179
  Prefix any utility with one or more variants, separated by `:`.
153
180
 
154
181
  ```html
155
- <button class="bg-blue-500 hover:bg-blue-600 dark:bg-blue-400 sm:px-6">…</button>
182
+ <button class="bg-brand-500 hover:bg-brand-600 dark:bg-brand-400 sm:px-6">…</button>
156
183
  ```
157
184
 
158
185
  Supported variants:
@@ -160,11 +187,13 @@ Supported variants:
160
187
  - **Pseudo-classes** — `hover`, `focus`, `focus-visible`, `active`, `visited`, `disabled`, `enabled`, `checked`, `empty`, `first`, `last`, `odd`, `even`, `only`
161
188
  - **Pseudo-elements** — `before`, `after`, `placeholder`, `file`, `marker`, `selection`, `first-line`, `first-letter`, `backdrop`
162
189
  - **Media** — `dark`, `print`, `portrait`, `landscape`, `motion-safe`, `motion-reduce`, `starting`
163
- - **Breakpoints** — `sm`, `md`, `lg`, `xl` (theme-driven, customizable via `@breakpoint`)
164
- - **Container queries** — `@sm`, `@md`,
190
+ - **Breakpoints** — every name from `@breakpoint`; none ships
191
+ - **Container queries** — the same names with an `@` prefix
165
192
  - **Attribute selectors** — `data-[state=open]`, `aria-[pressed=true]`
166
193
  - **Arbitrary** — `[selector]`, `[@media(...)]`
167
194
 
195
+ This list is a subset — the full variant tables are in [docs/class-syntax.md](docs/class-syntax.md).
196
+
168
197
  ### Arbitrary values
169
198
 
170
199
  ```html
@@ -183,24 +212,21 @@ When multiple utilities share the same variant prefix, group them with `{…}` i
183
212
 
184
213
  ```html
185
214
  <!-- These two lines are equivalent -->
186
- <div class="hover:text-red-500 hover:bg-blue-100 hover:underline">…</div>
187
- <div class="hover:{text-red-500 bg-blue-100 underline}">…</div>
215
+ <div class="hover:text-brand-500 hover:bg-brand-100 hover:underline">…</div>
216
+ <div class="hover:{text-brand-500 bg-brand-100 underline}">…</div>
188
217
  ```
189
218
 
190
- Groups stack and nest:
219
+ Prefixes chain:
191
220
 
192
221
  ```html
193
222
  <!-- Chained variants -->
194
- <div class="sm:hover:{bg-gray-700 text-white}">…</div>
223
+ <div class="sm:hover:{bg-theme-700 text-white}">…</div>
195
224
 
196
225
  <!-- Multiple groups in one class string -->
197
- <div class="focus:{outline-2 outline-blue-500} disabled:{opacity-50 cursor-not-allowed}">…</div>
198
-
199
- <!-- The data-attribute case -->
200
- <div class="data-[active]:{relative px-2} data-[slot=sidebar]:{fixed leading-none}">…</div>
226
+ <div class="focus:{outline-2 outline-brand-500} disabled:{opacity-50 cursor-not-allowed}">…</div>
201
227
  ```
202
228
 
203
- 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.
229
+ Expansion happens at scan-time, so the runtime never sees the grouped form. Only plain prefixes work bracketed variants like `data-[state=open]:` cannot prefix a group, and braces do not nest (chain the prefixes instead). Expansion input is capped at 500,000 characters, output at 100,000.
204
230
 
205
231
  ## Theming with CSS directives
206
232
 
@@ -211,8 +237,8 @@ Customization happens in your CSS input, not a JS config. The engine recognizes:
211
237
  | `@color` | Define color tokens. Supports generative (`chroma hue`), explicit (`oklch(...)`, `#rrggbb`), light/dark pairs, and aliases. |
212
238
  | `@spacing` | Set the spacing base unit. |
213
239
  | `@text` | Define text size tokens (`size, line-height`). |
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
- | `@rounded` | Border-radius tokens; modifier sets corner shape (`round`, `squircle`, `superellipse(N)`, etc). |
240
+ | `@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), and named radii. Unnamed radii are spacing multiples: `rounded-4`. |
216
242
  | `@fluid` | Configure fluid type/spacing range. |
217
243
  | `@animate` | Register named animations with inline `@keyframes`. |
218
244
  | `@utility` | Define a custom utility (static or functional `name-*`). |
@@ -221,7 +247,12 @@ Customization happens in your CSS input, not a JS config. The engine recognizes:
221
247
  | `@slot` | Slot marker inside `@custom` block form. |
222
248
  | `@source` | Declare additional source globs from CSS. Supports `not "..."` and `inline("...")`. |
223
249
  | `@preflight` | Toggle preflight base styles. |
224
- | `@layer`, `@media`, `@custom-media`, `@import` | Standard CSS plus a few extensions. |
250
+ | `@breakpoint`, `@shadow`, `@weight`, `@ease`, `@blur`, `@z`, `@leading`, `@tracking`, `@opacity`, `@duration` | Key-value token scales; `!key;` removes a token. |
251
+ | `@register` | Emit CSS `@property` registrations. |
252
+ | `@layer` | Place the generated output in cascade layers (intercepted, own grammar). |
253
+ | `@media`, `@import`, other standard at-rules | Standard CSS — passed through untouched. |
254
+
255
+ 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).
225
256
 
226
257
  Example:
227
258
 
@@ -250,11 +281,11 @@ Example:
250
281
  }
251
282
  }
252
283
 
253
- @source "../emails/**/*.html";
254
- @source not "../**/legacy/*";
284
+ @source "emails/**/*.html";
285
+ @source not "src/**/legacy/*";
255
286
  ```
256
287
 
257
- Default theme keys: `colors`, `spacing`, `text`, `breakpoints`, `rounded`, `shadows`, `weights`, `easing`, `fluid`, `animations`, `blur`, `z`.
288
+ 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.
258
289
 
259
290
  ## `ri()` — runtime class merger
260
291
 
@@ -263,8 +294,8 @@ Default theme keys: `colors`, `spacing`, `text`, `breakpoints`, `rounded`, `shad
263
294
  ```ts
264
295
  import { ri } from "rainbowindex";
265
296
 
266
- ri("px-2 py-1", isActive && "bg-blue-500", "px-4");
267
- // → "py-1 bg-blue-500 px-4" (px-2 is dropped — px-4 wins)
297
+ ri("px-2 py-1", isActive && "bg-brand-500", "px-4");
298
+ // → "py-1 bg-brand-500 px-4" (px-2 is dropped — px-4 wins)
268
299
  ```
269
300
 
270
301
  Accepted inputs:
@@ -297,17 +328,17 @@ themes in the same Node process, that shared state will leak — use
297
328
  // Anywhere ri() is single-compile-safe (browser, Vite, PostCSS):
298
329
  import { ri } from "rainbowindex";
299
330
 
300
- const className = ri("px-2 py-1", isActive && "bg-blue-500", "px-4");
331
+ const className = ri("px-2 py-1", isActive && "bg-brand-500", "px-4");
301
332
  ```
302
333
 
303
334
  ```ts
304
- // SSR / multi-tenant — capture the snapshot at compile time, bind per-request:
305
- import { compileProject, createRi, finalizeCompilationContext } from "rainbowindex";
335
+ // SSR / multi-tenant — bind a merger to one compile, isolated per theme:
336
+ import { compileProject, createRi } from "rainbowindex";
337
+ import { createThemeSnapshot } from "rainbowindex/editor";
306
338
 
307
339
  // At server startup (once per theme):
308
- const ctx = await compileProject({ cwd, css });
309
- const snapshot = finalizeCompilationContext(ctx);
310
- const ri = createRi(snapshot);
340
+ const result = await compileProject({ css });
341
+ const ri = createRi(createThemeSnapshot(result.theme));
311
342
 
312
343
  // In your request handler:
313
344
  function render(req, res) {
@@ -316,9 +347,9 @@ function render(req, res) {
316
347
  }
317
348
  ```
318
349
 
319
- If you call `ri()` while a compilation is still in progress (e.g. inside a
320
- server-rendering pass that triggers a fresh compile), the runtime emits a
321
- throttled `[RI-2004]` warning. That's your signal to switch to `createRi()`.
350
+ In any Node/SSR process, the default `ri()` emits a throttled `[RI-2004]`
351
+ warning (at most once per 60 s) to flag its shared module state. Switching to
352
+ `createRi(snapshot)` silences it.
322
353
 
323
354
  ## Editor tooling API
324
355
 
@@ -370,16 +401,16 @@ Warnings carry `RI-NNNN` codes. Ranges:
370
401
 
371
402
  | Range | Subsystem |
372
403
  | --- | --- |
404
+ | 00xx | PostCSS plugin bootstrap (thrown) |
373
405
  | 10xx | Compilation & directives |
374
- | 11xx | Color directives |
406
+ | 11xx | Color directives & resolver catch-alls |
375
407
  | 12xx | Font system |
376
408
  | 13xx | Merge / compilation context |
377
409
  | 14xx | Source scanner |
378
410
  | 15xx | Typography utilities |
379
411
  | 16xx | Integration plugins (Vite, PostCSS, CLI wiring) |
380
- | 20xx | CSS function processing |
381
- | 21xx | `ri()` runtime |
412
+ | 20xx | CSS functions, `ri()` runtime & `compile()` validation |
382
413
 
383
- See the [diagnostics reference](https://rainbowindex.dev/docs/diagnostics) for the full code → cause → fix table.
414
+ See [docs/diagnostics.md](docs/diagnostics.md) for the full code → cause → fix table.
384
415
 
385
416
  Warnings are deduplicated and capped at 200 per compile, with 20 slots reserved for high-severity errors.
package/dist/browser.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export { C as ColorDefinition, a as CompilationContext, b as CompilationSnapshot, 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-ruu2x_jR.js';
2
- export { D as DEFAULT_TEXT_SIZES, c as createRi, r as ri, s as safelist } from './safelist-D9-Plqta.js';
1
+ export { C as ColorDefinition, a as CompilationContext, b as CompilationSnapshot, 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 './index-DSgpB6bS.js';
2
+ export { c as createRi, r as ri, s as safelist } from './safelist-CGCtF-Fr.js';
3
3
 
4
4
  declare function browserEntryUnavailable(): never;
5
5
 
package/dist/browser.mjs CHANGED
@@ -2,7 +2,6 @@ import {
2
2
  safelist
3
3
  } from "./chunk-PD4ZXGJ6.mjs";
4
4
  import {
5
- DEFAULT_TEXT_SIZES,
6
5
  createCompilationContext,
7
6
  createRi,
8
7
  defaultTheme,
@@ -13,7 +12,7 @@ import {
13
12
  registerCustomTextSizes,
14
13
  registerCustomUtility,
15
14
  ri
16
- } from "./chunk-KRZL4IDK.mjs";
15
+ } from "./chunk-6U4IOFOS.mjs";
17
16
 
18
17
  // src/entries/browser.ts
19
18
  function browserEntryUnavailable() {
@@ -26,7 +25,6 @@ function browserEntryUnavailable() {
26
25
  }
27
26
  var browser_default = browserEntryUnavailable;
28
27
  export {
29
- DEFAULT_TEXT_SIZES,
30
28
  createCompilationContext,
31
29
  createRi,
32
30
  browser_default as default,
@@ -1,26 +1,27 @@
1
1
  import {
2
2
  compileScannedProject
3
- } from "./chunk-DT5HYIM3.mjs";
3
+ } from "./chunk-WK6S4HTC.mjs";
4
4
  import {
5
5
  APPLY_ALIASES,
6
6
  DIRECTIVE_NAMES_SET,
7
7
  MAX_DIRECTIVE_INPUT_SIZE,
8
8
  RI_IMPORT_SPECIFIER_ALTERNATION,
9
9
  applyVariantWrappers,
10
+ checkAppliedFontWeights,
10
11
  compileCSSFunctions,
11
12
  computeSortKey,
12
13
  expandVariantGroups,
13
- forEachApplyClass,
14
- getCustomUtility,
14
+ forEachApplyClassList,
15
15
  hasApplyLikeDirective,
16
16
  hasCSSFunctions,
17
17
  hasRIActivation,
18
+ matchCustomUtility,
18
19
  parseUtility,
19
20
  pushWarningsDeduped,
20
21
  resolveUtilityDeclarations,
21
22
  resolveVariant,
22
23
  splitSelectorList
23
- } from "./chunk-ZI5ZYNSU.mjs";
24
+ } from "./chunk-KSNYSR3C.mjs";
24
25
 
25
26
  // src/integrations/postcss/index.ts
26
27
  import postcss2 from "postcss";
@@ -28,6 +29,7 @@ import { isAbsolute } from "path";
28
29
 
29
30
  // src/integrations/postcss/apply.ts
30
31
  import postcss from "postcss";
32
+ var applyStateByTheme = /* @__PURE__ */ new WeakMap();
31
33
  function makeDeclGroup(r) {
32
34
  const sortKey = computeSortKey(r.declarations[0]?.property ?? "");
33
35
  return {
@@ -94,22 +96,29 @@ function resolveFullNestingSelector(rule) {
94
96
  }
95
97
  return composeNestedSelectors(parts);
96
98
  }
97
- function processApply(root, theme, warnings) {
99
+ function processApply(root, theme, warnings, cssPath) {
98
100
  for (const alias of APPLY_ALIASES) {
99
101
  root.walkAtRules(alias, (atRule) => {
100
102
  atRule.name = "apply";
101
103
  });
102
104
  }
103
- const customVariantMap = new Map(theme.customVariants.map((cv) => [cv.name, cv]));
104
- const checkedApplyRoots = /* @__PURE__ */ new Set();
105
- const resolveCache = /* @__PURE__ */ new Map();
105
+ let state = applyStateByTheme.get(theme);
106
+ if (!state) {
107
+ state = {
108
+ customVariantMap: new Map(theme.customVariants.map((cv) => [cv.name, cv])),
109
+ applyRootWarnings: /* @__PURE__ */ new Map(),
110
+ resolveCache: /* @__PURE__ */ new Map()
111
+ };
112
+ applyStateByTheme.set(theme, state);
113
+ }
114
+ const { customVariantMap, applyRootWarnings, resolveCache } = state;
106
115
  for (let depth = 0; depth < MAX_APPLY_DEPTH; depth++) {
107
116
  const applyNodes = [];
108
117
  const classListByNode = /* @__PURE__ */ new Map();
109
118
  const groupRoots = /* @__PURE__ */ new Set();
110
119
  root.walkAtRules("apply", (atRule) => {
111
120
  applyNodes.push(atRule);
112
- const params = expandVariantGroups(atRule.params, warnings);
121
+ const params = expandVariantGroups(atRule.params, warnings, cssPath);
113
122
  const classNames = params.trim().split(/\s+/).filter(Boolean);
114
123
  classListByNode.set(atRule, classNames);
115
124
  if (classNames.includes("group")) {
@@ -128,7 +137,7 @@ function processApply(root, theme, warnings) {
128
137
  warnings,
129
138
  customVariantMap,
130
139
  groupRoots,
131
- checkedApplyRoots,
140
+ applyRootWarnings,
132
141
  resolveCache
133
142
  );
134
143
  }
@@ -146,7 +155,7 @@ function processApply(root, theme, warnings) {
146
155
  }
147
156
  }
148
157
  }
149
- function expandApply(atRule, classNames, theme, warnings, customVariantMap, groupRoots, checkedApplyRoots, resolveCache) {
158
+ function expandApply(atRule, classNames, theme, warnings, customVariantMap, groupRoots, applyRootWarnings, resolveCache) {
150
159
  const parentRule = atRule.parent;
151
160
  if (parentRule?.type !== "rule") {
152
161
  warnings.push("[RI-1006] @apply must be used inside a CSS rule, not at the top level.");
@@ -166,6 +175,7 @@ function expandApply(atRule, classNames, theme, warnings, customVariantMap, grou
166
175
  atRule.remove();
167
176
  return;
168
177
  }
178
+ checkAppliedFontWeights(classNames, theme, warnings);
169
179
  const resolved = [];
170
180
  for (const className of classNames) {
171
181
  let entry = resolveCache.get(className);
@@ -177,7 +187,7 @@ function expandApply(atRule, classNames, theme, warnings, customVariantMap, grou
177
187
  theme,
178
188
  entryWarnings,
179
189
  customVariantMap,
180
- checkedApplyRoots
190
+ applyRootWarnings
181
191
  ),
182
192
  warnings: entryWarnings
183
193
  };
@@ -252,19 +262,24 @@ function expandApply(atRule, classNames, theme, warnings, customVariantMap, grou
252
262
  parentContainer.insertAfter(insertAfter, rule);
253
263
  insertAfter = rule;
254
264
  }
255
- const groupInfoByKey = /* @__PURE__ */ new Map();
256
- let hasGroupVariants = false;
257
- for (const [key, bucket] of variantBuckets) {
258
- const info = resolveGroupVariantInfo(bucket.wrappers, parentRule, groupRoots);
259
- groupInfoByKey.set(key, info);
260
- if (info) hasGroupVariants = true;
261
- }
262
265
  let fullSelectorMemo = null;
263
266
  const fullNestingSelector = () => {
264
267
  if (fullSelectorMemo === null)
265
268
  fullSelectorMemo = resolveFullNestingSelector(parentRule);
266
269
  return fullSelectorMemo;
267
270
  };
271
+ const groupInfoByKey = /* @__PURE__ */ new Map();
272
+ let hasGroupVariants = false;
273
+ for (const [key, bucket] of variantBuckets) {
274
+ const info = resolveGroupVariantInfo(
275
+ bucket.wrappers,
276
+ parentRule,
277
+ groupRoots,
278
+ fullNestingSelector
279
+ );
280
+ groupInfoByKey.set(key, info);
281
+ if (info) hasGroupVariants = true;
282
+ }
268
283
  let docRootMemo = null;
269
284
  const docRoot = () => {
270
285
  if (docRootMemo === null) docRootMemo = parentContainer.root();
@@ -276,7 +291,7 @@ function expandApply(atRule, classNames, theme, warnings, customVariantMap, grou
276
291
  if (groupVariantInfo) {
277
292
  const fullSelector = fullNestingSelector();
278
293
  const { groupRootSelector } = groupVariantInfo;
279
- const rootBranches = splitSelectorList(groupRootSelector);
294
+ const rootBranches = groupVariantInfo.isSelf ? [] : splitSelectorList(groupRootSelector);
280
295
  const fullBranches = splitSelectorList(fullSelector);
281
296
  const baseBranches = [];
282
297
  for (const fb of fullBranches) {
@@ -332,7 +347,7 @@ function expandApply(atRule, classNames, theme, warnings, customVariantMap, grou
332
347
  }
333
348
  atRule.remove();
334
349
  }
335
- function resolveGroupVariantInfo(wrappers, parentRule, groupRoots) {
350
+ function resolveGroupVariantInfo(wrappers, parentRule, groupRoots, fullNestingSelector) {
336
351
  if (groupRoots.size === 0) return null;
337
352
  const hasGroupVariant = wrappers.some(
338
353
  (w) => w.selectorSuffix && w.replaceAmpersand && GROUP_VARIANT_RE.test(w.selectorSuffix)
@@ -340,28 +355,31 @@ function resolveGroupVariantInfo(wrappers, parentRule, groupRoots) {
340
355
  if (!hasGroupVariant) return null;
341
356
  const groupInfo = findGroupAncestor(parentRule, groupRoots);
342
357
  if (!groupInfo) return null;
358
+ const isSelf = fullNestingSelector() === groupInfo.groupRootSelector;
343
359
  const rewrittenWrappers = wrappers.map((w) => {
344
360
  if (!w.selectorSuffix || !w.replaceAmpersand) return w;
345
361
  const match = w.selectorSuffix.match(GROUP_VARIANT_RE);
346
362
  if (!match) return w;
347
363
  const pseudo = match[1];
364
+ if (isSelf) return { ...w, selectorSuffix: `&${pseudo}` };
348
365
  const rootBranches = splitSelectorList(groupInfo.groupRootSelector);
349
366
  const newSuffix = rootBranches.map((b) => `${b}${pseudo} &`).join(", ");
350
367
  return { ...w, selectorSuffix: newSuffix };
351
368
  });
352
- return { rewrittenWrappers, groupRootSelector: groupInfo.groupRootSelector };
369
+ return { rewrittenWrappers, groupRootSelector: groupInfo.groupRootSelector, isSelf };
353
370
  }
354
371
  var MARKER_CLASSES = /* @__PURE__ */ new Set(["group"]);
355
372
  var MAX_CUSTOM_APPLY_DEPTH = 5;
356
- function findCustomUtility(utility, value, theme) {
357
- const target = value === null ? utility : `${utility}-${value}`;
358
- const cu = getCustomUtility(theme, target);
359
- return cu && !cu.functional ? cu : void 0;
373
+ function findCustomUtility(utility, value, negative, theme) {
374
+ return matchCustomUtility(utility, value, negative, theme)?.cu;
360
375
  }
361
376
  function checkCustomApplyWarnings(cu, theme, warnings, visiting = /* @__PURE__ */ new Set()) {
362
377
  if (!hasApplyLikeDirective(cu.body)) return;
363
378
  const innerClasses = [];
364
- forEachApplyClass(cu.body, (cls) => innerClasses.push(cls));
379
+ forEachApplyClassList(cu.body, (classes) => {
380
+ checkAppliedFontWeights(classes, theme, warnings);
381
+ innerClasses.push(...classes);
382
+ });
365
383
  if (innerClasses.length === 0) return;
366
384
  if (visiting.has(cu.name)) {
367
385
  warnings.push(
@@ -378,12 +396,12 @@ function checkCustomApplyWarnings(cu, theme, warnings, visiting = /* @__PURE__ *
378
396
  visiting.add(cu.name);
379
397
  for (const innerClass of innerClasses) {
380
398
  const parsed = parseUtility(innerClass);
381
- const innerCu = findCustomUtility(parsed.utility, parsed.value, theme);
399
+ const innerCu = findCustomUtility(parsed.utility, parsed.value, parsed.negative, theme);
382
400
  if (innerCu) checkCustomApplyWarnings(innerCu, theme, warnings, visiting);
383
401
  }
384
402
  visiting.delete(cu.name);
385
403
  }
386
- function resolveClassName(className, theme, warnings, customVariantMap, checkedApplyRoots) {
404
+ function resolveClassName(className, theme, warnings, customVariantMap, applyRootWarnings) {
387
405
  const parsed = parseUtility(className);
388
406
  if (parsed.variants.length === 0 && MARKER_CLASSES.has(parsed.utility) && parsed.value === null) {
389
407
  return null;
@@ -394,10 +412,15 @@ function resolveClassName(className, theme, warnings, customVariantMap, checkedA
394
412
  return null;
395
413
  }
396
414
  const declarations = [...utilResult.declarations];
397
- const cu = findCustomUtility(parsed.utility, parsed.value, theme);
398
- if (cu && !checkedApplyRoots.has(cu.name)) {
399
- checkedApplyRoots.add(cu.name);
400
- checkCustomApplyWarnings(cu, theme, warnings);
415
+ const cu = findCustomUtility(parsed.utility, parsed.value, parsed.negative, theme);
416
+ if (cu) {
417
+ let cuWarnings = applyRootWarnings.get(cu.name);
418
+ if (!cuWarnings) {
419
+ cuWarnings = [];
420
+ checkCustomApplyWarnings(cu, theme, cuWarnings);
421
+ applyRootWarnings.set(cu.name, cuWarnings);
422
+ }
423
+ warnings.push(...cuWarnings);
401
424
  }
402
425
  const variantWrappers = [];
403
426
  for (const variant of parsed.variants) {
@@ -564,28 +587,29 @@ var rainbowindex = (options = {}) => {
564
587
  if (!hasActivation) {
565
588
  return;
566
589
  }
590
+ const from = root.source?.input?.file ?? root.source?.input?.id ?? root.source?.input?.from ?? "<rainbowindex>";
567
591
  const { compiled, warningSeen } = await compileScannedProject({
568
592
  css: rawCSS,
569
593
  cwd,
594
+ cssPath: from,
570
595
  surfacePatterns: options.sources,
571
596
  onInvalidPattern: (err) => `[RI-1015] ${err}`
572
597
  });
573
598
  const compilationWarnings = compiled.warnings;
574
- const from = root.source?.input?.file ?? root.source?.input?.id ?? root.source?.input?.from ?? "<rainbowindex>";
575
599
  stripRIDirectiveNodes(root);
576
600
  const slotWarnings = [];
577
601
  warnStandaloneSlots(root, slotWarnings);
578
- pushWarningsDeduped(compilationWarnings, slotWarnings, warningSeen);
602
+ pushWarningsDeduped(compilationWarnings, slotWarnings, warningSeen, compiled.suppressed);
579
603
  const applyWarnings = [];
580
- processApply(root, compiled.theme, applyWarnings);
581
- pushWarningsDeduped(compilationWarnings, applyWarnings, warningSeen);
604
+ processApply(root, compiled.theme, applyWarnings, from);
605
+ pushWarningsDeduped(compilationWarnings, applyWarnings, warningSeen, compiled.suppressed);
582
606
  if (compiled.sections.length > 0) {
583
607
  const generatedRoot = postcss2.parse(compiled.sections.join("\n\n"), { from });
584
608
  root.prepend(generatedRoot.nodes);
585
609
  }
586
610
  const cssFnWarnings = [];
587
611
  processCSSFunctions(root, compiled.theme, cssFnWarnings);
588
- pushWarningsDeduped(compilationWarnings, cssFnWarnings, warningSeen);
612
+ pushWarningsDeduped(compilationWarnings, cssFnWarnings, warningSeen, compiled.suppressed);
589
613
  for (const warning of compilationWarnings) {
590
614
  result.warn(warning);
591
615
  }