@menapo/vicss 1.0.0 → 1.1.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
@@ -2,16 +2,18 @@
2
2
 
3
3
  Typed, design-system-first atomic CSS for Vite.
4
4
 
5
- vicss is a Vite 8 plugin and a runtime of under half a kilobyte (about 0.8 KB with recipes). You describe your design system once, in a typed config: tokens, semantic tokens, modes, conditions. You write styles as typed objects that use that system, next to your components. At build time the plugin checks every value, compiles the styles to atomic CSS in one order-proof stylesheet, and replaces each style with a constant that names its classes, short hashes in production. Overrides merge by property and condition, so `sx(component, props.style)` does what you expect without tailwind-merge, `!important` or load-order tricks. Recipes give you tailwind-variants' API (variants, compound variants, slots, `extends`) over style objects, with responsive variants.
5
+ vicss is a Vite 8 plugin and a runtime of about half a kilobyte (about 0.8 KB with recipes). You describe your design system once, in a typed config: tokens, semantic tokens, modes, conditions. You write styles as typed objects that use that system, next to your components. At build time the plugin checks every value, compiles the styles to atomic CSS in one order-proof stylesheet, and replaces each style with a constant that names its classes, short hashes in production. One style is an element's class (`class={card}`) or its inline style (`style={card}`), in every framework. Overrides merge by property and condition, so `sx(component, props.style)` does what you expect without tailwind-merge, `!important` or load-order tricks. Recipes give you tailwind-variants' API (variants, compound variants, slots, `extends`) over style objects, with responsive variants.
6
6
 
7
7
  ```tsx
8
8
  import { css, sx } from "@menapo/vicss";
9
9
 
10
10
  const card = css({ padding: { base: 4, _md: 6 }, borderRadius: "lg", background: "surface" });
11
11
 
12
- <section {...sx(card, props.style)}>…</section>;
12
+ <section className={card}>…</section>; // class={card} outside React and Preact
13
13
  // dev: <section class="bg_surface md:p_6 p_4 r_lg">
14
14
  // build: <section class="v1x7m v8q1z vk2f9 v0c3d">
15
+
16
+ <section {...sx(card, props.style)}>…</section>; // several styles, or a caller's override
15
17
  ```
16
18
 
17
19
  Requires Vite 8.3 or newer and Node 22.18 or newer.
@@ -20,6 +22,7 @@ Requires Vite 8.3 or newer and Node 22.18 or newer.
20
22
 
