@cssints/css-grammar 0.1.0 → 0.2.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,17 +2,29 @@
2
2
 
3
3
  <img src="https://cssints.dev/logo.svg" alt="" width="96" height="96">
4
4
 
5
- cssints is compile-time CSS-in-TypeScript. You write calls such as `css.flex().alignItems("center")`. A Vite plugin runs them at build time and replaces each one with atomic, content-hashed class names in `@layer`s. Nothing of the styling code ships to the browser except `cx` and the few lines behind `cv`, which join class names.
5
+ cssints is compile-time CSS-in-TypeScript. You write calls such as `css.flex().alignItems("center")`. A Vite plugin runs them at build time and replaces each one with atomic, content-hashed class names in `@layer`s. Nothing of the styling code ships to the browser except a few lines that join class names: for a `cn()` with runtime arguments and for `cv`.
6
6
 
7
7
  - Values are checked against the spec grammars (mdn-data) in the types and again at build time. A typo is an error in the editor and in the build.
8
8
  - Conditions (`media`, `container`, `supports`, `hover`), variants (`cv`), design tokens and themes (`createTokens`, `createGlobalTheme`, `check.contrast`) are all build-time, and all typed.
9
9
  - LightningCSS optimizes the output. Browser support (`@mdn/browser-compat-data` against your browserslist) is reported as warnings, except for what LightningCSS lowers for those targets.
10
10
 
11
- Status: 0.1 is not published yet. Decisions and findings are in [docs/wayfinder.md](docs/wayfinder.md); the API reference is [docs/API.md](docs/API.md). Both are also a site with search and type hovers: [apps/docs](apps/docs) (`cd apps/docs && bun run dev`); deployed to Cloudflare with Alchemy: <https://cssints.dev>.
11
+ > **Alpha.** cssints is on npm at 0.x. The API can still change between minor versions, the plugin packages
12
+ > may move, and some browsers and frameworks are checked less than others (docs/API.md, "Browser support and what 0.1
13
+ > does not have"). Pin the version, and report what breaks.
14
+
15
+ Decisions and findings are in [docs/wayfinder.md](docs/wayfinder.md); the API reference is [docs/API.md](docs/API.md). Both are also a site with search and type hovers: [apps/docs](apps/docs) (`cd apps/docs && bun run dev`); deployed to Cloudflare with Alchemy: <https://cssints.dev>.
12
16
 
13
17
  ## Install
14
18
 
15
- Not on npm yet. In this repo `cssints` is a workspace package (`apps/demo` depends on `"cssints": "workspace:*"`). It needs Vite 8 (Vite+ works too) and, for the plugin, Node with type stripping (22.18 or later).
19
+ ```bash
20
+ npm install --save-dev cssints vite@8 # or: bun add -d cssints vite@8, pnpm add -D cssints vite@8
21
+ ```
22
+
23
+ cssints needs Vite 8 (Vite+ works too) and Node 22.18 or later (the plugin runs your modules with type stripping). It is a
24
+ dev dependency: nothing of it ships except the few lines behind `cn` with runtime arguments and `cv`. The plugin packages are installed the same
25
+ way, each beside `cssints` (`npm install --save-dev @cssints/fluid`); the list is on the
26
+ [plugin packages](docs/API.md#plugin-packages-cssints-dk1q) page. In this repository `cssints` is a workspace package
27
+ (`"cssints": "workspace:*"`).
16
28
 
17
29
  ```ts
18
30
  // vite.config.ts
@@ -48,10 +60,9 @@ export default {
48
60
  };
49
61
  ```
50
62
 
51
- `tsconfig.json` is Solid's: `"jsx": "preserve"`, `"jsxImportSource": "@solidjs/web"`, `"types": ["vite/client"]`. Solid's `class` takes a string, an object or an array, so `cx` is not needed: `class={[card, on() && p(4)]}` and `class={badge({ tone: tone() })}` work as in React. A site's arguments are still build-time values, so a signal goes in the condition or in a `cv` variant, not in `p(n())`. Under `vite dev` an edit of a component replaces it and resets its state below it (Solid's refresh); an edit of a style changes the class strings and the sheet with no reload.
63
+ `tsconfig.json` is Solid's: `"jsx": "preserve"`, `"jsxImportSource": "@solidjs/web"`, `"types": ["vite/client"]`. Solid's `class` takes a string, an object or an array, so `cn` is not needed for the join: `class={[card, on() && p(4)]}` and `class={badge({ tone: tone() })}` work as in React. A site's arguments are still build-time values, so a signal goes in the condition or in a `cv` variant, not in `p(n())`. Under `vite dev` an edit of a component replaces it and resets its state below it (Solid's refresh); an edit of a style changes the class strings and the sheet with no reload.
52
64
 
