cssints 0.3.1 → 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.
Files changed (152) hide show
  1. package/README.md +3 -2
  2. package/dist/check.mjs +2 -2
  3. package/dist/clay-types.d.mts +232 -0
  4. package/dist/clay.d.mts +40 -0
  5. package/dist/clay.mjs +577 -0
  6. package/dist/{contrast-B49XCnVX.mjs → contrast-DLi4pp25.mjs} +2 -2
  7. package/dist/group-common-3PAVTxpd.d.mts +1 -0
  8. package/dist/{group-types-9ttphdku.d.mts → group-types-Dn9CIR8J.d.mts} +1 -1
  9. package/dist/groups/baseline-2015/with-downstream.d.mts +3 -3
  10. package/dist/groups/baseline-2015.d.mts +3 -3
  11. package/dist/groups/baseline-2016/with-downstream.d.mts +3 -3
  12. package/dist/groups/baseline-2016.d.mts +3 -3
  13. package/dist/groups/baseline-2017/with-downstream.d.mts +3 -3
  14. package/dist/groups/baseline-2017.d.mts +3 -3
  15. package/dist/groups/baseline-2018/with-downstream.d.mts +3 -3
  16. package/dist/groups/baseline-2018.d.mts +3 -3
  17. package/dist/groups/baseline-2019/with-downstream.d.mts +3 -3
  18. package/dist/groups/baseline-2019.d.mts +3 -3
  19. package/dist/groups/baseline-2020/with-downstream.d.mts +3 -3
  20. package/dist/groups/baseline-2020.d.mts +3 -3
  21. package/dist/groups/baseline-2021/with-downstream.d.mts +3 -3
  22. package/dist/groups/baseline-2021.d.mts +3 -3
  23. package/dist/groups/baseline-2022/with-downstream.d.mts +3 -3
  24. package/dist/groups/baseline-2022.d.mts +3 -3
  25. package/dist/groups/baseline-2023/with-downstream.d.mts +3 -3
  26. package/dist/groups/baseline-2023.d.mts +3 -3
  27. package/dist/groups/baseline-2024/with-downstream.d.mts +3 -3
  28. package/dist/groups/baseline-2024.d.mts +3 -3
  29. package/dist/groups/baseline-2025/with-downstream.d.mts +3 -3
  30. package/dist/groups/baseline-2025.d.mts +3 -3
  31. package/dist/groups/baseline-2026/with-downstream.d.mts +1 -1
  32. package/dist/groups/baseline-2026.d.mts +1 -1
  33. package/dist/groups/compositing-2.d.mts +3 -3
  34. package/dist/groups/css-align-3.d.mts +3 -3
  35. package/dist/groups/css-anchor-position-1.d.mts +3 -3
  36. package/dist/groups/css-anchor-position-2.d.mts +3 -3
  37. package/dist/groups/css-animations-1.d.mts +3 -3
  38. package/dist/groups/css-animations-2.d.mts +3 -3
  39. package/dist/groups/css-backgrounds-3.d.mts +3 -3
  40. package/dist/groups/css-backgrounds-4.d.mts +3 -3
  41. package/dist/groups/css-borders-4.d.mts +3 -3
  42. package/dist/groups/css-box-4.d.mts +3 -3
  43. package/dist/groups/css-break-4.d.mts +3 -3
  44. package/dist/groups/css-cascade-5.d.mts +3 -3
  45. package/dist/groups/css-cascade-6.d.mts +3 -3
  46. package/dist/groups/css-color-4.d.mts +3 -3
  47. package/dist/groups/css-color-5.d.mts +3 -3
  48. package/dist/groups/css-color-6.d.mts +3 -3
  49. package/dist/groups/css-color-adjust-1.d.mts +3 -3
  50. package/dist/groups/css-color-hdr-1.d.mts +3 -3
  51. package/dist/groups/css-conditional-3.d.mts +3 -3
  52. package/dist/groups/css-conditional-4.d.mts +3 -3
  53. package/dist/groups/css-conditional-5.d.mts +3 -3
  54. package/dist/groups/css-contain-2.d.mts +3 -3
  55. package/dist/groups/css-content-3.d.mts +3 -3
  56. package/dist/groups/css-counter-styles-3.d.mts +3 -3
  57. package/dist/groups/css-display-4.d.mts +3 -3
  58. package/dist/groups/css-easing-2.d.mts +3 -3
  59. package/dist/groups/css-env-1.d.mts +3 -3
  60. package/dist/groups/css-flexbox-1.d.mts +3 -3
  61. package/dist/groups/css-fonts-4.d.mts +3 -3
  62. package/dist/groups/css-fonts-5.d.mts +3 -3
  63. package/dist/groups/css-forms-1.d.mts +3 -3
  64. package/dist/groups/css-gaps-1.d.mts +3 -3
  65. package/dist/groups/css-gcpm-3.d.mts +3 -3
  66. package/dist/groups/css-gcpm-4.d.mts +3 -3
  67. package/dist/groups/css-grid-2.d.mts +3 -3
  68. package/dist/groups/css-grid-3.d.mts +3 -3
  69. package/dist/groups/css-images-3.d.mts +3 -3
  70. package/dist/groups/css-images-4.d.mts +3 -3
  71. package/dist/groups/css-images-5.d.mts +3 -3
  72. package/dist/groups/css-inline-3.d.mts +3 -3
  73. package/dist/groups/css-lists-3.d.mts +3 -3
  74. package/dist/groups/css-logical-1.d.mts +3 -3
  75. package/dist/groups/css-masking-1.d.mts +3 -3
  76. package/dist/groups/css-multicol-2.d.mts +3 -3
  77. package/dist/groups/css-overflow-3.d.mts +3 -3
  78. package/dist/groups/css-overflow-4.d.mts +3 -3
  79. package/dist/groups/css-overflow-5.d.mts +3 -3
  80. package/dist/groups/css-overscroll-1.d.mts +3 -3
  81. package/dist/groups/css-page-3.d.mts +3 -3
  82. package/dist/groups/css-page-floats-3.d.mts +3 -3
  83. package/dist/groups/css-position-3.d.mts +3 -3
  84. package/dist/groups/css-position-4.d.mts +3 -3
  85. package/dist/groups/css-rhythm-1.d.mts +3 -3
  86. package/dist/groups/css-round-display-1.d.mts +3 -3
  87. package/dist/groups/css-ruby-1.d.mts +3 -3
  88. package/dist/groups/css-scroll-anchoring-1.d.mts +3 -3
  89. package/dist/groups/css-scroll-snap-1.d.mts +3 -3
  90. package/dist/groups/css-scroll-snap-2.d.mts +3 -3
  91. package/dist/groups/css-scrollbars-1.d.mts +3 -3
  92. package/dist/groups/css-shapes-1.d.mts +3 -3
  93. package/dist/groups/css-shapes-2.d.mts +3 -3
  94. package/dist/groups/css-size-adjust-1.d.mts +3 -3
  95. package/dist/groups/css-sizing-3.d.mts +3 -3
  96. package/dist/groups/css-sizing-4.d.mts +3 -3
  97. package/dist/groups/css-speech-1.d.mts +3 -3
  98. package/dist/groups/css-tables-3.d.mts +3 -3
  99. package/dist/groups/css-text-3.d.mts +3 -3
  100. package/dist/groups/css-text-4.d.mts +3 -3
  101. package/dist/groups/css-text-decor-3.d.mts +3 -3
  102. package/dist/groups/css-text-decor-4.d.mts +3 -3
  103. package/dist/groups/css-transforms-1.d.mts +3 -3
  104. package/dist/groups/css-transforms-2.d.mts +3 -3
  105. package/dist/groups/css-transitions-1.d.mts +3 -3
  106. package/dist/groups/css-transitions-2.d.mts +3 -3
  107. package/dist/groups/css-ui-4.d.mts +3 -3
  108. package/dist/groups/css-values-4.d.mts +3 -3
  109. package/dist/groups/css-values-5.d.mts +3 -3
  110. package/dist/groups/css-variables-2.d.mts +2 -2
  111. package/dist/groups/css-view-transitions-2.d.mts +3 -3
  112. package/dist/groups/css-viewport.d.mts +3 -3
  113. package/dist/groups/css-will-change-1.d.mts +3 -3
  114. package/dist/groups/css-writing-modes-4.d.mts +3 -3
  115. package/dist/groups/css2.d.mts +3 -3
  116. package/dist/groups/fill-stroke-3.d.mts +3 -3
  117. package/dist/groups/filter-effects-1.d.mts +3 -3
  118. package/dist/groups/filter-effects-2.d.mts +3 -3
  119. package/dist/groups/limited-availability.d.mts +1 -1
  120. package/dist/groups/mediaqueries-4.d.mts +3 -3
  121. package/dist/groups/mediaqueries-5.d.mts +3 -3
  122. package/dist/groups/motion-1.d.mts +3 -3
  123. package/dist/groups/newly-available/with-downstream.d.mts +3 -3
  124. package/dist/groups/newly-available.d.mts +3 -3
  125. package/dist/groups/not-discouraged.d.mts +3 -3
  126. package/dist/groups/scroll-animations-1.d.mts +3 -3
  127. package/dist/groups/selectors-4.d.mts +2 -2
  128. package/dist/groups/selectors-5.d.mts +2 -2
  129. package/dist/groups/widely-available/with-downstream.d.mts +3 -3
  130. package/dist/groups/widely-available.d.mts +3 -3
  131. package/dist/index.d.mts +1 -1
  132. package/dist/index.mjs +12 -5
  133. package/dist/{instrument-BqIZpbIU.mjs → instrument-C7Am_dJ3.mjs} +1 -1
  134. package/dist/{lowered-aJVapQT6.mjs → lowered-CKSYUc0i.mjs} +1 -1
  135. package/dist/{node-OJ_w_Usj.d.mts → node-BABEZ2oF.d.mts} +6 -0
  136. package/dist/plugin-types.d.mts +1 -1
  137. package/dist/plugin.d.mts +1 -1
  138. package/dist/{registry-BUSEO32-.mjs → registry-ByrYF2v5.mjs} +5 -1
  139. package/dist/{scope-INuJD4RT.d.mts → scope-1R_Np2Mj.d.mts} +5 -6
  140. package/dist/{sheet-BeHdq4xt.mjs → sheet-BT2R1wk_.mjs} +2 -2
  141. package/dist/surface.d.mts +1 -1
  142. package/dist/{theme-SlQ73rFL.mjs → theme-WGcgMD4V.mjs} +7 -5
  143. package/dist/{theme-types-Bqv0Ggz_.d.mts → theme-types-BY0mRf3K.d.mts} +5 -6
  144. package/dist/tui-types.d.mts +1 -1
  145. package/dist/tui.mjs +4 -4
  146. package/dist/types.d.mts +1 -1
  147. package/dist/vite.d.mts +1 -1
  148. package/dist/vite.mjs +4 -4
  149. package/docs/API.md +134 -2
  150. package/package.json +6 -2
  151. package/skills/cssints/references/plugins.md +12 -0
  152. package/dist/group-common-9zpBxfUF.d.mts +0 -1
