cssints 0.4.0 → 0.5.1
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 +13 -10
- package/dist/clay-types.d.mts +25 -0
- package/dist/clay.d.mts +1 -1
- package/dist/clay.mjs +31 -4
- package/docs/API.md +35 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -148,7 +148,7 @@ export const Row = ({ wide, on }: { wide?: boolean; on?: boolean }) => (
|
|
|
148
148
|
|
|
149
149
|
### In a terminal
|
|
150
150
|
|
|
151
|
-
`import * as css from "cssints/tui" with { type: "cssints" }` gives the members a terminal has (cells, colours, borders, flex, states, `cursor`), and the plugin compiles them to prop objects for [OpenTUI](https://opentui.com) renderables instead of classes; `@cssints/tui` applies their states and themes: [packages/tui](packages/tui/README.md), docs/API.md "TUI".
|
|
151
|
+
`import * as css from "cssints/tui" with { type: "cssints" }` gives the members a terminal has (cells, colours, borders, flex, states, `cursor`), and the plugin compiles them to prop objects for [OpenTUI](https://opentui.com) renderables instead of classes; `@cssints/tui` applies their states and themes: [packages/tui](packages/tui/README.md), docs/API.md "TUI". The package to apply them, `@cssints/tui`, is **deprecated** in favour of the terminal target in [`native/terminal`](native/terminal) (Clay in cells, termbox2); its README says what carries over and how to run the replacement.
|
|
152
152
|
|
|
153
153
|
More in [docs/API.md](docs/API.md): property names and short names, `flex()` and `grid()` chains, conditions and layers, tokens and themes, `cn` and `cv`, and the errors you can get.
|
|
154
154
|
|
|
@@ -175,15 +175,18 @@ and Cursor. The skills use it when it is configured and work without it.
|
|
|
175
175
|
|
|
176
176
|
## Layout
|
|
177
177
|
|
|
178
|
-
| Path | What
|
|
179
|
-
| ------------------------------- |
|
|
180
|
-
| `packages/cssints` | Vite plugin (`cssints/vite`) on the Vite Module Runner, and the `cssints` library
|
|
181
|
-
| `packages/css-grammar` | Type-level recognizer and its runtime twin over mdn-data grammars, Typed OM values, browser support and prose rules
|
|
182
|
-
| `packages/icons` | `@cssints/icons`: icons from iconify JSON collections or SVG folders, a mask style with no runtime
|
|
183
|
-
| `packages/mise-en-mode` | `@cssints/mise-en-mode`: looks as themes in the layers app < author < viewer, on `data-mode` scopes, checked against the tokens and by `check.contrast`
|
|
184
|
-
| `
|
|
185
|
-
| `
|
|
186
|
-
| `
|
|
178
|
+
| Path | What |
|
|
179
|
+
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
180
|
+
| `packages/cssints` | Vite plugin (`cssints/vite`) on the Vite Module Runner, and the `cssints` library |
|
|
181
|
+
| `packages/css-grammar` | Type-level recognizer and its runtime twin over mdn-data grammars, Typed OM values, browser support and prose rules |
|
|
182
|
+
| `packages/icons` | `@cssints/icons`: icons from iconify JSON collections or SVG folders, a mask style with no runtime |
|
|
183
|
+
| `packages/mise-en-mode` | `@cssints/mise-en-mode`: looks as themes in the layers app < author < viewer, on `data-mode` scopes, checked against the tokens and by `check.contrast` |
|
|
184
|
+
| `packages/headless` | `@cssints/headless`: headless APG widgets on Solid 2 (state and DOM attribute objects, no styling), conformance-checked against Quint specs; not published yet |
|
|
185
|
+
| `packages/apg-specs` | `@cssints/apg-specs` (private): the APG and canvas Quint specs, generated models, scenario bridges and ported scenarios the widgets are tested against |
|
|
186
|
+
| `packages/tui` | `@cssints/tui` (**deprecated**): the runtime of `cssints/tui` styles on OpenTUI and @bomb.sh/tty; `native/terminal` replaces it |
|
|
187
|
+
| `apps/demo` | React demo of every 0.1 feature |
|
|
188
|
+
| `apps/docs` | The documentation site: this file and `docs/API.md` as pages, with search and type hovers (`cd apps/docs && bun run dev`) |
|
|
189
|
+
| `docs/research`, `docs/archive` | Comparisons with other libraries; outdated plans |
|
|
187
190
|
|
|
188
191
|
## Development
|
|
189
192
|
|
package/dist/clay-types.d.mts
CHANGED
|
@@ -40,6 +40,18 @@ export type FloatOptions = {
|
|
|
40
40
|
root?: boolean;
|
|
41
41
|
parent?: never;
|
|
42
42
|
});
|
|
43
|
+
/** The options of `image()`: the image's size in pixels, and how it fills its box. */
|
|
44
|
+
export type ImageOptions = {
|
|
45
|
+
/** The image's width in whole pixels: the element's box starts at the image's own size. */
|
|
46
|
+
width: Px;
|
|
47
|
+
/** The image's height in whole pixels. */
|
|
48
|
+
height: Px;
|
|
49
|
+
/**
|
|
50
|
+
* `"fill"` stretches the image into a box another style gives it; `"contain"` sets Clay's `aspectRatio` to the
|
|
51
|
+
* image's, so a box that grows keeps its proportions. Default: `"fill"`, as CSS's `object-fit`.
|
|
52
|
+
*/
|
|
53
|
+
fit?: "fill" | "contain";
|
|
54
|
+
};
|
|
43
55
|
interface Rgba {
|
|
44
56
|
r: number;
|
|
45
57
|
g: number;
|
|
@@ -88,6 +100,13 @@ export interface ClayFields {
|
|
|
88
100
|
horizontal: number;
|
|
89
101
|
vertical: number;
|
|
90
102
|
};
|
|
103
|
+
/** `Clay_ImageElementConfig`: `imageData` is the path of the image, which the renderer loads. */
|
|
104
|
+
image?: {
|
|
105
|
+
imageData: string;
|
|
106
|
+
};
|
|
107
|
+
aspectRatio?: {
|
|
108
|
+
aspectRatio: number;
|
|
109
|
+
};
|
|
91
110
|
floating?: {
|
|
92
111
|
offset?: {
|
|
93
112
|
x: number;
|
|
@@ -181,6 +200,12 @@ export declare const whiteSpace: One<"normal" | "pre-line" | "nowrap">;
|
|
|
181
200
|
* the one `parent` names, or to the root. A float is out of the flow of its siblings and of its parent's size.
|
|
182
201
|
*/
|
|
183
202
|
export declare const float: (options?: FloatOptions) => ClayStyle;
|
|
203
|
+
/**
|
|
204
|
+
* An image element: the path it is loaded from, and its size in whole pixels, which is the element's box. `fit`
|
|
205
|
+
* `"contain"` keeps its proportions in a box another style grows (Clay's `aspectRatio`); the path is passed to the
|
|
206
|
+
* renderer as `imageData`.
|
|
207
|
+
*/
|
|
208
|
+
export declare const image: (src: string, options: ImageOptions) => ClayStyle;
|
|
184
209
|
/** Merges styles in argument order: a later prop wins. */
|
|
185
210
|
export declare const cn: (...styles: ClayStyle[]) => ClayStyle;
|
|
186
211
|
/** Under the pointer: header() prints the style with it as `<name>_hover`. States do not nest. */
|
package/dist/clay.d.mts
CHANGED
|
@@ -25,7 +25,7 @@ export declare const active: (...styles: unknown[]) => ClayNode;
|
|
|
25
25
|
* @returns {Record<string, unknown>} a style per font, by name, and the list
|
|
26
26
|
*/
|
|
27
27
|
export declare const fonts: (...names: unknown[]) => Record<string, unknown>;
|
|
28
|
-
export declare const backgroundColor: ((...parts: unknown[]) => ClayNode) | undefined, bg: ((...parts: unknown[]) => ClayNode) | undefined, color: ((...parts: unknown[]) => ClayNode) | undefined, borderColor: ((...parts: unknown[]) => ClayNode) | undefined, width: ((...parts: unknown[]) => ClayNode) | undefined, height: ((...parts: unknown[]) => ClayNode) | undefined, padding: ((...parts: unknown[]) => ClayNode) | undefined, p: ((...parts: unknown[]) => ClayNode) | undefined, paddingInline: ((...parts: unknown[]) => ClayNode) | undefined, paddingBlock: ((...parts: unknown[]) => ClayNode) | undefined, paddingTop: ((...parts: unknown[]) => ClayNode) | undefined, paddingRight: ((...parts: unknown[]) => ClayNode) | undefined, paddingBottom: ((...parts: unknown[]) => ClayNode) | undefined, paddingLeft: ((...parts: unknown[]) => ClayNode) | undefined, pt: ((...parts: unknown[]) => ClayNode) | undefined, pr: ((...parts: unknown[]) => ClayNode) | undefined, pb: ((...parts: unknown[]) => ClayNode) | undefined, pl: ((...parts: unknown[]) => ClayNode) | undefined, gap: ((...parts: unknown[]) => ClayNode) | undefined, flexDirection: ((...parts: unknown[]) => ClayNode) | undefined, justifyContent: ((...parts: unknown[]) => ClayNode) | undefined, alignItems: ((...parts: unknown[]) => ClayNode) | undefined, borderRadius: ((...parts: unknown[]) => ClayNode) | undefined, borderWidth: ((...parts: unknown[]) => ClayNode) | undefined, border: ((...parts: unknown[]) => ClayNode) | undefined, overflow: ((...parts: unknown[]) => ClayNode) | undefined, fontSize: ((...parts: unknown[]) => ClayNode) | undefined, letterSpacing: ((...parts: unknown[]) => ClayNode) | undefined, lineHeight: ((...parts: unknown[]) => ClayNode) | undefined, textAlign: ((...parts: unknown[]) => ClayNode) | undefined, whiteSpace: ((...parts: unknown[]) => ClayNode) | undefined, float: ((...parts: unknown[]) => ClayNode) | undefined;
|
|
28
|
+
export declare const backgroundColor: ((...parts: unknown[]) => ClayNode) | undefined, bg: ((...parts: unknown[]) => ClayNode) | undefined, color: ((...parts: unknown[]) => ClayNode) | undefined, borderColor: ((...parts: unknown[]) => ClayNode) | undefined, width: ((...parts: unknown[]) => ClayNode) | undefined, height: ((...parts: unknown[]) => ClayNode) | undefined, padding: ((...parts: unknown[]) => ClayNode) | undefined, p: ((...parts: unknown[]) => ClayNode) | undefined, paddingInline: ((...parts: unknown[]) => ClayNode) | undefined, paddingBlock: ((...parts: unknown[]) => ClayNode) | undefined, paddingTop: ((...parts: unknown[]) => ClayNode) | undefined, paddingRight: ((...parts: unknown[]) => ClayNode) | undefined, paddingBottom: ((...parts: unknown[]) => ClayNode) | undefined, paddingLeft: ((...parts: unknown[]) => ClayNode) | undefined, pt: ((...parts: unknown[]) => ClayNode) | undefined, pr: ((...parts: unknown[]) => ClayNode) | undefined, pb: ((...parts: unknown[]) => ClayNode) | undefined, pl: ((...parts: unknown[]) => ClayNode) | undefined, gap: ((...parts: unknown[]) => ClayNode) | undefined, flexDirection: ((...parts: unknown[]) => ClayNode) | undefined, justifyContent: ((...parts: unknown[]) => ClayNode) | undefined, alignItems: ((...parts: unknown[]) => ClayNode) | undefined, borderRadius: ((...parts: unknown[]) => ClayNode) | undefined, borderWidth: ((...parts: unknown[]) => ClayNode) | undefined, border: ((...parts: unknown[]) => ClayNode) | undefined, overflow: ((...parts: unknown[]) => ClayNode) | undefined, fontSize: ((...parts: unknown[]) => ClayNode) | undefined, letterSpacing: ((...parts: unknown[]) => ClayNode) | undefined, lineHeight: ((...parts: unknown[]) => ClayNode) | undefined, textAlign: ((...parts: unknown[]) => ClayNode) | undefined, whiteSpace: ((...parts: unknown[]) => ClayNode) | undefined, float: ((...parts: unknown[]) => ClayNode) | undefined, image: ((...parts: unknown[]) => ClayNode) | undefined;
|
|
29
29
|
/**
|
|
30
30
|
* The styles of a module as C: a `Clay_ElementDeclaration` per export, and a `Clay_TextElementConfig` named
|
|
31
31
|
* `<name>_text` for one that sets text props, for `CLAY(id, name)` and `CLAY_TEXT(s, name_text)`. A state is a whole
|
package/dist/clay.mjs
CHANGED
|
@@ -176,6 +176,27 @@ const floating = (prop, parts) => {
|
|
|
176
176
|
if (o.clip !== void 0) set("floatClip", keyword("none", "parent")(`${prop}.clip`, o.clip) === "none" ? "NONE" : "ATTACHED_PARENT");
|
|
177
177
|
return out;
|
|
178
178
|
};
|
|
179
|
+
const IMAGE_OPTIONS = [
|
|
180
|
+
"width",
|
|
181
|
+
"height",
|
|
182
|
+
"fit"
|
|
183
|
+
];
|
|
184
|
+
const imageOf = (prop, parts) => {
|
|
185
|
+
if (parts.length < 1 || parts.length > 2) throw fail(`cssints: ${prop} takes the path of an image and its { width, height, fit }, got ${parts.length} values`);
|
|
186
|
+
const src = parts[0];
|
|
187
|
+
if (typeof src !== "string" || !src || /\p{Cc}/u.test(src)) throw fail(`cssints: ${prop}: unexpected ${say(src)}, expected the path of an image (a string, not empty, no control characters)`);
|
|
188
|
+
const o = options(prop, parts[1] ?? {}, IMAGE_OPTIONS);
|
|
189
|
+
if (o.width === void 0 || o.height === void 0) throw fail(`cssints: ${prop}: unexpected ${say(parts[1] ?? {})}, expected the image's width and height in pixels`);
|
|
190
|
+
const w = whole(`${prop}.width`, o.width);
|
|
191
|
+
const h = whole(`${prop}.height`, o.height);
|
|
192
|
+
if (!w || !h) throw fail(`cssints: ${prop}: unexpected ${say(w)}x${say(h)}, expected a positive whole number of pixels for each side`);
|
|
193
|
+
return [
|
|
194
|
+
["imageSrc", src],
|
|
195
|
+
["width", w],
|
|
196
|
+
["height", h],
|
|
197
|
+
["aspectRatio", keyword("fill", "contain")(`${prop}.fit`, o.fit ?? "fill") === "contain" ? w / h : 0]
|
|
198
|
+
];
|
|
199
|
+
};
|
|
179
200
|
const PADDING = [
|
|
180
201
|
"paddingTop",
|
|
181
202
|
"paddingRight",
|
|
@@ -235,7 +256,8 @@ const MEMBERS = {
|
|
|
235
256
|
lineHeight: one("lineHeight", whole),
|
|
236
257
|
textAlign: one("textAlign", keyword("left", "center", "right")),
|
|
237
258
|
whiteSpace: one("whiteSpace", keyword("normal", "pre-line", "nowrap")),
|
|
238
|
-
float: floating
|
|
259
|
+
float: floating,
|
|
260
|
+
image: imageOf
|
|
239
261
|
};
|
|
240
262
|
const member = (name, build) => (...parts) => {
|
|
241
263
|
if (parts.some(isClay)) throw fail(`cssints: ${name}: a style is not a value`);
|
|
@@ -288,7 +310,7 @@ const fonts = (...names) => {
|
|
|
288
310
|
]])]))
|
|
289
311
|
};
|
|
290
312
|
};
|
|
291
|
-
const { backgroundColor, bg, color, borderColor, width, height, padding, p, paddingInline, paddingBlock, paddingTop, paddingRight, paddingBottom, paddingLeft, pt, pr, pb, pl, gap, flexDirection, justifyContent, alignItems, borderRadius, borderWidth, border, overflow, fontSize, letterSpacing, lineHeight, textAlign, whiteSpace, float } = Object.fromEntries(Object.entries(MEMBERS).map(([k, b]) => [k, member(k, b)]));
|
|
313
|
+
const { backgroundColor, bg, color, borderColor, width, height, padding, p, paddingInline, paddingBlock, paddingTop, paddingRight, paddingBottom, paddingLeft, pt, pr, pb, pl, gap, flexDirection, justifyContent, alignItems, borderRadius, borderWidth, border, overflow, fontSize, letterSpacing, lineHeight, textAlign, whiteSpace, float, image } = Object.fromEntries(Object.entries(MEMBERS).map(([k, b]) => [k, member(k, b)]));
|
|
292
314
|
const rgba = (prop, text, values) => {
|
|
293
315
|
const flat = text.replace(VAR, (m, n) => valueOf(tables, n, values) ?? m);
|
|
294
316
|
const c = flat.includes("var(") ? void 0 : parseColor(flat);
|
|
@@ -404,6 +426,8 @@ const shapeOf = (entries, values) => {
|
|
|
404
426
|
};
|
|
405
427
|
if (p.has("overflow")) out.clip = get("overflow") === "hidden" ? HIDDEN : VISIBLE;
|
|
406
428
|
if (FLOATING.some((k) => p.has(k))) out.floating = floatingOf(p);
|
|
429
|
+
if (p.has("imageSrc")) out.image = { imageData: get("imageSrc") };
|
|
430
|
+
if (p.get("aspectRatio")) out.aspectRatio = { aspectRatio: get("aspectRatio") };
|
|
407
431
|
const text = {};
|
|
408
432
|
if (p.has("color")) text.textColor = colourOf("color");
|
|
409
433
|
for (const k of [
|
|
@@ -491,9 +515,10 @@ const ENUM = {
|
|
|
491
515
|
attachTo: (v) => `CLAY_ATTACH_TO_${v}`,
|
|
492
516
|
clipTo: (v) => `CLAY_CLIP_TO_${v}`
|
|
493
517
|
};
|
|
518
|
+
const cString = (s) => `"${s.replace(/[\\"]/g, "\\$&")}"`;
|
|
494
519
|
const c = (key, v) => {
|
|
495
520
|
if (typeof v === "number") return key === "parentId" ? `${v}u` : String(v);
|
|
496
|
-
if (typeof v === "string") return ENUM[key](v);
|
|
521
|
+
if (typeof v === "string") return key === "imageData" ? `(void *)${cString(v)}` : ENUM[key](v);
|
|
497
522
|
if (v.type !== void 0) return `{ .size = { ${v.percent !== void 0 ? `.percent = ${c("", v.percent)}` : `.minMax = { ${c("", v.min ?? 0)}, ${c("", v.max ?? 0)} }`} }, .type = ${c("type", v.type)} }`;
|
|
498
523
|
return `{ ${Object.entries(v).map(([k, x]) => `.${k} = ${c(k, x)}`).join(", ")} }`;
|
|
499
524
|
};
|
|
@@ -504,6 +529,8 @@ const SHAPE = /* @__PURE__ */ new Set([
|
|
|
504
529
|
"border",
|
|
505
530
|
"clip",
|
|
506
531
|
"floating",
|
|
532
|
+
"image",
|
|
533
|
+
"aspectRatio",
|
|
507
534
|
"text",
|
|
508
535
|
"themes",
|
|
509
536
|
...STATES
|
|
@@ -547,4 +574,4 @@ const header = (styles) => {
|
|
|
547
574
|
return `${lines.join("\n")}\n`;
|
|
548
575
|
};
|
|
549
576
|
//#endregion
|
|
550
|
-
export { active, alignItems, backgroundColor, bg, border, borderColor, borderRadius, borderWidth, cn, color, createGlobalTheme, createTokens, flexDirection, float, focus, fontSize, fonts, gap, header, height, hover, justifyContent, letterSpacing, lineHeight, overflow, p, padding, paddingBlock, paddingBottom, paddingInline, paddingLeft, paddingRight, paddingTop, pb, pl, pr, pt, textAlign, whiteSpace, width };
|
|
577
|
+
export { active, alignItems, backgroundColor, bg, border, borderColor, borderRadius, borderWidth, cn, color, createGlobalTheme, createTokens, flexDirection, float, focus, fontSize, fonts, gap, header, height, hover, image, justifyContent, letterSpacing, lineHeight, overflow, p, padding, paddingBlock, paddingBottom, paddingInline, paddingLeft, paddingRight, paddingTop, pb, pl, pr, pt, textAlign, whiteSpace, width };
|
package/docs/API.md
CHANGED
|
@@ -1269,7 +1269,15 @@ const { ratio, text } = await Effect.runPromise(contrast("#777", "white"));
|
|
|
1269
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`).
|
|
1270
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.
|
|
1271
1271
|
|
|
1272
|
-
### 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
|
+
> [`native/`](../native/README.md) in this repository instead: `native/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); `native/docs/map.md` step 6 is the full account.
|
|
1273
1281
|
|
|
1274
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).
|
|
1275
1283
|
|
|
@@ -1406,7 +1414,7 @@ static const Clay_TextElementConfig label_text = { .textColor = { .r = 235, .g =
|
|
|
1406
1414
|
CLAY(CLAY_ID("Units"), pill) { CLAY_TEXT(CLAY_STRING("switch to g"), label_text); }
|
|
1407
1415
|
```
|
|
1408
1416
|
|
|
1409
|
-
- **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), and for text `color`, `fontSize`, `letterSpacing`, `lineHeight`, `textAlign`, `whiteSpace` (`wrapMode`); `cn` merges, a later prop wins. Any other property is not a member.
|
|
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.
|
|
1410
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.
|
|
1411
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`.
|
|
1412
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.
|
|
@@ -1471,6 +1479,31 @@ css.float({ attach: { element: "top left" } }); // error: a point is x, then y
|
|
|
1471
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 } };
|
|
1472
1480
|
```
|
|
1473
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
|
+
|
|
1474
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).
|
|
1475
1508
|
|
|
1476
1509
|
### Export groups: `cssints/widely-available`, `cssints/css-grid-2`, … (decided 2026-10-11, cssints-n7z3)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cssints",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
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": {
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
}
|
|
69
69
|
},
|
|
70
70
|
"dependencies": {
|
|
71
|
-
"@cssints/css-grammar": "^0.
|
|
71
|
+
"@cssints/css-grammar": "^0.5.1",
|
|
72
72
|
"culori": "^4.0.2",
|
|
73
73
|
"effect": "4.0.0",
|
|
74
74
|
"happy-dom": "^20.14.5",
|