53
65
  ```tsx
54
- import { cx } from "cssints";
55
66
  import { bg, color, cn, cv, flex, hover, media, p, style, token } from "cssints" with { type: "cssints" };
56
67
 
57
68
  const md = media("(width >= 40rem)");
@@ -63,13 +74,13 @@ const button = cv({
63
74
  });
64
75
 
65
76
  export const Row = ({ wide, on }: { wide?: boolean; on?: boolean }) => (
66
- <div className={cx(flex().alignItems("center").gap(2), on && p(1))} style={style(token.set(accent, "#dc2626"))}>
77
+ <div className={cn(flex().alignItems("center").gap(2), on && p(1))} style={style(token.set(accent, "#dc2626"))}>
67
78
  <button className={button({ wide })}>Save</button>
68
79
  </div>
69
80
  );
70
81
  ```
71
82
 
72
- `with { type: "cssints" }` marks what runs at build time. The plugin replaces each call with a class string, so the browser gets `cx("_1s47490 _1ob5z5m _g2iyum", on && "_lqk467")` and `button` is a call of the runtime with `{ base: "_1t8gqy5 ...", ... }`, so it never sees `flex()`. `virtual:cssints.css` is one sheet with every rule once, sorted by layer, then condition, then shorthand before longhand, then class name:
83
+ `with { type: "cssints" }` marks what runs at build time. The plugin replaces each call with a class string, so the browser gets `__cx([], "_1s47490 _1ob5z5m _g2iyum", on && "_lqk467")` and `button` is a call of the runtime with `{ base: "_1t8gqy5 ...", ... }`, so it never sees `flex()`. `virtual:cssints.css` is one sheet with every rule once, sorted by layer, then condition, then shorthand before longhand, then class name:
73
84
 
74
85
  ```css
75
86
  @layer _.k, _.a, _.b, _.c, _.d, _.e;
@@ -95,7 +106,7 @@ export const Row = ({ wide, on }: { wide?: boolean; on?: boolean }) => (
95
106
 
96
107
  `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".
97
108
 
98
- More in [docs/API.md](docs/API.md): property names and short names, `flex()` and `grid()` chains, conditions and layers, tokens and themes, `cx`, `cn` and `cv`, and the errors you can get.
109
+ 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.
99
110
 
100
111
  ## Debugging
101
112
 
@@ -104,7 +115,7 @@ More in [docs/API.md](docs/API.md): property names and short names, `flex()` and
104
115
  ## For agents
105
116
 
106
117
  `cssints` ships two [agent skills](https://agentskills.io) in its package, `skills/cssints` (writing styles: setup,
107
- `cn`/`cx`/`cv`, conditions, tokens and themes, the plugin packages, build errors) and `skills/cssints-plugin` (writing
118
+ `cn`/`cv`, conditions, tokens and themes, the plugin packages, build errors) and `skills/cssints-plugin` (writing
108
119
  a plugin on `scope()`, with its tests). Install them into your agent's skills folder:
109
120
 
110
121
  ```bash
@@ -112,8 +123,7 @@ npx skills-npm # links the skills of every instal
112
123
  npx skills add ./node_modules/cssints/skills # or the skills CLI, for opencode, Claude Code, Codex, Cursor, ...
113
124
  ```
114
125
 
115
- Until 0.1 is on npm, point `npx skills add` at `packages/cssints/skills` of this repository. Agents working on this
116
- repository get `.agents/skills/cssints-contributor` (also `.claude/skills`) with no install.
126
+ Agents working on this repository get `.agents/skills/cssints-contributor` (also `.claude/skills`) with no install.
117
127
 
118
128
  `@cssints/mcp` is an MCP server with the build's own checks (`check_declaration`, `check_support`, `check_contrast`,
119
129
  `compile`, `search_docs`, `list_plugins`), in [packages/mcp](packages/mcp) with the config for opencode, Claude Code