package/docs/API.md CHANGED
@@ -1084,8 +1084,10 @@ More plugins on `scope()`, each a package of its own with its rules and limits i
1084
1084
  | [`@cssints/layout`](../packages/layout/README.md) | `stack()`, `cluster()`, `sidebar()`, `switcher()`, `cover()`, `autoGrid()` | Every Layout primitives as chain members |
1085
1085
  | [`@cssints/fonts`](../packages/fonts/README.md) | `font(family, { provider, ... })` → `.family()`, `.trim()` | `@font-face` and a metric-matched fallback face; cap-height trimming by `text-box`, else capsize's margins |
1086
1086
  | [`@cssints/reset`](../packages/reset/README.md) | `reset({ media?, prose? })` | A modern reset in a top-level `@layer reset` declared before the engine's, below every style; `@cssints/reset/<baseline group>` keeps only the declarations of that group |
1087
- | [`@cssints/tokens`](../packages/tokens/README.md) | `/open-props`, `/radix`, `/flexoki`, `/reasonable-colors` | DTCG data for `createTokens` |
1087
+ | [`@cssints/tokens`](../packages/tokens/README.md) | `/open-props`, `/radix`, `/flexoki`, `/reasonable-colors`, `/madoromi` | DTCG data for `createTokens` |
1088
1088
  | [`@cssints/gradient`](../packages/gradient/README.md) | `gradient(kind, direction, from, to, easing?, stops?)` | Eased colour stops mixed in oklab at build time |