21
23
  - [Install](#install)
22
24
  - [A two-minute tour](#a-two-minute-tour)
25
+ - [One style: `class=` and `style=`](#one-style-class-and-style)
23
26
  - [Principles](#principles)
24
27
  - [Frameworks](#frameworks)
25
28
  - [Critical CSS for prerendered pages](#critical-css-for-prerendered-pages)
@@ -135,10 +138,38 @@ const title = css({ textStyle: "heading", color: "text" });
135
138
 
136
139
  Property names are CSS, in camelCase. Each value is checked against the scale of its property: `background` takes a color, `gap` a spacing step, `borderRadius` a radius. CSS keywords (`auto`, `100%`, `fit-content`) and `0` always work. A typo is a build error with a code frame, which the `vicss/valid-styles` lint rule reports in the editor; under `strict: "tokens"` it is a TypeScript error too.
137
140
 
138
- `css()` compiles to a constant, an object from each property and condition the style sets to its class (`{ p: "p_4", "md:p": "md:p_6" }`), so you can write it anywhere, including inside a component. It is not a class string: an element takes it through `sx()`. Its argument must be known at build time; a value only known at runtime goes through a function style (below). The build fails, and says why, rather than drop a style.
141
+ `css()` compiles to a constant, an object from each property and condition the style sets to its class (`{ p: "p_4", "md:p": "md:p_6" }`), so you can write it anywhere, including inside a component. Read as a string, it is its class attribute, so one style is an element's class as it is, `className={title}` (`class={title}` elsewhere), and `style={…}` takes one too ([below](#one-style-class-and-style)); several styles go through `sx()`. Its argument must be known at build time; a value only known at runtime goes through a function style (below). The build fails, and says why, rather than drop a style.
139
142
 
140
143
  `css(style, { name: "card" })` adds the class `card` to the style, as it is, for tests, theming hooks and devtools.
141
144
 
145
+ ### One style: `class=` and `style=`
146
+
147
+ One style → `class={card}`; several styles or overrides → `sx(a, b)`.
148
+
149
+ ```tsx
150
+ import { css } from "@menapo/vicss";
151
+
152
+ const card = css({ padding: 4, background: "surface" });
153
+
154
+ <div className={card} />; // class={card} outside React and Preact
155
+ <div style={css({ padding: 4, color: "text" })} />; // style="padding: calc(var(--spacing) * 4); color: var(--color-text)"
156
+ <div style={css({ color: "text", background: { base: "surface", _hover: "accent" } })} />;
157
+ // style="color: var(--color-text)" class="bg_surface hover:bg_accent"
158
+ ```
159
+
160
+ - **As a class**, a compiled style reads as its class attribute wherever a string is read: `class={card}`, `className={card}`, `` `<p class="${card}">` ``, `element.className = card`, `String(card)`. A recipe's result and a marker are styles too. Vue's `:class` and Svelte's `class` read an object as a map of classes, so the plugin points every compiled component's class calls at a version that takes a style.
161
+ - **As a style**, the plugin writes `style={css({ … })}`, and `style={card}` where `card` is a style it can see (a constant in the module, or imported from one it compiles), as a literal inline style: tokens as their `var(--…)`, an object in React, Preact and Solid, a string in Vue, Svelte, Astro and templates. An inline declaration beats every class, so it cannot carry conditions: a property with one (`_hover`, `_md`, `_dark`, a marker's state) goes to classes whole, its base value included, with the shorthands and longhands around it, since an inline `padding` would beat a hovered `paddingTop`. Those classes join the element's own `class` (`className`), static or dynamic, exactly as `sx(own, style)` merges them, or become its class when it has none. A palette, a theme and a `name` stay classes.
162
+ - **A `style=` value the plugin cannot see**, such as a prop, is the framework's own; in dev, a compiled style there fails where it renders, with the fix: `class={style}`, or `sx()`.
163
+ - A function style (runtime values) and several styles go through `sx()`, whose `style` sets the custom properties.
164
+
165
+ | Framework | One style as a class | As a style |
166
+ | ------------- | ------------------------------------------------ | ---------------------------------------------- |
167
+ | React, Preact | `className={card}` | `style={card}`: `{ paddingTop: "…" }` |
168
+ | Solid | `class={card}` | `style={card}`: `{ "padding-top": "…" }` |
169
+ | Vue | `:class="card"` | `:style="card"`: `"padding-top:…"` |
170
+ | Svelte, Astro | `class={card}` | `style={card}`: `"padding-top:…"` |
171
+ | Plain JS | `` `<p class="${card}">` ``, `.className = card` | `` `<p style="${card}">` ``: `"padding-top:…"` |
172
+
142
173
  ### Applying and merging: `sx()`
143
174
 
144
175
  ```tsx
@@ -309,13 +340,13 @@ import { css, group, peer, sx, theme, type Token } from "@menapo/vicss";
309
340
  // A subtree with other token values: one class, no re-render, no provider.
310
341
  // Its token names and values are checked by the build (the editor completes the scales).
311
342
  const marketing = theme({ colors: { accent: "{colors.red.600}" }, radii: { md: "0.75rem" } });
312
- <section {...sx(marketing)}>…</section>;
343
+ <section className={marketing}>…</section>;
313
344
 
314
345
  // Relations to an ancestor or a sibling, typed: a misspelt marker is an error.
315
346
  const row = group();
316
347
  const icon = css({ opacity: { base: 0, [row.hover]: 1 } });
317
- <li {...sx(row)}>
318
- <svg {...sx(icon)} />
348
+ <li className={row}>
349
+ <svg className={icon} />
319
350
  </li>;
320
351
 
321
352
  const toggle = peer();
@@ -342,16 +373,16 @@ export default defineConfig({ presets: [tailwind], palettes: { default: "gray" }
342
373
  ```
343
374
 
344
375
  ```tsx
345
- import { css, recipe, sx } from "@menapo/vicss";
376
+ import { css, recipe } from "@menapo/vicss";
346
377
 
347
378
  const button = recipe({
348
379
  base: { background: { base: "palette.600", _hover: "palette.700" }, color: "white" },
349
380
  variants: { tone: { danger: { colorPalette: "red" } } },
350
381
  });
351
382
 
352
- <section {...sx(css({ colorPalette: "blue" }))}>
353
- <button {...sx(button())}>Blue, from the section</button>
354
- <button {...sx(button({ tone: "danger" }))}>Red</button>
383
+ <section className={css({ colorPalette: "blue" })}>
384
+ <button className={button()}>Blue, from the section</button>
385
+ <button className={button({ tone: "danger" })}>Red</button>
355
386
  </section>;
356
387
  ```
357
388
 
@@ -411,7 +442,7 @@ The plugin knows each file's framework from its extension and, for JSX, from its
411
442
 
412
443
  `vicss.d.ts` registers the project's framework, so `sx()`'s result is typed with that shape (`SxAttributes<"vue">` names any one of them). `.vue`, `.svelte` and `.astro` files are compiled after their framework's compiler; write styles in the component's script (or Astro's frontmatter) and use them in the template, since a style written in the template fails the build with a hint to move it. In dev, `sx()` adds `data-vx-src="src/Card.vue:12"`, the component's own line, in every framework's shape.
413
444
 
414
- `style` is present only when a runtime value sets a custom property. Spread the result (`{...sx(a)}` in JSX, `v-bind="sx(a)"` in Vue, `{...sx(a)}` in Svelte and Astro), or read the class where you need a string:
445
+ One style needs no `sx()`: `class={card}`, `style={card}` ([One style](#one-style-class-and-style)). `sx()`'s `style` is present only when a runtime value sets a custom property. Spread the result (`{...sx(a)}` in JSX, `v-bind="sx(a)"` in Vue, `{...sx(a)}` in Svelte and Astro), or read the class where you need a string:
415
446
 
416
447
  ```ts
417
448
  element.className = sx(card, isActive && active).class; // .className in React and Preact
@@ -506,28 +537,28 @@ vicss({
506
537
 
507
538
  ## Runtime API
508
539
 
509
- Everything below comes from `@menapo/vicss`. In an app, the plugin replaces every function except `sx` and recipe calls with constants; only those ship to the browser. In React and Preact that is 443 B gzip for `sx`, 782 B with the recipe resolver and 1,017 B with responsive recipes (Solid and Vue: the same). Svelte, Astro and vanilla JS get `style` as a string, which costs more: 486, 822 and 1,054 B. Figures as the size tests measure them; see [the benchmarks](./docs/benchmarks.md#2-runtime-bytes).
510
-
511
- | Function | Returns | Notes |
512
- | ----------------------- | --------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
513
- | `css(style)` | `Style`: an object from merge id to class (`{ p: "p_4" }`) | The argument must be static; apply it with `sx()` |
514
- | `css(style, { name })` | `Style`, with the class `name` as it is | A stable class for tests, theming hooks and devtools |
515
- | `css((…args) => style)` | `(…args) => DynamicStyle` | A runtime value becomes a custom property the class reads |
516
- | `sx(...styles)` | The attributes of your framework (see [Frameworks](#frameworks)) | Takes styles, recipe results, class names of your own (kept as they are, first), `false`/`null`/`undefined` and arrays; later wins per property and condition, and one theme and one flattened recipe base per element |
517
- | `recipe(definition)` | `Recipe`: `(props?) => Style`, or an object of styles with slots | Plus `variantKeys`, `splitProps(props)`, `schema`; `flatten: true` compiles the base to one class ([Flattened bases](#flattened-bases)) |
518
- | `theme(overrides)` | `Style`: a class that overrides token values for a subtree | Token names and values are checked by the build, with a suggestion; `sx()` keeps the last theme on an element, and warns when it drops one |
519
- | `group(name?)` | `Marker`: a style whose states are condition keys for descendants (`[row.hover]`) | Apply it with `sx(row)` |
520
- | `peer(name?)` | `Marker`: the same, for following siblings (`[toggle.checked]`) | |
521
- | `keyframes(frames)` | `string`: the generated animation name | `css({ animationName: fadeIn })` |
522
- | `token(path)` | `string`: `var(--…)` | `token("colors.accent")` is `"var(--color-accent)"`, for inline styles and third-party APIs |
523
-
524
- | Type | Is |
525
- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
526
- | `Style<Props?>` | A compiled style, not a class string (`className={css(…)}` is a type error: spread `{...sx(style)}`). `Style<"margin" \| "width">` limits what a caller may pass: `type CardProps = { style?: Style<"margin" \| "maxWidth"> }` |
527
- | `Token<Scale>` | A token path of one scale: `Token<"colors">` is `"accent" \| "blue.600" \| …` |
528
- | `Palette` | A palette name, for a runtime `colorPalette`: `css((tone: Palette) => ({ colorPalette: tone }))` |
529
- | `Variants<typeof recipe>` | A recipe's props: `{ intent?: "primary" \| "ghost"; size?: "sm" \| "md" \| { base?, _md?, … }; block?: boolean }` |
530
- | `SxAttributes<F?>` | What `sx()` returns: in the framework `vicss.d.ts` registers, or in framework `F` (`SxAttributes<"svelte">`) |
540
+ Everything below comes from `@menapo/vicss`. In an app, the plugin replaces every function except `sx` and recipe calls with constants; only those ship to the browser. In React and Preact that is 496 B gzip for `sx` and the prototype every compiled style shares (its `toString`), 835 B with the recipe resolver and 1,071 B with responsive recipes (Solid and Vue: the same). Svelte, Astro and vanilla JS get `style` as a string, which costs more: 536, 876 and 1,109 B. Figures as the size tests measure them; see [the benchmarks](./docs/benchmarks.md#2-runtime-bytes).
541
+
542
+ | Function | Returns | Notes |
543
+ | ----------------------- | ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
544
+ | `css(style)` | `Style`: an object from merge id to class (`{ p: "p_4" }`), its class attribute as a string | The argument must be static; one style is `class={style}` or `style={style}`, several go through `sx()` |
545
+ | `css(style, { name })` | `Style`, with the class `name` as it is | A stable class for tests, theming hooks and devtools |
546
+ | `css((…args) => style)` | `(…args) => DynamicStyle` | A runtime value becomes a custom property the class reads |
547
+ | `sx(...styles)` | The attributes of your framework (see [Frameworks](#frameworks)) | Takes styles, recipe results, class names of your own (kept as they are, first), `false`/`null`/`undefined` and arrays; later wins per property and condition, and one theme and one flattened recipe base per element |
548
+ | `recipe(definition)` | `Recipe`: `(props?) => Style`, or an object of styles with slots | Plus `variantKeys`, `splitProps(props)`, `schema`; `flatten: true` compiles the base to one class ([Flattened bases](#flattened-bases)) |
549
+ | `theme(overrides)` | `Style`: a class that overrides token values for a subtree | Token names and values are checked by the build, with a suggestion; `sx()` keeps the last theme on an element, and warns when it drops one |
550
+ | `group(name?)` | `Marker`: a style whose states are condition keys for descendants (`[row.hover]`) | Apply it with `sx(row)` |
551
+ | `peer(name?)` | `Marker`: the same, for following siblings (`[toggle.checked]`) | |
552
+ | `keyframes(frames)` | `string`: the generated animation name | `css({ animationName: fadeIn })` |
553
+ | `token(path)` | `string`: `var(--…)` | `token("colors.accent")` is `"var(--color-accent)"`, for inline styles and third-party APIs |
554
+
555
+ | Type | Is |
556
+ | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
557
+ | `Style<Props?>` | A compiled style, typed as a string that records what it sets, so `className={css(…)}` and `style={css(…)}` type-check (at runtime it is an object whose `toString` is its classes). `Style<"margin" \| "width">` limits what a caller may pass: `type CardProps = { style?: Style<"margin" \| "maxWidth"> }` |
558
+ | `Token<Scale>` | A token path of one scale: `Token<"colors">` is `"accent" \| "blue.600" \| …` |
559
+ | `Palette` | A palette name, for a runtime `colorPalette`: `css((tone: Palette) => ({ colorPalette: tone }))` |
560
+ | `Variants<typeof recipe>` | A recipe's props: `{ intent?: "primary" \| "ghost"; size?: "sm" \| "md" \| { base?, _md?, … }; block?: boolean }` |
561
+ | `SxAttributes<F?>` | What `sx()` returns: in the framework `vicss.d.ts` registers, or in framework `F` (`SxAttributes<"svelte">`) |
531
562
 
532
563
  The precise types come from `vicss.d.ts`, which the plugin and `vicss types` write from your config; commit it or generate it in CI. It leaves private tokens out of `Token<Scale>` and the style types.
533
564
 
@@ -586,6 +617,10 @@ The entry's default export is the plugin (`meta`, `rules`, `configs.recommended`
586
617
 
587
618
  - **A nested theme cannot re-point an outer theme's alias.** A custom property inherits its resolved value, so a nested `theme()` that overrides `colors.blue.600` updates the config's aliases of it, not an alias the outer theme set (a CSS limit; [Design tokens](./docs/guides/design-tokens.md)).
588
619
  - **One theme and one palette per element.** `sx()` keeps the last theme it is given (and warns); a palette is chosen per element, so one declaration cannot mix two.
620
+ - **`style=` is rewritten where the plugin reads it.** JSX attributes, Vue's vnode props and server template, Svelte's setters and server template, Astro's template and plain template literals (`` `<p style="${card}">` ``). A style set another way (a spread, `v-bind="attrs"`, `element.style.cssText`) is the framework's, and kit's own templates (`@menapo/markup`, `@menapo/og`) take a style object of literal CSS, so they are left alone. In a plain HTML string, quote the attribute: an inline style holds spaces.
621
+ - **A class list that holds a style is `sx()`'s job.** Vue's `:class="[card, extra]"`, Svelte's `class={[card, extra]}` and Astro's `class:list` read the objects inside as maps of classes; one style goes in as it is (`:class="card"`), several through `sx()`.
622
+ - **An element's own class beside a split `style=` is what `sx()` takes:** a string, a style or an array of them. Vue's and Svelte's object form (`{ active: isOpen }`) there reads as a style; write `sx()` or a string.
623
+ - **A library compiled by vicss 1.0** ships plain objects: `sx()` takes them as before, and `class={…}` needs the library built with 1.1.
589
624
  - **Folding happens only in modules the plugin compiles.** An `sx()` call anywhere else runs at runtime, and `css()` and `recipe()` work only in compiled modules.
590
625
  - **A library's recipe calls are not folded in the app.** The library ships its compiled table, which the resolver runs at runtime.
591
626
  - **Templates are invisible to the CLI.** `vicss check` and the lint rules read the scripts of `.vue`, `.svelte` and `.astro` files; a style written in a template is checked by the build alone.
package/dist/cli.js CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { D as layerName, E as flatLayerName, Q as isPaletteAtom, St as isArbitrary, _t as VicssError, c as printAtoms, d as printTokens, g as normalizeStyle, s as paletteProblem, t as baseTokens, y as resolveSystem, yt as compareStrings } from "./print-Ds6fkgio.js";
3
- import { u as baseAtoms } from "./theme-C-0LpsJx.js";
4
- import { _ as isSfc, c as moduleLoader, g as frameworkOf, h as extractScripts, l as resolveFile, m as detectFramework, p as configuredJsxSource, v as compileModule, y as createModuleCache } from "./pool-BGXMhLfL.js";
5
- import { C as CompileCache, D as generateTypes, _ as critical, c as scan, f as loadLibraries, h as withLibraries, i as writeIfChanged, k as codeFrame, n as snapshotRoot, p as ownPackageName, r as typesFile, t as snapshotFile, x as hashesProblem } from "./types-file-R_knFzME.js";
6
- import { r as snapshotText } from "./snapshot-BQb8ZmSV.js";
7
- import { n as loadSystem, t as CONFIG_FILES } from "./config-loader-DzP68Snn.js";
2
+ import { $ as isPaletteAtom, Ct as isArbitrary, D as flatLayerName, O as layerName, _ as normalizeStyle, _t as VicssError, b as resolveSystem, bt as compareStrings, c as paletteProblem, f as printTokens, l as printAtoms, t as baseTokens } from "./print-B_mn7wBu.js";
3
+ import { _ as isSfc, c as moduleLoader, g as frameworkOf, h as extractScripts, l as resolveFile, m as detectFramework, p as configuredJsxSource, v as compileModule, y as createModuleCache } from "./pool-F3qb30Fy.js";
4
+ import { u as baseAtoms } from "./theme-zBcr5DHi.js";
5
+ import { C as CompileCache, D as generateTypes, _ as critical, c as scan, f as loadLibraries, h as withLibraries, i as writeIfChanged, k as codeFrame, n as snapshotRoot, p as ownPackageName, r as typesFile, t as snapshotFile, x as hashesProblem } from "./types-file-D6Iu4kzv.js";
6
+ import { r as snapshotText } from "./snapshot-DAJ8FLU4.js";
7
+ import { n as loadSystem, t as CONFIG_FILES } from "./config-loader-1kWS0cRU.js";
8
8
  import { createRequire } from "node:module";
9
9
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
10
10
  import path from "node:path";
@@ -1,4 +1,4 @@
1
- import { l as printBase, st as configError, ut as isPlainObject, y as resolveSystem } from "./print-Ds6fkgio.js";
1
+ import { b as resolveSystem, ct as configError, dt as isPlainObject, u as printBase } from "./print-B_mn7wBu.js";
2
2
  import { existsSync } from "node:fs";
3
3
  import path from "node:path";
4
4
  import { AsyncLocalStorage } from "node:async_hooks";
@@ -0,0 +1 @@
1
+ export { n as loadSystem } from "./config-loader-1kWS0cRU.js";
package/dist/index.d.ts CHANGED
@@ -819,13 +819,23 @@ declare const PROPERTIES: unique symbol;
819
819
  * that records the keys it sets. `Style<"margin" | "width">` accepts
820
820
  * `css({ margin: 4 })` and rejects `css({ color: "accent" })`, so a
821
821
  * component decides what a caller may override. A utility or `textStyle`
822
- * counts as the properties it sets. It is not a class string: an element
823
- * takes it through `sx()` (`<div {...sx(card)} />`), and a plain class
824
- * string is `sx(card).className` (`.class` outside React and Preact).
822
+ * counts as the properties it sets.
823
+ *
824
+ * One style is an element's class and its style (PLAN §5.16): its
825
+ * `toString()` is its class attribute, so it goes where a class string
826
+ * goes (`class={card}`, `className={card}`, `${card}`), and the plugin
827
+ * writes it as an inline style where it is `style=`. The type says so: a
828
+ * string that records its keys, which `className` and `class` take, and
829
+ * which React's `style` takes too through one property it shares with
830
+ * React's style object (always `undefined`: the value is no style object).
831
+ * At runtime it is an object, not a string: `String(card)` is its classes,
832
+ * and string methods are not there.
825
833
  */
826
- interface Style<K extends string = string> {
834
+ type Style<K extends string = string> = string & {
827
835
  readonly [PROPERTIES]: K;
828
- }
836
+ /** Never set: what lets React's `style` prop take a compiled style, which the plugin writes inline. */
837
+ readonly WebkitBoxOrient?: undefined;
838
+ };
829
839
  /**
830
840
  * A compiled style with runtime values (PLAN §5.8): its constant style and
831
841
  * the custom properties its classes read, which `sx()` sets inline.
@@ -1377,6 +1387,39 @@ export declare function keyframes(frames: {
1377
1387
  /** A token reference as a CSS value: `token("colors.accent")` is `var(--color-accent)`. */
1378
1388
  export declare function token(path: string): string;
1379
1389
  //#endregion
1390
+ //#region src/runtime/style.d.ts
1391
+ declare class Styled {
1392
+ /**
1393
+ * The class attribute `sx(style)` sets, by `sx()`'s own merge and cache:
1394
+ * the same string by construction, a cache hit after the first render,
1395
+ * and 31 B less than a merge of its own beside `sx()`, which nearly every
1396
+ * app ships (PLAN §6).
1397
+ */
1398
+ toString(this: StyleRecord): string;
1399
+ }
1400
+ /** What the compiler wraps a compiled style literal in (`__vxStyled({…})`). */
1401
+ declare const styled: <T extends object>(record: T) => T & Styled;
1402
+ /**
1403
+ * The classes of `sx(...args)`, as a string: what a `style=` value's
1404
+ * conditional properties add to the element's own class (core/attributes.ts).
1405
+ */
1406
+ declare const classList: (...args: StyleArg$1[]) => string;
1407
+ /**
1408
+ * A framework's class normaliser that reads an object as a map from class
1409
+ * to condition (Vue's `normalizeClass`, Svelte's `clsx`), made to take a
1410
+ * compiled style as its classes: the plugin points the compiled
1411
+ * components' calls at it (vite/attributes.ts).
1412
+ */
1413
+ declare const classWith: <A extends unknown[], R>(normalize: (value: unknown, ...rest: A) => R) => (value: unknown, ...rest: A) => R;
1414
+ /**
1415
+ * A framework function that normalises the `class` of the props objects
1416
+ * it takes (Vue's `mergeProps`, `normalizeProps` and `ssrRenderAttrs`,
1417
+ * Svelte's server `attributes`), made to take a compiled style there too.
1418
+ */
1419
+ declare const propsWith: <A extends unknown[], R>(merge: (...args: A) => R) => (...args: A) => R;
1420
+ /** Svelte's `attribute_effect(element, fn, …)`: the props `fn` returns, as `propsWith` takes them. */
1421
+ declare const effectWith: <A extends unknown[], R>(effect: (element: unknown, fn: (...values: never[]) => unknown, ...rest: A) => R) => (element: unknown, fn: (...values: never[]) => unknown, ...rest: A) => R;
1422
+ //#endregion
1380
1423
  //#region src/runtime/dev.d.ts
1381
1424
  /** What every dev variant adds. A `data-*` name, which each framework passes through as it is. */
1382
1425
  interface Source {
@@ -1408,6 +1451,20 @@ declare const sxDevString: (src: string, ...args: StyleArg$1[]) => {
1408
1451
  class: string;
1409
1452
  style?: string;
1410
1453
  } & Source;
1454
+ /**
1455
+ * Dev only (PLAN §5.16): a `style=` value the compiler could not write as
1456
+ * an inline style. A compiled style there would set nothing a browser
1457
+ * reads (its keys are merge ids), so it fails where the element is
1458
+ * rendered, naming the fix; any other value passes through.
1459
+ */
1460
+ declare const styleCheck: <T>(value: T, src: string) => T;
1461
+ /**
1462
+ * A framework's style setter that checks its value at `at` first: what the
1463
+ * plugin points a compiled component's calls at in dev
1464
+ * (vite/attributes.ts). With `key`, only when the call sets that attribute
1465
+ * (Astro's `addAttribute(value, "style")`).
1466
+ */
1467
+ declare const styleWith: <A extends unknown[], R>(set: (...args: A) => R, at: number, src: string, key?: string) => (...args: A) => R;
1411
1468
  //#endregion
1412
1469
  //#region src/runtime/recipe.d.ts
1413
1470
  /** One table cell: a style, or a style per slot. */
@@ -1468,4 +1525,4 @@ export declare const __vxResponsive: Responsive;
1468
1525
  */
1469
1526
  export declare const sx: Sx;
1470
1527
  //#endregion
1471
- export { type CompoundSlot, type CompoundVariant, type ConditionalObject, type ConditionalValue, type Conditions, type CssOptions, type CssProperties, type CssResult, type DynamicStyle, type Marker, type MarkerKey, type MarkerState, type NumberKind, type Palette, type Recipe, type RecipeDefinition, type RecipeProps, type RecipeResult, type RecipeShape, type Register, type RegisteredFramework, type ResponsiveValue, type ScaleName, type ScaleValue, type Strict, type Style, type StyleArg, type StyleAttributes, type StyleFunction, type StyleInput, type StyleInputValue, type StyleObject, type SxAttributes, type SxShapes, type TextStyles, type Token, type Tokens, type Unscaled, type Utilities, type VariantValue, type Variants, devToken as __vxDevToken, devValue as __vxDevValue, flatBases as __vxFlatBases, sxClass as __vxSxClass, sxDev as __vxSxDev, sxDevClass as __vxSxDevClass, sxDevString as __vxSxDevString, sxString as __vxSxString };
1528
+ export { type CompoundSlot, type CompoundVariant, type ConditionalObject, type ConditionalValue, type Conditions, type CssOptions, type CssProperties, type CssResult, type DynamicStyle, type Marker, type MarkerKey, type MarkerState, type NumberKind, type Palette, type Recipe, type RecipeDefinition, type RecipeProps, type RecipeResult, type RecipeShape, type Register, type RegisteredFramework, type ResponsiveValue, type ScaleName, type ScaleValue, type Strict, type Style, type StyleArg, type StyleAttributes, type StyleFunction, type StyleInput, type StyleInputValue, type StyleObject, type SxAttributes, type SxShapes, type TextStyles, type Token, type Tokens, type Unscaled, type Utilities, type VariantValue, type Variants, classList as __vxClassList, classWith as __vxClassWith, devToken as __vxDevToken, devValue as __vxDevValue, effectWith as __vxEffectWith, flatBases as __vxFlatBases, propsWith as __vxPropsWith, styleCheck as __vxStyleCheck, styleWith as __vxStyleWith, styled as __vxStyled, sxClass as __vxSxClass, sxDev as __vxSxDev, sxDevClass as __vxSxDevClass, sxDevString as __vxSxDevString, sxString as __vxSxString };
package/dist/index.js CHANGED
@@ -113,6 +113,59 @@ function token() {
113
113
  return notCompiled("token");
114
114
  }
115
115
  //#endregion
116
+ //#region src/runtime/style.ts
117
+ /**
118
+ * One compiled style as a class (PLAN §5.6, §5.16): `class={card}`,
119
+ * `className={card}` and `${card}` read it as its class attribute. A
120
+ * compiled style keeps its shape, merge id → class, so `sx()` still merges
121
+ * it; the string comes from the prototype every compiled style shares. A
122
+ * class method is not enumerable, so the merge's `for…in`, `Object.keys`
123
+ * and a spread never see it, and a constant carries no function of its own.
124
+ */
125
+ var Styled = class {
126
+ /**
127
+ * The class attribute `sx(style)` sets, by `sx()`'s own merge and cache:
128
+ * the same string by construction, a cache hit after the first render,
129
+ * and 31 B less than a merge of its own beside `sx()`, which nearly every
130
+ * app ships (PLAN §6).
131
+ */
132
+ toString() {
133
+ return classes([this], {});
134
+ }
135
+ };
136
+ /** What the compiler wraps a compiled style literal in (`__vxStyled({…})`). */
137
+ const styled = (record) => Object.setPrototypeOf(record, Styled.prototype);
138
+ /** Whether `value` is a compiled style: one the compiler wrote, or a recipe's result. */
139
+ const isStyled = (value) => value instanceof Styled;
140
+ /**
141
+ * The classes of `sx(...args)`, as a string: what a `style=` value's
142
+ * conditional properties add to the element's own class (core/attributes.ts).
143
+ */
144
+ const classList = (...args) => classes(args, {});
145
+ /**
146
+ * A framework's class normaliser that reads an object as a map from class
147
+ * to condition (Vue's `normalizeClass`, Svelte's `clsx`), made to take a
148
+ * compiled style as its classes: the plugin points the compiled
149
+ * components' calls at it (vite/attributes.ts).
150
+ */
151
+ const classWith = (normalize) => (value, ...rest) => normalize(value instanceof Styled ? "" + value : value, ...rest);
152
+ /** Props whose `class` is a compiled style, with its classes there instead. */
153
+ const plain = (props) => {
154
+ const own = props;
155
+ return own && own.class instanceof Styled ? {
156
+ ...own,
157
+ class: "" + own.class
158
+ } : props;
159
+ };
160
+ /**
161
+ * A framework function that normalises the `class` of the props objects
162
+ * it takes (Vue's `mergeProps`, `normalizeProps` and `ssrRenderAttrs`,
163
+ * Svelte's server `attributes`), made to take a compiled style there too.
164
+ */
165
+ const propsWith = (merge) => (...args) => merge(...args.map(plain));
166
+ /** Svelte's `attribute_effect(element, fn, …)`: the props `fn` returns, as `propsWith` takes them. */
167
+ const effectWith = (effect) => (element, fn, ...rest) => effect(element, (...values) => plain(fn(...values)), ...rest);
168
+ //#endregion
116
169
  //#region src/runtime/recipe.ts
117
170
  /**
118
171
  * The recipe resolver (PLAN §4.4, §5.7): what a compiled `recipe()` becomes,
@@ -175,7 +228,7 @@ const __vxRecipe = (t, x) => {
175
228
  const add = x?.(t, props, values, cells);
176
229
  for (const compound of t.c) if (add) add(compound);
177
230
  else if (Object.keys(compound[0]).every((key) => compound[0][key].includes(values[key]))) cells.push(compound[1]);
178
- const slot = (name) => Object.fromEntries(merge(cells.map((cell) => typeof cell == "object" && (name ? cell[name] : cell)))[1]);
231
+ const slot = (name) => styled(Object.fromEntries(merge(cells.map((cell) => typeof cell == "object" && (name ? cell[name] : cell)))[1]));
179
232
  result = t.s ? Object.fromEntries(t.s.map((name) => [name, slot(name)])) : slot();
180
233
  if (process.env.NODE_ENV != "production") Object.freeze(result);
181
234
  if (memo.size >> 10) memo.clear();
@@ -351,6 +404,26 @@ const sxDev = /*#__PURE__*/ withSource(sx$1);
351
404
  const sxDevClass = /*#__PURE__*/ withSource(sxClass);
352
405
  /** Svelte, Astro and vanilla JS: `{ class, style, "data-vx-src" }`, `style` a string. */
353
406
  const sxDevString = /*#__PURE__*/ withSource(sxString);
407
+ /**
408
+ * Dev only (PLAN §5.16): a `style=` value the compiler could not write as
409
+ * an inline style. A compiled style there would set nothing a browser
410
+ * reads (its keys are merge ids), so it fails where the element is
411
+ * rendered, naming the fix; any other value passes through.
412
+ */
413
+ const styleCheck = (value, src) => {
414
+ if (isStyled(value) || isStyled(value?.$c)) throw new Error(`vicss: ${src}: style= got a css() style the plugin cannot see, so it is not an inline style. Write it in this module (style={css({ … })}, or style={card} with const card = css({ … }) in the same file), or apply it as a class: class={card} (className= in React and Preact), or {...sx(card)} for several styles or a function style.`);
415
+ return value;
416
+ };
417
+ /**
418
+ * A framework's style setter that checks its value at `at` first: what the
419
+ * plugin points a compiled component's calls at in dev
420
+ * (vite/attributes.ts). With `key`, only when the call sets that attribute
421
+ * (Astro's `addAttribute(value, "style")`).
422
+ */
423
+ const styleWith = (set, at, src, key) => (...args) => {
424
+ if (key === void 0 || args[1] === key) styleCheck(args[at], src);
425
+ return set(...args);
426
+ };
354
427
  //#endregion
355
428
  //#region src/index.ts
356
429
  /**
@@ -366,4 +439,4 @@ const sxDevString = /*#__PURE__*/ withSource(sxString);
366
439
  */
367
440
  const sx = sx$1;
368
441
  //#endregion
369
- export { devToken as __vxDevToken, devValue as __vxDevValue, flatBases as __vxFlatBases, __vxRecipe, __vxResponsive, sxClass as __vxSxClass, sxDev as __vxSxDev, sxDevClass as __vxSxDevClass, sxDevString as __vxSxDevString, sxString as __vxSxString, css, group, keyframes, peer, recipe, sx, theme, token };
442
+ export { classList as __vxClassList, classWith as __vxClassWith, devToken as __vxDevToken, devValue as __vxDevValue, effectWith as __vxEffectWith, flatBases as __vxFlatBases, propsWith as __vxPropsWith, __vxRecipe, __vxResponsive, styleCheck as __vxStyleCheck, styleWith as __vxStyleWith, styled as __vxStyled, sxClass as __vxSxClass, sxDev as __vxSxDev, sxDevClass as __vxSxDevClass, sxDevString as __vxSxDevString, sxString as __vxSxString, css, group, keyframes, peer, recipe, sx, theme, token };
package/dist/lint.js CHANGED
Binary file