@@ -20,6 +20,14 @@ export interface Warning {
20
20
  }
21
21
  export declare const lackingOf: (key: string, ts?: Target[], exact?: boolean) => Target[];
22
22
  export declare const lacking: (key: string, ts?: Target[] | undefined, exact?: boolean | undefined) => Effect.Effect<Target[], never, never>;
23
+ /**
24
+ * The BCD key of a keyword or function in a value of a property, by the lookup above.
25
+ * @param {string} p the property
26
+ * @param {string} k the keyword, or the name of the function
27
+ * @param {boolean} fn whether `k` is a function
28
+ * @returns {string | undefined} the key, or `undefined` when BCD has none
29
+ */
30
+ export declare const tokenKey: (p: string, k: string, fn: boolean) => string | undefined;
23
31
  export declare function supportOf(p: string, v: string, ts?: Target[]): Warning[];
24
32
  export declare function browsers(lacking: string[]): string;
25
33
  export declare const support: (p: string, v: string, ts?: Target[] | undefined) => Effect.Effect<Warning[], never, never>;
package/dist/support.mjs CHANGED
@@ -77,6 +77,23 @@ const reachOf = (p) => {
77
77
  }
78
78
  return r;
79
79
  };
80
+ const tokenKeys = (owners, types, k, fn) => [
81
+ ...owners.map((q) => `css.properties.${q}.${fn ? `${k}_function` : k}`),
82
+ ...fn ? owners.map((q) => `css.properties.${q}.${k}`) : [],
83
+ ...fn ? [`css.types.${k}`] : [],
84
+ ...types.map((t) => `css.types.${t}.${k}`)
85
+ ];
86
+ /**
87
+ * The BCD key of a keyword or function in a value of a property, by the lookup above.
88
+ * @param {string} p the property
89
+ * @param {string} k the keyword, or the name of the function
90
+ * @param {boolean} fn whether `k` is a function
91
+ * @returns {string | undefined} the key, or `undefined` when BCD has none
92
+ */
93
+ const tokenKey = (p, k, fn) => {
94
+ const [types, owners] = reachOf(p);
95
+ return tokenKeys(owners, types, k, fn).find((x) => get(x)?.__compat);
96
+ };
80
97
  const known = /* @__PURE__ */ new WeakMap();
81
98
  function supportOf(p, v, ts = current()) {
82
99
  let byDecl = known.get(ts);
@@ -95,12 +112,7 @@ function compute(p, v, ts) {
95
112
  for (const { 0: tok, 1: fn, index } of code.matchAll(/(?<![\w.#-])-?[A-Za-z_][\w-]*(\()?/g)) {
96
113
  const k = fn ? tok.slice(0, -1) : tok;
97
114
  const rel = tok === "from" ? /([a-z-]+)\(\s*$/.exec(code.slice(0, index))?.[1] : void 0;
98
- const key = (rel ? [`css.types.color.${rel}.relative_syntax`] : [
99
- ...owners.map((q) => `css.properties.${q}.${fn ? `${k}_function` : k}`),
100
- ...fn ? owners.map((q) => `css.properties.${q}.${k}`) : [],
101
- ...fn ? [`css.types.${k}`] : [],
102
- ...types.map((t) => `css.types.${t}.${k}`)
103
- ]).find((x) => get(x)?.__compat);
115
+ const key = (rel ? [`css.types.color.${rel}.relative_syntax`] : tokenKeys(owners, types, k, !!fn)).find((x) => get(x)?.__compat);
104
116
  if (key) found.push([fn ? `${k}()` : k, key]);
105
117
  }
106
118
  if (/"[^"]*"|'[^']*'/.test(v)) {
@@ -145,4 +157,4 @@ const message = Effect.fn("support.message")(function* (p, v, w) {
145
157
  return yield* Effect.sync(() => messageOf(p, v, w));
146
158
  });
147
159
  //#endregion
148
- export { browsers, get, lacking, lackingOf, message, messageOf, support, supportOf, targets, useTargets };
160
+ export { browsers, get, lacking, lackingOf, message, messageOf, support, supportOf, targets, tokenKey, useTargets };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cssints/css-grammar",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Type-level and runtime recognizer of CSS values over mdn-data grammars (the engine of cssints)",
5
5
  "license": "MIT",
6
6
  "repository": {