1089
+ | [`@cssints/gradient-2d`](../packages/gradient-2d/README.md) | `meshGradient(mesh)`, `freeformGradient(points)` | `mesh-gradient()` / `freeform-gradient()` where the browser has them, else a runtime's canvas (this package's algorithms; lines and shapes not yet) |
1090
+ | [`@cssints/color-names`](../packages/color-names/README.md) | `named(name)`, `colorNames.<sector>(name)`, `/nearest` | The Color Name List (31,939 names) as hex colours; the ten sector members keep an editor's completion short, `nearest()` is the build-time reverse lookup |
1089
1091
  | [`@cssints/motion`](../packages/motion/README.md) | `motion().preset().duration().delay().easing()` | Animation presets over `keyframes()`, with reduced motion |
1090
1092
  | [`@cssints/squircle`](../packages/squircle/README.md) | `squircle().radius()` | `corner-shape: squircle`, else a CSS `mask` of the same shape |
1091
1093
  | [`@cssints/reveal`](../packages/reveal/README.md) | `reveal().preset().range().duration().easing()` | motion's entrance presets on `animation-timeline: view()`; for other targets a script plays it once on entry (delivered by the runtime) |
@@ -1267,7 +1269,15 @@ const { ratio, text } = await Effect.runPromise(contrast("#777", "white"));
1267
1269
  - **`contrast(foreground, background)`** is the WCAG 2.x ratio of `css.check.contrast` (see Themes): culori reads the colour, one outside sRGB is clipped to it. A colour it cannot read (`currentColor`, a `var()`) or a translucent one fails with a `ContrastError` (`colour`, `message`).
1268
1270
  - **`loweredOf(property, value, warning)`** answers what a build asks before it says a support warning ("No warning for what LightningCSS lowers", under Errors): the value of the first declaration LightningCSS writes for the targets of the warning, or `undefined` when it leaves the feature in place (`text-wrap`, a `var()` inside the value). `warning` is one of `support()` of `@cssints/css-grammar/support`; a custom property is `--name`. For `color: oklch(70% 0.2 330)` and chrome 100 it is a hex colour, so a build does not warn.
1269
1271
 
1270
- ### TUI: `cssints/tui` (decided 2026-10-10, cssints-3ff7)
1272
+ ### TUI: `cssints/tui` (decided 2026-10-10, cssints-3ff7; deprecated 2026-10-21)
1273
+
1274
+ > **Deprecated.** The members below still compile, and `@cssints/tui` still applies them, but importing it now prints
1275
+ > one warning, and the package will not get new members. A terminal app builds on
1276
+ > [cssints-native](https://github.com/alexeiaccio/cssints-native) instead: `terminal/` takes `cssints/clay` styles
1277
+ > (`generator/clay-header.ts` → a C header), lays them out with [Clay](https://github.com/nicbarker/clay) and draws them
1278
+ > into character cells on [termbox2](https://github.com/termbox/termbox2), with the keyboard focus layer in C
1279
+ > (`terminal/focus.c`). What carries over and what does not, and the commands that run it, are in
1280
+ > [packages/tui/README.md](../packages/tui/README.md); `docs/map.md` step 6 of that project is the full account.
1271
1281
 
1272
1282
  A terminal app writes its styles with `cssints/tui`, and the same Vite plugin compiles them to **prop objects** for terminal renderables, not to classes: there is no sheet and no cascade. `@cssints/tui` applies them (states, the mouse pointer, the caret, themes); its adapters are [OpenTUI](https://opentui.com) and [@bomb.sh/tty](https://bomb.sh/docs/tty/api/). The design, the full property table and what is out of scope are in [research/tui.md](research/tui.md).
1273
1283
 
@@ -1374,6 +1384,128 @@ process.stdin.on("data", (bytes) => dispatch(input.scan(follow.feed(bytes)).even
1374
1384
  - **Live in the browser** (cssints-ey13, 2026-10-11). [The TUI page](https://cssints.dev/tui) of this site runs apps/csssky-tui's styles on `@cssints/tui/bomb` in a real terminal: Ghostty's emulator compiled to WebAssembly (ghostty-web) draws csssky's feed and thread from its recorded fixtures, with keys and the mouse. The page is the terminal: it answers the system theme's queries (OSC 10, 11, 4, `CSI ? 996 n`) from the site's display settings and reports a change of them (mode 2031), so the theme is solved again as the palette or the mode changes. OpenTUI is not shown: its renderer is native Zig over `bun:ffi`. On a change notification, `followSystemTheme` of `@cssints/tui/bomb` asks the colours again (`COLOURS` of `@cssints/tui/system`), not the scheme report: its reply is the same sequence as a notification, and asking it looped with a terminal that answers it.
1375
1385
  - **Cost.** A colour member costs what the same web member costs in the types (the `color` grammar); `bench/tui.ts` is 184k instantiations (tsc) for 16 calls and a token set. No terminal HMR in this version. The system theme brings culori's rgb, oklab, oklch and okhsl modes to the app (45 kB unminified in csssky-tui's bundle).
1376
1386
 
1387
+ ### Clay: `cssints/clay` (2026-10-19, cssints-iwey)
1388
+
1389
+ A native UI laid out by [Clay](https://github.com/nicbarker/clay), with no browser and no JS where it runs (an ESP32's firmware), writes its styles with `cssints/clay`. The same Vite plugin compiles each site to an object literal shaped as Clay's own structs, and `header()` prints a module of them as C initializers:
1390
+
1391
+ ```ts
1392
+ import * as css from "cssints/clay" with { type: "cssints" };
1393
+
1394
+ export const pill = css.cn(
1395
+ css.width(200),
1396
+ css.height(38),
1397
+ css.justifyContent("center"),
1398
+ css.alignItems("center"),
1399
+ css.bg("rgb(36 40 52)"),
1400
+ css.borderRadius(19),
1401
+ css.border(1, "#969baa"),
1402
+ );
1403
+ export const label = css.cn(css.color("#ebebf0"), css.fontSize(14));
1404
+ css.width("10rem"); // error: Clay counts pixels
1405
+ css.boxShadow("0 0 1px red"); // error: no such member in Clay
1406
+ ```
1407
+
1408
+ ```c
1409
+ static const Clay_ElementDeclaration pill = { .layout = { .sizing = { .width = { .size = { .minMax = { 200, 200 } }, .type = CLAY__SIZING_TYPE_FIXED }, ... }, .backgroundColor = { .r = 36, .g = 40, .b = 52, .a = 255 }, ... };
1410
+ static const Clay_TextElementConfig label_text = { .textColor = { .r = 235, .g = 235, .b = 240, .a = 255 }, .fontSize = 14 };
1411
+ ```
1412
+
1413
+ ```c
1414
+ CLAY(CLAY_ID("Units"), pill) { CLAY_TEXT(CLAY_STRING("switch to g"), label_text); }
1415
+ ```
1416
+
1417
+ - **Members**: `width`, `height` (pixels, `N%`, `auto` for Clay's FIT, `stretch` for GROW), `padding`/`p` and the sides, `gap` (`childGap`), `flexDirection` (`row`, `column`), `justifyContent` and `alignItems` (start, center, end: Clay's `childAlignment`, the axis from `flexDirection`), `backgroundColor`/`bg`, `borderRadius`, `border(width, colour)`, `borderWidth`, `borderColor`, `overflow("hidden")` (`clip`), `float()` (`floating`, below), `image(src, { width, height, fit })` (`image`, below), and for text `color`, `fontSize`, `letterSpacing`, `lineHeight`, `textAlign`, `whiteSpace` (`wrapMode`); `cn` merges, a later prop wins. Any other property is not a member.
1418
+ - **Pixels.** A number or `"Npx"`; padding, gap and border widths are whole numbers (`uint16_t` in Clay). Another unit is a type error and a build error.
1419
+ - **Colours** are checked by the web's `color` grammar, or are colour tokens of cssints/clay's `createTokens`, and are written as 0-255 `r g b a`.
1420
+ - **`header(exports)`** prints a `Clay_ElementDeclaration` per export and, for one with text props, a `Clay_TextElementConfig` named `<name>_text`. A field it does not set is zero, which is Clay's default.
1421
+ - **States**: `hover`, `focus`, `active` wrap styles. Clay has no states, so the literal has an overlay per state (`":active": { backgroundColor }`, the fields the state sets, at their values over the base), and `header()` prints the whole declaration again, `<name>_active` (`<name>_hover_text` for text): the page asks Clay and picks one, nothing is merged in C. States do not nest.
1422
+ - **Tokens and themes**: `createTokens` and `createGlobalTheme(tokens, name, overrides)` of cssints/clay, on tables of their own (a web or terminal token is not a Clay one, and a theme of one is a build error). Resolved at build time: a site has its colours at the tokens' values and, under `themes`, what each theme changes. `header()` prints every style of a module with a theme again, the theme's name last (`units_light`, `units_active_light`, `title_text_light`), so a page switches at run time by a suffix. A style sees the themes evaluated before it: declare them first, or in a module it imports.
1423
+ - **Fonts**: `fonts("Roboto", "Mono")` is the order of the app's font array; `.Mono` is a style that sets Clay's `fontId` to 1, and `header()` prints the order as an enum, `font_Roboto = 0, font_Mono = 1` (the export's name, then the font's).
1424
+
1425
+ ```ts
1426
+ import * as css from "cssints/clay" with { type: "cssints" };
1427
+
1428
+ export const t = css.createTokens({
1429
+ color: { $type: "color", surface: { $value: "rgb(36 40 52)" }, pressed: { $value: "#ffc878" } },
1430
+ });
1431
+ css.createGlobalTheme(t, "light", { color: { surface: "white" } });
1432
+ export const font = css.fonts("Roboto", "Mono");
1433
+
1434
+ export const units = css.cn(
1435
+ css.width(200),
1436
+ css.bg(t.color.surface),
1437
+ css.active(css.bg(t.color.pressed)),
1438
+ );
1439
+ export const label = css.cn(font.Mono, css.fontSize(14));
1440
+ css.cn(font.Serif); // error: not one of the fonts
1441
+ ```
1442
+
1443
+ ```c
1444
+ #define T(name) (light ? name##_light : name)
1445
+ CLAY(CLAY_ID("Units"), pressed ? T(units_active) : T(units)) { CLAY_TEXT(CLAY_STRING("switch to g"), label_text); }
1446
+ ```
1447
+
1448
+ - **Floating elements**: `float({ ... })` fills Clay's `floating`, the foundation of menus, popovers and dialogs. Every option is optional, and what it leaves out is Clay's own default, so `header()` prints only what is set (`float()` alone is `{ .attachTo = CLAY_ATTACH_TO_PARENT }`). The name is `float` as in CSS: it is a valid binding, and no other Clay member shares it.
1449
+
1450
+ | Option | Clay field | Default |
1451
+ | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |
1452
+ | `attach: { element, parent }` | `attachPoints`: which point of the float sits on which point of its parent, each `"left top"`, `"center center"`, `"right bottom"`, ... (x, then y) | `left top` on `left top` |
1453
+ | `parent: "Units"` | `parentId` and `attachTo = ELEMENT_WITH_ID`: the string of the parent's `CLAY_ID("Units")` (ASCII; a `CLAY_IDI` or local id is not one) | the element the float is a child of (`attachTo = PARENT`) |
1454
+ | `root: true` | `attachTo = ROOT`: absolute positioning, a dialog on the screen (not with `parent`) | off |
1455
+ | `offset: { x, y }` | `offset`: pixels from where `attach` puts it; a negative number goes left or up | 0, 0 |
1456
+ | `expand: { width, height }` | `expand`: pixels the float's own box grows by, its children's not; not negative | 0, 0 |
1457
+ | `zIndex` | `zIndex`: a whole number, `int16_t`; floats draw in ascending order, with their children | 0 |
1458
+ | `capture: false` | `pointerCaptureMode = PASSTHROUGH`: the pointer reaches what lies under the float (`true` is `CAPTURE`) | `true` |
1459
+ | `clip: "parent"` | `clipTo = ATTACHED_PARENT`: clipped to the box its parent is clipped to (`"none"` is `CLIP_TO_NONE`; with `root` it has nothing to clip to, a build error) | `"none"` |
1460
+
1461
+ `parentId` is printed as a number, the hash of the id (`Clay__HashString(id, 0)` computed at build time), because `CLAY_ID()` is a call and a `static const` initializer takes constants. A misspelt id is Clay's own `FLOATING_CONTAINER_PARENT_NOT_FOUND` error at run time. A bad value, an option that does not exist, a negative `expand`, a non-ASCII id are build errors (the types catch what a type can: a point such as `"top left"`, an unknown option). Two floats merge in a `cn()` as the other members do: the later one's options win and the rest stay; a state (`hover(float(...))`) is a declaration of its own.
1462
+
1463
+ ```ts
1464
+ import * as css from "cssints/clay" with { type: "cssints" };
1465
+
1466
+ export const menu = css.cn(
1467
+ css.width(160),
1468
+ css.bg("#242834"),
1469
+ css.float({ parent: "Units", attach: { parent: "left bottom" }, offset: { y: 4 }, zIndex: 10 }),
1470
+ );
1471
+ export const dialog = css.float({
1472
+ root: true,
1473
+ attach: { element: "center center", parent: "center center" },
1474
+ });
1475
+ css.float({ attach: { element: "top left" } }); // error: a point is x, then y
1476
+ ```
1477
+
1478
+ ```c
1479
+ static const Clay_ElementDeclaration menu = { .layout = { ... }, .backgroundColor = { ... }, .floating = { .offset = { .x = 0, .y = 4 }, .parentId = 3516868942u, .zIndex = 10, .attachPoints = { .element = CLAY_ATTACH_POINT_LEFT_TOP, .parent = CLAY_ATTACH_POINT_LEFT_BOTTOM }, .attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID } };
1480
+ ```
1481
+
1482
+ - **Images**: `image(src, { width, height, fit })` fills Clay's `image`: the element that draws a picture, its children none. Clay 1.0's `Clay_ImageElementConfig` is one field, `void *imageData`, which Clay passes to the renderer untouched, so the path of the image travels in it (the desktop renderer loads it with ThorVG; the firmware's own renderer is still the app's to write). Older Clay also kept the image's `sourceDimensions` there; 1.0 has no such field, so the image's own size becomes the element's box, and `fit` is Clay's `aspectRatio`:
1483
+
1484
+ | Option | Clay field | Default |
1485
+ | -------------------------- | ---------------------------------------------------------------------------------------------------------------- | --------- |
1486
+ | `"glyph.png"` (argument 1) | `image.imageData`: the path, printed as a C string, `(void *)"glyph.png"` | — |
1487
+ | `width`, `height` | `layout.sizing`: the image's size, `CLAY_SIZING_FIXED`, whole pixels (a later `width` of a `cn()` wins) | — |
1488
+ | `fit: "contain"` | `aspectRatio`: width / height, which Clay uses where it has one side to derive the other's (`"fill"` stretches) | `"fill"` |
1489
+
1490
+ A path that is not a non-empty string, a size that is not a positive whole number, an option that does not exist are build errors. `fit` is how the picture meets a box another style gives it: `fill` (CSS's `object-fit` default) stretches it, `contain` keeps the image's proportions.
1491
+
1492
+ ```ts
1493
+ import * as css from "cssints/clay" with { type: "cssints" };
1494
+
1495
+ export const glyph = css.image("examples/sensors/glyph.png", { width: 32, height: 32 });
1496
+ export const logo = css.cn(
1497
+ css.image("assets/logo.png", { width: 64, height: 32, fit: "contain" }),
1498
+ css.width("stretch"),
1499
+ );
1500
+ css.image("logo.png", { width: 64, height: 32, fit: "cover" }); // error: fill or contain
1501
+ ```
1502
+
1503
+ ```c
1504
+ static const Clay_ElementDeclaration glyph = { .layout = { .sizing = { .width = { .size = { .minMax = { 32, 32 } }, .type = CLAY__SIZING_TYPE_FIXED }, .height = { .size = { .minMax = { 32, 32 } }, .type = CLAY__SIZING_TYPE_FIXED } } }, .image = { .imageData = (void *)"examples/sensors/glyph.png" } };
1505
+ ```
1506
+
1507
+ - **Not yet**: min and max sizes, derived colour tokens (`color-mix()` of tokens: a colour Clay is given must parse as one), a theme under a condition (a theme is a name; `media()` has no meaning on a board).
1508
+
1377
1509
  ### Export groups: `cssints/widely-available`, `cssints/css-grid-2`, … (decided 2026-10-11, cssints-n7z3)
1378
1510
 
1379
1511
  A group is a subpath of `cssints` that exports the members of `cssints` which a set of CSS features has, and refuses a value that holds a feature outside the set. It is the same import (`with { type: "cssints" }`), the same members, the same classes:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cssints",
3
- "version": "0.3.1",
3
+ "version": "0.5.0",
4
4
  "description": "CSS in TypeScript: typed css.* calls compiled to atomic CSS at build time (a Vite plugin)",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -54,6 +54,10 @@
54
54
  "types": "./dist/tui-types.d.mts",
55
55
  "default": "./dist/tui.mjs"
56
56
  },
57
+ "./clay": {
58
+ "types": "./dist/clay-types.d.mts",
59
+ "default": "./dist/clay.mjs"
60
+ },
57
61
  "./devtools-link": {
58
62
  "types": "./dist/devtools-link.d.mts",
59
63
  "default": "./dist/devtools-link.mjs"
@@ -64,7 +68,7 @@
64
68
  }
65
69
  },
66
70
  "dependencies": {
67
- "@cssints/css-grammar": "^0.3.1",
71
+ "@cssints/css-grammar": "^0.5.0",
68
72
  "culori": "^4.0.2",
69
73
  "effect": "4.0.0",
70
74
  "happy-dom": "^20.14.5",
@@ -62,6 +62,18 @@ a suggestion.
62
62
  **`@cssints/gradient`**: `backgroundImage(gradient("linear", "to bottom", "#000", "transparent", "ease-in-out", 5))`:
63
63
  eased stops mixed in oklab. Each argument its own string; the direction is one string (`"to bottom"`).
64
64
 
65
+ **`@cssints/gradient-2d`**: `meshGradient("red, blue; green, yellow")` (rows from the top of the box down, separated by
66
+ `;`, stops by `,`) and `freeformGradient("red top left, gold 4 60% 40%, yellow bottom right")` (points, an optional
67
+ weight before the position). One string per call. Where the browser has `mesh-gradient()`, the declaration is native;
68
+ elsewhere a runtime the build adds draws the same points on a canvas, so it needs a script.
69
+
70
+ **`@cssints/color-names`**: `color(named("tangerine"))` is `#ff9300`. `colorNames.<sector>(name)` is the same name
71
+ restricted to one sector (`red`, `orange`, `yellow`, `green`, `cyan`, `blue`, `purple`, `pink`, `brown`, `neutral`), so
72
+ completion stays short; the colour word in the name picks the sector. An unknown name is a build error with the nearest
73
+ one, and a name outside the sector is one that names the sectors it is in. The list's hex wins over the CSS keyword's
74
+ (`named("green")` is `#00ff00`; `color("green")` is the CSS one). `@cssints/color-names/nearest` maps a hex to the
75
+ closest name at build time.
76
+
65
77
  **`@cssints/placeholder`**: `backgroundImage(placeholder("src/hero.jpg"))` (a gradient of the image's colours) or
66
78
  `{ mode: "thumbhash" }` (a tiny PNG; pair it with `backgroundSize("cover")`). JPEG and PNG only; the path is relative
67
79
  to the working directory.
@@ -1 +0,0 @@
1
- import "./theme-types-Bqv0Ggz_.mjs";