cssints 0.0.0-stage → 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.
Files changed (148) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +188 -2
  3. package/dist/author-types-C1AhsnrT.d.mts +80 -0
  4. package/dist/check.d.mts +22 -0
  5. package/dist/check.mjs +37 -0
  6. package/dist/color-BYIxVLlS.mjs +29 -0
  7. package/dist/color-C8OzjAeg.mjs +2 -0
  8. package/dist/contrast-DhK69Xak.mjs +83 -0
  9. package/dist/derived.d.mts +14 -0
  10. package/dist/derived.mjs +391 -0
  11. package/dist/errors-D4ZbcwRo.d.mts +32 -0
  12. package/dist/frame-BoBKjgBB.mjs +92 -0
  13. package/dist/frame.d.mts +72 -0
  14. package/dist/frame.mjs +2 -0
  15. package/dist/group-common-9zpBxfUF.d.mts +1 -0
  16. package/dist/group-types-9ttphdku.d.mts +55 -0
  17. package/dist/groups/compositing-2.d.mts +20 -0
  18. package/dist/groups/css-align-3.d.mts +29 -0
  19. package/dist/groups/css-anchor-position-1.d.mts +49 -0
  20. package/dist/groups/css-anchor-position-2.d.mts +50 -0
  21. package/dist/groups/css-animations-1.d.mts +28 -0
  22. package/dist/groups/css-animations-2.d.mts +29 -0
  23. package/dist/groups/css-backgrounds-3.d.mts +27 -0
  24. package/dist/groups/css-backgrounds-4.d.mts +29 -0
  25. package/dist/groups/css-borders-4.d.mts +95 -0
  26. package/dist/groups/css-box-4.d.mts +38 -0
  27. package/dist/groups/css-break-4.d.mts +23 -0
  28. package/dist/groups/css-cascade-5.d.mts +18 -0
  29. package/dist/groups/css-cascade-6.d.mts +18 -0
  30. package/dist/groups/css-color-4.d.mts +29 -0
  31. package/dist/groups/css-color-5.d.mts +32 -0
  32. package/dist/groups/css-color-6.d.mts +32 -0
  33. package/dist/groups/css-color-adjust-1.d.mts +20 -0
  34. package/dist/groups/css-color-hdr-1.d.mts +19 -0
  35. package/dist/groups/css-conditional-3.d.mts +17 -0
  36. package/dist/groups/css-conditional-4.d.mts +17 -0
  37. package/dist/groups/css-conditional-5.d.mts +22 -0
  38. package/dist/groups/css-contain-2.d.mts +19 -0
  39. package/dist/groups/css-content-3.d.mts +23 -0
  40. package/dist/groups/css-counter-styles-3.d.mts +16 -0
  41. package/dist/groups/css-display-4.d.mts +22 -0
  42. package/dist/groups/css-easing-2.d.mts +18 -0
  43. package/dist/groups/css-env-1.d.mts +16 -0
  44. package/dist/groups/css-flexbox-1.d.mts +24 -0
  45. package/dist/groups/css-fonts-4.d.mts +46 -0
  46. package/dist/groups/css-fonts-5.d.mts +44 -0
  47. package/dist/groups/css-forms-1.d.mts +19 -0
  48. package/dist/groups/css-gaps-1.d.mts +21 -0
  49. package/dist/groups/css-gcpm-3.d.mts +20 -0
  50. package/dist/groups/css-gcpm-4.d.mts +20 -0
  51. package/dist/groups/css-grid-2.d.mts +34 -0
  52. package/dist/groups/css-grid-3.d.mts +34 -0
  53. package/dist/groups/css-images-3.d.mts +25 -0
  54. package/dist/groups/css-images-4.d.mts +32 -0
  55. package/dist/groups/css-images-5.d.mts +33 -0
  56. package/dist/groups/css-inline-3.d.mts +28 -0
  57. package/dist/groups/css-lists-3.d.mts +26 -0
  58. package/dist/groups/css-logical-1.d.mts +35 -0
  59. package/dist/groups/css-masking-1.d.mts +37 -0
  60. package/dist/groups/css-multicol-2.d.mts +24 -0
  61. package/dist/groups/css-overflow-3.d.mts +28 -0
  62. package/dist/groups/css-overflow-4.d.mts +29 -0
  63. package/dist/groups/css-overflow-5.d.mts +31 -0
  64. package/dist/groups/css-overscroll-1.d.mts +22 -0
  65. package/dist/groups/css-page-3.d.mts +18 -0
  66. package/dist/groups/css-page-floats-3.d.mts +19 -0
  67. package/dist/groups/css-position-3.d.mts +29 -0
  68. package/dist/groups/css-position-4.d.mts +30 -0
  69. package/dist/groups/css-rhythm-1.d.mts +18 -0
  70. package/dist/groups/css-round-display-1.d.mts +16 -0
  71. package/dist/groups/css-ruby-1.d.mts +21 -0
  72. package/dist/groups/css-scroll-anchoring-1.d.mts +18 -0
  73. package/dist/groups/css-scroll-snap-1.d.mts +42 -0
  74. package/dist/groups/css-scroll-snap-2.d.mts +43 -0
  75. package/dist/groups/css-scrollbars-1.d.mts +19 -0
  76. package/dist/groups/css-shapes-1.d.mts +27 -0
  77. package/dist/groups/css-shapes-2.d.mts +27 -0
  78. package/dist/groups/css-size-adjust-1.d.mts +18 -0
  79. package/dist/groups/css-sizing-3.d.mts +34 -0
  80. package/dist/groups/css-sizing-4.d.mts +33 -0
  81. package/dist/groups/css-speech-1.d.mts +18 -0
  82. package/dist/groups/css-tables-3.d.mts +22 -0
  83. package/dist/groups/css-text-3.d.mts +36 -0
  84. package/dist/groups/css-text-4.d.mts +40 -0
  85. package/dist/groups/css-text-decor-3.d.mts +30 -0
  86. package/dist/groups/css-text-decor-4.d.mts +31 -0
  87. package/dist/groups/css-transforms-1.d.mts +31 -0
  88. package/dist/groups/css-transforms-2.d.mts +48 -0
  89. package/dist/groups/css-transitions-1.d.mts +22 -0
  90. package/dist/groups/css-transitions-2.d.mts +23 -0
  91. package/dist/groups/css-ui-4.d.mts +35 -0
  92. package/dist/groups/css-values-4.d.mts +38 -0
  93. package/dist/groups/css-values-5.d.mts +43 -0
  94. package/dist/groups/css-variables-2.d.mts +6 -0
  95. package/dist/groups/css-view-transitions-2.d.mts +20 -0
  96. package/dist/groups/css-viewport.d.mts +18 -0
  97. package/dist/groups/css-will-change-1.d.mts +18 -0
  98. package/dist/groups/css-writing-modes-4.d.mts +22 -0
  99. package/dist/groups/css2.d.mts +19 -0
  100. package/dist/groups/fill-stroke-3.d.mts +27 -0
  101. package/dist/groups/filter-effects-1.d.mts +32 -0
  102. package/dist/groups/filter-effects-2.d.mts +33 -0
  103. package/dist/groups/limited-availability.d.mts +2 -0
  104. package/dist/groups/mediaqueries-4.d.mts +16 -0
  105. package/dist/groups/mediaqueries-5.d.mts +16 -0
  106. package/dist/groups/motion-1.d.mts +24 -0
  107. package/dist/groups/newly-available.d.mts +631 -0
  108. package/dist/groups/not-discouraged.d.mts +727 -0
  109. package/dist/groups/scroll-animations-1.d.mts +30 -0
  110. package/dist/groups/selectors-4.d.mts +54 -0
  111. package/dist/groups/selectors-5.d.mts +54 -0
  112. package/dist/groups/widely-available.d.mts +616 -0
  113. package/dist/index.d.mts +48 -0
  114. package/dist/index.mjs +1269 -0
  115. package/dist/instrument-gCazujmb.mjs +1624 -0
  116. package/dist/lowered-D2KusPLp.mjs +28 -0
  117. package/dist/node-OJ_w_Usj.d.mts +233 -0
  118. package/dist/parent-vars.d.mts +13 -0
  119. package/dist/parent-vars.mjs +65 -0
  120. package/dist/plugin-types.d.mts +377 -0
  121. package/dist/plugin.d.mts +3 -0
  122. package/dist/plugin.mjs +30 -0
  123. package/dist/registry-CTumHB8p.mjs +73 -0
  124. package/dist/runtime-Dt4Tm0Q7.d.mts +19 -0
  125. package/dist/runtime.d.mts +18 -0
  126. package/dist/runtime.mjs +25 -0
  127. package/dist/scope-ZVhUxQH1.d.mts +2101 -0
  128. package/dist/sheet-BKsMTa0c.mjs +757 -0
  129. package/dist/surface.d.mts +228 -0
  130. package/dist/surface.mjs +101 -0
  131. package/dist/theme-BsP2E4LB.mjs +929 -0
  132. package/dist/theme-types-Bqv0Ggz_.d.mts +1808 -0
  133. package/dist/trace-otlp-CbKFDsn9.mjs +14 -0
  134. package/dist/tui-types.d.mts +181 -0
  135. package/dist/tui.d.mts +23 -0
  136. package/dist/tui.mjs +427 -0
  137. package/dist/types.d.mts +2 -0
  138. package/dist/vite.d.mts +134 -0
  139. package/dist/vite.mjs +977 -0
  140. package/docs/API.md +1193 -0
  141. package/package.json +85 -4
  142. package/skills/cssints/SKILL.md +203 -0
  143. package/skills/cssints/references/errors.md +86 -0
  144. package/skills/cssints/references/plugins.md +91 -0
  145. package/skills/cssints/references/tokens.md +91 -0
  146. package/skills/cssints-plugin/SKILL.md +177 -0
  147. package/skills/cssints-plugin/references/table.md +124 -0
  148. package/skills/cssints-plugin/references/testing.md +122 -0
package/package.json CHANGED
@@ -1,6 +1,87 @@
1
1
  {
2
2
  "name": "cssints",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.0",
4
+ "description": "CSS in TypeScript: typed css.* calls compiled to atomic CSS at build time (a Vite plugin)",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/alexeiaccio/css-in-ts-compiler.git",
9
+ "directory": "packages/cssints"
10
+ },
11
+ "homepage": "https://cssints.dev",
12
+ "files": [
13
+ "dist",
14
+ "skills",
15
+ "docs"
16
+ ],
17
+ "type": "module",
18
+ "sideEffects": false,
19
+ "exports": {
20
+ ".": {
21
+ "types": "./dist/types.d.mts",
22
+ "browser": "./dist/runtime.mjs",
23
+ "default": "./dist/index.mjs"
24
+ },
25
+ "./vite": {
26
+ "types": "./dist/vite.d.mts",
27
+ "default": "./dist/vite.mjs"
28
+ },
29
+ "./plugin": {
30
+ "types": "./dist/plugin-types.d.mts",
31
+ "default": "./dist/plugin.mjs"
32
+ },
33
+ "./frame": {
34
+ "types": "./dist/frame.d.mts",
35
+ "default": "./dist/frame.mjs"
36
+ },
37
+ "./surface": {
38
+ "types": "./dist/surface.d.mts",
39
+ "default": "./dist/surface.mjs"
40
+ },
41
+ "./parent-vars": {
42
+ "types": "./dist/parent-vars.d.mts",
43
+ "default": "./dist/parent-vars.mjs"
44
+ },
45
+ "./derived": {
46
+ "types": "./dist/derived.d.mts",
47
+ "default": "./dist/derived.mjs"
48
+ },
49
+ "./check": {
50
+ "types": "./dist/check.d.mts",
51
+ "default": "./dist/check.mjs"
52
+ },
53
+ "./tui": {
54
+ "types": "./dist/tui-types.d.mts",
55
+ "default": "./dist/tui.mjs"
56
+ },
57
+ "./*": {
58
+ "types": "./dist/groups/*.d.mts",
59
+ "default": "./dist/index.mjs"
60
+ }
61
+ },
62
+ "dependencies": {
63
+ "@cssints/css-grammar": "^0.2.0",
64
+ "culori": "^4.0.2",
65
+ "effect": "4.0.0",
66
+ "happy-dom": "^20.14.5",
67
+ "lightningcss": "^1.32.0",
68
+ "magic-string": "^0.30.0",
69
+ "mdn-data": "2.27.1",
70
+ "oxc-parser": "^0.149.0"
71
+ },
72
+ "peerDependencies": {
73
+ "@opencode/client": "^2.0.26",
74
+ "vite": "^8.3.0"
75
+ },
76
+ "peerDependenciesMeta": {
77
+ "@opencode/client": {
78
+ "optional": true
79
+ }
80
+ },
81
+ "engines": {
82
+ "node": ">=22"
83
+ },
84
+ "publishConfig": {
85
+ "access": "public"
86
+ }
87
+ }
@@ -0,0 +1,203 @@
1
+ ---
2
+ name: cssints
3
+ description: >-
4
+ Write, review and fix styles in a project that uses cssints, the CSS-in-TypeScript compiler (a Vite plugin that
5
+ runs css.* calls at build time and replaces them with atomic class strings). Load it when code imports from
6
+ "cssints" with { type: "cssints" }, uses cn(), cv(), css.* members, media()/hover()/container()/within(),
7
+ createTokens, createGlobalTheme, check.contrast, or an @cssints/* package (layout, motion, fluid, fonts, reset,
8
+ tokens, icons, ...), and when a build fails with a "cssints:" message such as "must be known at build time",
9
+ "without the attribute", "unexpected ... expected <...>", "overlap without one containing the other" or
10
+ "wraps ... the marker of kernel".
11
+ ---
12
+
13
+ # cssints: styles in TypeScript, compiled at build time
14
+
15
+ Every `css.*` call runs at build time in Vite's Module Runner and is replaced by a class string. The values are
16
+ checked against the CSS grammars (mdn-data) twice: in the types, where the call is, and again by the build, with the
17
+ file and the place of the call. One sheet, `virtual:cssints.css`, holds every rule once, in `@layer`s. The browser
18
+ gets class strings and the few lines behind `cn` with runtime arguments and `cv`. The reference is `docs/API.md` of the cssints repository; the
19
+ parts below are what you need most of the time.
20
+
21
+ If the cssints MCP server is configured, prefer it for checks: `check_declaration` (is this value valid, and the
22
+ message the build would give), `check_support` (browser support for the project's browserslist), `check_contrast`,
23
+ `compile` (a module's classes, CSS, errors and warnings), `search_docs` and `list_plugins`. On a running dev server
24
+ of an app with `cssints({ devtools: true })` (`url`, default `http://localhost:5173`): `explain_class` (a class from
25
+ the DOM → its declarations, conditions, layer, tokens and the call that made it), `module_styles` (a file's calls and
26
+ their CSS) and `dev_errors` (the build errors Vite has now). In a headless Chromium (playwright-core):
27
+ `inspect_element` (a CSS selector → the element's classes by call, the winner per property, what it overrides, ✓/✗
28
+ against the computed style) and `why_property` (why one property has its value), with `hover`, `focus`, `width` and
29
+ `height`. Without it, the build is the check: run the dev server or `vite build` and read the message.
30
+
31
+ ## Setup
32
+
33
+ ```ts
34
+ // vite.config.ts
35
+ import cssints, { cssTarget, targets } from "cssints/vite";
36
+
37
+ export default {
38
+ plugins: [cssints()], // any order beside React, Solid, ... plugins
39
+ css: { transformer: "lightningcss", lightningcss: { targets }, devSourcemap: true },
40
+ build: { cssMinify: "lightningcss", cssTarget },
41
+ };
42
+ ```
43
+
44
+ - The entry imports `"virtual:cssints.css"` once. `tsconfig.json` needs `"types": ["vite/client"]`.
45
+ - `targets` and `cssTarget` come from the browserslist (`BROWSERSLIST`, else `package.json` / `.browserslistrc`, else
46
+ `defaults`), read from `process.cwd()`: run Vite from the app's folder. Support warnings read the config of Vite's
47
+ `root`, and each is said at the `css.*` call that made it, once per file per build (in dev, again after an edit).
48
+ - `devSourcemap: true` maps each class rule in Chrome's Styles pane to the call that made it; in a build,
49
+ `build: { sourcemap: true }` (or `"hidden"`) maps the sheet asset too (bundle analysers split its bytes by module).
50
+
51
+ ## The import attribute
52
+
53
+ ```ts
54
+ import { cn, cv, hover, media, p } from "cssints" with { type: "cssints" }; // build time
55
+ import * as css from "cssints" with { type: "cssints" }; // the same, as a namespace
56
+ import { stack } from "./stack.ts" with { type: "cssints" }; // your own helper module, run at build time
57
+ ```
58
+
59
+ - A build-time member imported without the attribute is a build error:
60
+ `imports build-time p from "cssints" without the attribute`.
61
+ - Plugin packages are imported with the attribute too: `import { stack } from "@cssints/layout" with { type: "cssints" }`.
62
+ - `import type { Style } from "cssints"` needs no attribute.
63
+
64
+ ## Arguments are build-time values
65
+
66
+ `p(n)` with `n` a prop, a parameter or state fails: `css arguments must be known at build time: n is not defined
67
+ where the call is evaluated`. The types do not catch it. Choose at runtime between styles made at build time:
68
+
69
+ ```tsx
70
+ <div className={cn(base, on && p(4))} /> // a condition: cn joins it at runtime, p(4) is a class already
71
+ <button className={button({ size })} /> // a cv() variant
72
+ <div style={style(token.set(accent, "#dc2626"))} /> // a token set inline (static values)
73
+ <div style={{ "--accent": value }} /> // a dynamic value: a plain style prop
74
+ ```
75
+
76
+ A helper module imported with the attribute may take parameters (`stack(4)`): each call is a site of its own. A call
77
+ the run does not reach (`false && p(4)`, a branch) still gets its class string.
78
+
79
+ ## Properties and values
80
+
81
+ - Every property is a camelCase member (`paddingLeft`, `alignItems`); short names `p pt pr pb pl`, `m mt mr mb ml`,
82
+ `bg`. There is no `items` or `justify`.
83
+ - A bare number is a spacing step of 0.25rem in `padding*`, `margin*`, `gap*`, `inset*`: `p(4)` is `1rem`. Elsewhere
84
+ it is a `<number>`: `opacity(0.5)`; `width(4)` is a type error, write `width("4rem")`.
85
+ - Parts are joined by a space: `border("1px solid", accent)`, `padding("1rem 2rem")`, `margin("0", "auto")`.
86
+ - Keywords are written in their canonical case: `color("RED")` is an error that says to write `"red"`.
87
+ - `flex()` and `grid()` with no argument set `display` and chain container properties without the prefix:
88
+ `flex().direction("column").alignItems("center").gap(2)`. With an argument they are the shorthand
89
+ (`flex("1")`, a plain style that does not chain). Item properties (`flexGrow`, `alignSelf`) are plain members.
90
+ - CSS functions are calls: `calc(t.space.md, "*", 2)`, `clamp("1rem", "2vw", "2rem")`, `colorMix("in oklab", a, "black 15%")`,
91
+ `linearGradient("to right", [a, "25%"], "blue")`. Clashing names end in `Fn`: `colorFn`, `translateFn`, `attrFn`.
92
+ - `firstThatWorks("100dvh", "100vh")` is a value with fallbacks in one class.
93
+ - `keyframes({ from: opacity(0), to: opacity(1) })` returns a name for `animation(fade, "200ms")`.
94
+
95
+ ## Merging: cn, cv
96
+
97
+ - `cn(...)` merges at build time into one string; the later of one property wins (`cn(p(4), p(2))` is `p(2)`). A
98
+ longhand beats its shorthand whatever the order. Two properties that overlap without one containing the other
99
+ (`borderTop` and `borderColor`, `marginLeft` and `marginInline`) are a build error: set one, or the longhands.
100
+ - `cn(...)` takes runtime values too: `cn(base, on && p(4), props.class)`. An argument that names a parameter, a prop
101
+ or state, a string, or a call of something that is not a `css.*` member is joined at runtime (falsy values skipped,
102
+ arrays flattened; no clsx objects); the styles in it are class strings made at build time, and while a style
103
+ is active it replaces the classes it overrides (`cn(p(2), on && p(4))` is `p-4` when `on`, else `p-2`). A class
104
+ string such as `props.class` is only appended. `cx` is gone: importing it is a build error.
105
+ - `cv({ base, variants, defaultVariants, compoundVariants })` is the function you call; props are typed. A variant
106
+ wins over the base, a compound (`{ size: "lg", tone: "loud", class: p(6) }`) over both. Values are `css.*` styles,
107
+ never CSS strings.
108
+
109
+ ```ts
110
+ const button = cv({
111
+ base: cn(p(2), fontSize("1rem")),
112
+ variants: { size: { sm: p(1), lg: p(4) }, disabled: { true: opacity(0.5) } },
113
+ defaultVariants: { size: "sm" },
114
+ });
115
+ button({ size: "lg", disabled: true });
116
+ ```
117
+
118
+ ## Conditions and layers
119
+
120
+ Conditions are wrappers that nest: `hover(bg("#eee"))`, `media("(width >= 40rem)")(p(4))`, `md(hover(...))`.
121
+
122
+ - Pseudo-classes: `hover`, `focus`, `focusVisible`, `active`, `disabled` and the other standard ones without arguments.
123
+ - `media(q)`, `container("sidebar (width < 30rem)")`, `supports("(display: grid)")`; queries are checked, and a range
124
+ needs parentheses: `(width >= 40rem)`, not `min-width > 40rem`. Bind them once: `const md = media("(width >= 40rem)")`.
125
+ - `attr("aria-current", "page")(color("blue"))`; `attr(name)` is presence; `op` is `=`, `~=`, `|=`, `^=`, `$=`, `*=`.
126
+ - Parent state: `group()` (or `group("card")`) marks the ancestor, `within(hover, "card")(...)` reads it.
127
+ - Layer = number of conditions: none `_.a`, one `_.b`, up to four (`_.e`); a fifth is an error. More conditions win.
128
+ In one layer the sheet orders rules (pseudo-classes by their usual order, queries by min-width, `attr()` after
129
+ pseudo-classes), not your argument order.
130
+ - `global(selector, ...styles)` is a rule with no class (a reset): in `_.k`, below every class. It takes `media()`,
131
+ `container()` and `supports()`, not `hover` (put the state in the selector).
132
+ - `layers("app", "author", "viewer")` declares top-level layers after the engine's; `{ before: true }` before them.
133
+ - No pseudo-elements and no `:has()` in 0.1.
134
+
135
+ A condition wraps declarations. A plugin member that is only a kernel's marker class (`pretty()`, `balance()`,
136
+ `frame()` with no method, `group()`) cannot go under a condition: `:hover wraps _balance-prettyCore, the marker of
137
+ kernel balance.prettyCore, with no declaration of its own`. Put the member outside, and the condition around one of
138
+ its methods (`cn(skeleton(), hover(skeleton().highlight("white")))`) or a plain property.
139
+
140
+ ## Tokens, themes, contrast
141
+
142
+ ```ts
143
+ const t = createTokens({
144
+ color: { $type: "color", bg: { $value: "#ffffff" }, fg: { $value: "#111827" } },
145
+ space: { $type: "dimension", md: { $value: "1rem" } },
146
+ });
147
+ createGlobalTheme(t, media("(prefers-color-scheme: dark)"), {
148
+ color: { bg: "#0b0b0f", fg: "#f3f4f6" },
149
+ });
150
+ createGlobalTheme(t, ".brand", { color: { fg: "#7c2d12" } });
151
+ check.contrast(t.color.fg, t.color.bg, { min: 4.5 }); // every theme too; below min is a build error
152
+ export const card = cn(bg(t.color.bg), color(t.color.fg), p(t.space.md));
153
+ ```
154
+
155
+ A token is its `var(--name)` and fits where a value of its type fits. Pass the token, not `"var(--color-bg)"` (a
156
+ warning). Single tokens: `token.color("#2563eb", "accent")` (named, settable) or `token.color("#6b7280")` (private).
157
+ DTCG JSON, Figma exports, references `{color.accent}`, derived values and layers of themes:
158
+ [references/tokens.md](references/tokens.md).
159
+
160
+ ## Plugin packages: which one
161
+
162
+ Import them with the attribute, except `@cssints/tokens` and `@cssints/ramp` (plain data for `createTokens`). A
163
+ member returns a class string (or a value) that goes into `cn()`. Details and gotchas:
164
+ [references/plugins.md](references/plugins.md).
165
+
166
+ | Need | Package |
167
+ | ------------------------------------------- | ------------------------------------------------------------ |
168
+ | Stacks, clusters, sidebars, auto grids | `@cssints/layout`: `stack()`, `cluster()`, `sidebar()`, ... |
169
+ | A length that grows with the viewport | `@cssints/fluid`: `fontSize(fluid("1rem", "1.5rem"))` |
170
+ | Web fonts with a metric-matched fallback | `@cssints/fonts`: `font(family, { provider })` |
171
+ | A CSS reset below every style | `@cssints/reset`: `reset({ media?, prose? })` |
172
+ | Ready-made tokens (Open Props, Radix, ...) | `@cssints/tokens/<set>`: data for `createTokens` |
173
+ | A palette with contrast steps | `@cssints/ramp`: `ramp(base, { mode, ... })` into tokens |
174
+ | Icons as a mask class | `@cssints/icons`: `icons({ provider, ... })` |
175
+ | Looks per `data-mode` (app, author, viewer) | `@cssints/mise-en-mode`: `look(t, name, source, overrides)` |
176
+ | Eased gradients | `@cssints/gradient`: `gradient(kind, dir, from, to, ...)` |
177
+ | Enter and exit animations | `@cssints/motion`: `motion().preset("fade-in")` |
178
+ | Animations on scroll into view | `@cssints/reveal`: `reveal().preset("slide-in-up")` |
179
+ | Styles while a sticky header is stuck | `@cssints/stuck`: `sticky().top("0")`, `stuck(styles)` |
180
+ | Balanced headings, no orphans | `@cssints/balance`: `balance()`, `pretty()` |
181
+ | Squircle corners | `@cssints/squircle`: `squircle().radius("24px")` |
182
+ | Masonry | `@cssints/masonry`: `masonry().columns("12rem")` |
183
+ | Image placeholders from the image's colours | `@cssints/placeholder`: `backgroundImage(placeholder(path))` |
184
+ | Loading skeletons in one phase | `@cssints/skeleton`: `skeleton().radius("50%")` |
185
+ | Concentric radius, parent-relative colour | `cssints/frame`: `frame()`; `cssints/surface`: `surface()` |
186
+
187
+ A member conflicts with the properties its kernel sets: `cn(stack(), display("grid"))` is a build error. Use the
188
+ member's methods instead.
189
+
190
+ ## Build errors
191
+
192
+ Read the message: it names the property or the member, the bad value and what the grammar expected, and it points at
193
+ the call. The common ones, with fixes: [references/errors.md](references/errors.md).
194
+
195
+ ## Rules of thumb
196
+
197
+ - Make every style at module level or in a helper module; choose between them at runtime with `cn(base, on && p(4))`, `cv` or tokens.
198
+ - A `cn()` with no runtime argument is checked for conflicts and leaves nothing at runtime; keep what is fixed in it.
199
+ - Prefer a token and a theme to a second style for each colour mode.
200
+ - Write `check.contrast` for each text/background pair you use; it checks every theme.
201
+ - A support warning is not an error: the value works where the targets have it. LightningCSS lowers what it can
202
+ (`oklch()` gets a fallback) and those values do not warn.
203
+ - Styles of a library on cssints are evaluated by the app's plugin; such a package must be a direct dependency.
@@ -0,0 +1,86 @@
1
+ # cssints build errors and warnings
2
+
3
+ A value error is given twice: as a type error on the bad part in the editor, and as a build error with the file and
4
+ the place of the call (the Vite overlay points at it). Some errors only the build sees: runtime arguments, conflicts,
5
+ contrast, ranges in some positions, `supports()` property names. The messages below are the real text, shortened with
6
+ `…`.
7
+
8
+ ## Imports and build-time values
9
+
10
+ | Message | Meaning and fix |
11
+ | ------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
12
+ | `<file> imports build-time p from "cssints" without the attribute. Import it as: … with { type: "cssints" }` | Every member runs at build time. Add `with { type: "cssints" }` to that import. |
13
+ | `css arguments must be known at build time: n is not defined where the call is evaluated (a parameter, a prop or state)` | The call reads a runtime value. Make the styles at build time and choose at runtime: `cn(base, on && p(4))`, a `cv()` variant, or a token set in `style={...}`. Also when a local shadows a global (`open`, `name`). |
14
+ | `imports cx from "cssints", which is gone: cn(...) takes runtime values now` | Old code: `cx(a, on && b)` is `cn(a, on && b)` now (cn from `"cssints" with { type: "cssints" }`). `cx(p(4), p(2))` kept both; `cn` keeps `p(2)`. |
15
+ | `imports _cv from "cssints", which is gone` | Old code: `cv({ ... })` is the function now. Drop `_cv(` and its closing bracket. |
16
+
17
+ ## Grammar errors
18
+
19
+ The shape is `<property>: unexpected "<token>" in "<value>", expected <grammar>`.
20
+
21
+ | Example | Fix |
22
+ | ------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- |
23
+ | `padding: unexpected "-0.25rem" in "-0.25rem", expected <length-percentage [0,∞]>` (`p(-1)`) | Out of the range. A negative spacing is not a padding; use `margin` for negative values. |
24
+ | `color: unexpected "RED" in "RED" ("RED" is red in CSS; write "red"), expected <color>` | Keywords and function names have one spelling, the canonical lowercase one. |
25
+ | `color: unexpected "1px" … ("var(--gap)" is checked as "1px": a token stands for a value of its type)` | A token of the wrong type: a length token in a colour property. Use a token of the property's type. |
26
+ | `width: unexpected "calc(1px" … (cannot add length and time)` | Math on mixed kinds. Check the units and the tokens in the `calc()`. |
27
+ | `@media: unexpected "min-width" in "screen and min-width > 40rem", expected <media-query>` | A range needs parentheses: `media("(width >= 40rem)")`. |
28
+ | `@supports: unexpected "grdi" in "(display: grdi)", expected <supports-in-parens>` | A typo in the declaration of `supports()`. |
29
+ | `selector: unexpected end in "a >", expected <complex-selector-list>` | A selector of `global()` or of a theme is incomplete. |
30
+ | `cv() base must be a css.* value (cn(...), a flex() chain or one property), got "padding: 1rem"` | CSS strings are gone everywhere: write `cn(p(4), fontSize("1rem"))`. |
31
+
32
+ A type error with no build error usually means the value is valid CSS that the types read too strictly (or the
33
+ reverse); trust the build's message and, with the MCP server, `check_declaration`.
34
+
35
+ ## Chains
36
+
37
+ | Message | Fix |
38
+ | -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
39
+ | `flex() has no .padding(); it chains direction, wrap, flow, alignItems, …` | A chain has only its container properties. Merge the rest: `cn(flex().gap(2), p(4))`. |
40
+ | `plain styles do not chain (.gap); merge them with cn(...)` | `flex("1")` (with an argument) is the shorthand property, a plain style. |
41
+ | `sticky().top() is used twice` (and other members: a method is used once) | Each method of a chain once. Under a condition, a second chain is fine: `hover(x().y(...))`. |
42
+
43
+ ## Conflicts
44
+
45
+ | Message | Fix |
46
+ | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
47
+ | `border-top and border-color overlap without one containing the other, so the sheet cannot order them …` | In one `cn()`. Set one of them, or the longhands (`borderTopColor`). Same for a physical and a logical side (`marginLeft`, `marginInline`). |
48
+ | `cv() base and variant active.yes: border-bottom and border-color overlap …` | The same rule between base, variants and compounds that can be active together. |
49
+ | `cv() variants a.x and b.x both set color and can be active together, so nothing says which wins` | Set the property in one variant, or in the base. |
50
+ | `layout: kernel stack-core sets display, flex-direction, gap (this style also sets display)` | A plugin member and a property its kernel sets, in one `cn()`. Use the member's method (`stack().gap(4)`), or drop the property. |
51
+ | `frame: frame() sets padding, margin, border-radius and the border: use its methods (this style also sets padding)` | The same, with the plugin's own message. |
52
+
53
+ A runtime argument of `cn()` is joined at runtime; a style in it replaces what it overrides, and `props.class` is only appended, so a conflict with it is not reported.
54
+
55
+ ## Conditions
56
+
57
+ | Message | Fix |
58
+ | -------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
59
+ | `:hover wraps _balance-prettyCore, the marker of kernel balance.prettyCore, with no declaration of its own: …` | A kernel is global CSS keyed to a class: it cannot be conditional. Put the member outside the condition; wrap one of its methods or a plain property (`hover(css.textWrap("pretty"))`). |
60
+ | `a style takes at most 4 conditions (layers _.a to _.e); this one has 5: …` | Fewer nested wrappers. Bind a combined query (`media("(width >= 40rem) and (hover: hover)")`) instead of two. |
61
+ | `global() takes media(), container() and supports() conditions, not :hover: write the state in the selector` | `global("a:hover", color("red"))`. |
62
+ | `within(media(...))` is an error | `within()` takes a pseudo-class wrapper or `attr(...)`, not a query. |
63
+
64
+ ## Tokens, themes, layers
65
+
66
+ | Message | Fix |
67
+ | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
68
+ | `contrast 2.84 is below 4.5: c.fg #999 on c.bg #fff, in the default values` | `check.contrast` failed (it names the theme when a theme fails). Change the colour, or the theme that names it. |
69
+ | warning: `padding: "var(--space-sm)" is the token space.sm: pass the token itself, …` | Pass `t.space.sm`; for a fallback, `css.var(t.space.sm, "4px")`. |
70
+ | warning: `… stays at its static default in chrome 109 …` | A derived colour the old-browser runtime cannot compute; it keeps its default there. Usually acceptable. |
71
+ | two tokens with one name, `2xs` and `_2xs` in one group, a cycle of references | Rename; a digit key `2xs` is `t.space._2xs` in code. |
72
+ | `$type "fontFamily"` (or `string`, a composite type) has no token type | Leave it out of the token file; set fonts with `fontFamily(...)` or `@cssints/fonts`. |
73
+ | an undeclared layer in `{ layer: "app" }`, the same layers in another order | Call `layers("app", ...)` first, in this module or one it imports; keep one order everywhere. |
74
+
75
+ ## Support warnings
76
+
77
+ `cursor: "pointer" … is not supported by ios_saf … (css.properties.cursor)`: the value is valid, a browserslist target
78
+ lacks it. It is a warning, not an error. Options: accept it, add a fallback (`firstThatWorks(wanted, fallback)`), or
79
+ narrow the browserslist. The BCD key is in parentheses; `check_support` of the MCP server answers the same question.
80
+ Vite shows the warning at the `css.*` call that made it (file, line, column, a code frame). A build says it once per
81
+ file; a dev server again when that file is edited, not on every update. The targets are the browserslist of Vite's
82
+ `root`.
83
+
84
+ ## When an error has `(trace <id>)`
85
+
86
+ The build ran with `CSSINTS_TRACE=otlp`; search for the id in the trace backend (Jaeger) to see the hook and the file.
@@ -0,0 +1,91 @@
1
+ # The plugin packages
2
+
3
+ Each package is written on `scope()` of `cssints/plugin` and has a README with its rules and limits
4
+ (`node_modules/@cssints/<name>/README.md`). Import them with `with { type: "cssints" }`, except `@cssints/tokens`
5
+ and `@cssints/ramp`, which are plain data. What the others share:
6
+
7
+ - A chain member (`stack()`, `motion()`, `skeleton()`) is a kernel class plus one class per method; each method sets a
8
+ custom property, so a method works under a condition: `media("(width >= 40rem)")(stack().gap(8))`.
9
+ - A member conflicts with what its kernel sets, in one `cn()`: use its methods, not the plain property.
10
+ - A member that is only a kernel class (`balance()`, `pretty()`, `skeleton()` with no method) cannot be inside a
11
+ condition. Put it outside; put a method inside.
12
+ - Fallbacks for older browsers (CSS under `@supports not`, or a small script) are in the build only when a browserslist
13
+ target needs them, and are delivered by the engine: nothing to import by hand.
14
+ - Files read at build time (icons, fonts, placeholder images) are not watched in dev: restart, or touch the module.
15
+
16
+ ## Layout and type
17
+
18
+ **`@cssints/layout`**: `stack().gap(6)`, `cluster().gap(2).justify("space-between").align("center")`,
19
+ `sidebar().side("right").width("20rem").content("50%").gap(4)`, `switcher().threshold("30rem").gap(2)`,
20
+ `cover().minHeight("100vh")`, `autoGrid().min("16rem").gap(4)`. No media queries: they respond to their space.
21
+ `cn(stack(), display("grid"))` is a conflict; padding, sizes and colours are fine.
22
+
23
+ **`@cssints/masonry`**: `masonry().columns("12rem").gap(4)`. `display: grid-lanes` where the browser has it (Safari
24
+ 26.4), CSS columns elsewhere (the order then runs down each column).
25
+
26
+ **`@cssints/fluid`**: `fontSize(fluid("1rem", "1.5rem", { from: "20rem", to: "80rem" }))`, a `clamp()` computed at
27
+ build time; `fluid.container(...)` against the container's width. px or rem only. A range that fails WCAG 1.4.4
28
+ (text resize) is a build error; `{ zoom: false }` for lengths that are not text.
29
+
30
+ **`@cssints/fonts`**: in a module of its own, `export const body = font("Inter", { provider: "fontsource", weights:
31
+ [400, 700] })` (or `provider: "fs", files: [...]`); then `body.family()` (the `@font-face` rules, a metric-matched
32
+ fallback face, `font-family`) and `body.trim({ lineHeight: 1.2 })` (cap-height trim: `text-box`, else capsize margins).
33
+
34
+ **`@cssints/balance`**: `balance()` (`text-wrap: balance`, with a 0.55 kB script for targets without it) and
35
+ `pretty()` (native only). Under a condition write `textWrap("balance")` instead.
36
+
37
+ ## Base and tokens
38
+
39
+ **`@cssints/reset`**: `reset({ media: true, prose: true })` once, at the top level of a module the entry loads. In
40
+ `@layer reset` before the engine's layers: any style beats it. A layer of yours between:
41
+ `layers("reset", "base", { before: true })`.
42
+
43
+ **`@cssints/tokens`**: data, not a call. `createTokens({ radix: light })`, themes from `dark`;
44
+ `createTokens({ ...colors, size: sizes })` from `/open-props`. Pass only the scales you use (type cost).
45
+
46
+ **`@cssints/ramp`**: `ramp("#2563eb", { mode: "contrast", ratios: [1.2, 1.5, 3, 4.5, 7], background: "#fff" })` or
47
+ `{ mode: "lightness", steps: 9 }`, as a group of `createTokens`; `overrides(ramp(...))` for a theme. A contrast step
48
+ meets its ratio on that background only: make a second ramp for a dark background. Imported without the attribute
49
+ (plain data).
50
+
51
+ **`@cssints/mise-en-mode`**: `look(t, "dark", "app" | "author" | "viewer", overrides, { scope? })`: a theme on
52
+ `[data-mode="dark"]` (and `data-mode="dark/color"` for one aspect, a top-level token group) in the layers
53
+ `app < author < viewer`. Checked like `createGlobalTheme`, read by `check.contrast`.
54
+
55
+ **`@cssints/icons`**: in a module of its own, `export const mdi = icons<typeof import("@iconify-json/mdi/icons.json")>({
56
+ provider: "iconify-json", collection: "mdi" })` or `icons({ provider: "fs", dir: "src/icons" })`; then
57
+ `cn(mdi("close"), color("crimson"), width("1.5em"))`. One-colour icons take `color`; a wrong name is a build error with
58
+ a suggestion.
59
+
60
+ ## Paint
61
+
62
+ **`@cssints/gradient`**: `backgroundImage(gradient("linear", "to bottom", "#000", "transparent", "ease-in-out", 5))`:
63
+ eased stops mixed in oklab. Each argument its own string; the direction is one string (`"to bottom"`).
64
+
65
+ **`@cssints/placeholder`**: `backgroundImage(placeholder("src/hero.jpg"))` (a gradient of the image's colours) or
66
+ `{ mode: "thumbhash" }` (a tiny PNG; pair it with `backgroundSize("cover")`). JPEG and PNG only; the path is relative
67
+ to the working directory.
68
+
69
+ **`@cssints/squircle`**: `squircle().radius("24px")`: `corner-shape: squircle`, else a mask of the same shape (which
70
+ clips shadows and outlines: put those on a wrapper). Set the radius with `radius()`, not `borderRadius`.
71
+
72
+ **`@cssints/skeleton`**: `cn(skeleton().radius("50%"), width("3rem"), height("3rem"))`: one clock on `:root`, so every
73
+ skeleton shimmers in phase; still under reduced motion. Do not set `background`, `background-image`, `-size`,
74
+ `-position`, `-attachment` or `border-radius` beside it (`backgroundColor` is fine and shows through).
75
+
76
+ **`cssints/frame`** (`frame().radius("1.5rem").padding(4).border("1px")`: a nested frame's radius is concentric) and
77
+ **`cssints/surface`** (`surface().color(canvas)`, `.lighten(0.06)`, `.darken()`, `.contrast()`, `.ink()`: a colour
78
+ relative to the parent surface). Both ship in `cssints`; a script fallback for browsers without style queries.
79
+
80
+ ## Motion and scroll
81
+
82
+ **`@cssints/motion`**: `motion().preset("slide-in-up").duration("500ms").delay("100ms").easing("linear")`. Presets
83
+ `fade-*`, `slide-*-up|down|left|right`, `scale-*`, `blur-*` (in and out). Reduced motion turns movement into a fade.
84
+ No `animation*` property beside it.
85
+
86
+ **`@cssints/reveal`**: `reveal().preset("blur-in").range("entry 0% entry 100%")`: motion's entrance presets on
87
+ `animation-timeline: view()`; where a target lacks it, a 0.3 kB script plays the animation once on entry.
88
+
89
+ **`@cssints/stuck`**: `sticky().top("0")` on the sticky element, `stuck(color("red"))` or `stuck("bottom")(...)` on an
90
+ element inside it (a scroll-state query matches descendants only). An IntersectionObserver fallback where the targets
91
+ lack scroll-state queries.
@@ -0,0 +1,91 @@
1
+ # Tokens, themes and contrast
2
+
3
+ ## Single tokens
4
+
5
+ ```ts
6
+ import * as css from "cssints" with { type: "cssints" };
7
+
8
+ const accent = css.token.color("#2563eb", "accent"); // --accent, can be set
9
+ const gap = css.token.length("16px", { var: "gap", inherit: false });
10
+ const muted = css.token.color("#6b7280"); // --_<hash>, private: token.set() rejects it
11
+
12
+ export const a = css.cn(css.color(accent), css.gap(gap));
13
+ export const b = css.style(css.token.set(accent, "red")); // { "--accent": "red" } for a style prop
14
+ ```
15
+
16
+ Types: `color`, `length`, `percentage`, `lengthPercentage`, `number`, `integer`, `angle`, `time`, `customIdent`,
17
+ `resolution`, `url`, `image`, `transformList`, `easingFunction`, `shadow`. Each gives one `@property` rule. A value in
18
+ `rem`/`em`/`ch` gets a px initial value and the real value on `:root`, so it follows the user's font size.
19
+
20
+ ## Token sets (DTCG)
21
+
22
+ ```ts
23
+ export const t = css.createTokens({
24
+ color: {
25
+ $type: "color",
26
+ accent: { $value: "#2563eb" },
27
+ hover: { $value: "color-mix(in oklab, {color.accent}, black 15%)" }, // derived: follows accent in themes
28
+ link: { $value: "{color.accent}" }, // alias
29
+ },
30
+ space: {
31
+ $type: "dimension",
32
+ "2xs": { $value: "2px" },
33
+ md: { $value: { value: 1, unit: "rem" } },
34
+ },
35
+ });
36
+ // t.color.accent, t.space._2xs (a key that starts with a digit gets "_"), variable --space-2xs
37
+ ```
38
+
39
+ - `$type` is inherited through groups and required: `color`, `dimension`, `number`, `duration`, `fontWeight`,
40
+ `cubicBezier`, `shadow`. `fontFamily`, `string`, `boolean` and the other composite types are errors.
41
+ - Names are the kebab-case path (`--color-accent`). The second argument maps a path to a name or `{ var, inherit }`.
42
+ - JSON works: `import tokens from "./tokens.json" with { type: "json" }; createTokens(tokens)`. The editor then knows
43
+ the paths but not the types; the build checks the values.
44
+ - Figma's own export (one DTCG file per mode) and TokensBrücke ("Split modes into separate files"): the default mode
45
+ is `createTokens(json)`, another mode is `createGlobalTheme(t, dark, darkJson)` as it is. Tokens Studio's format goes
46
+ through Style Dictionary with `@tokens-studio/sd-transforms` first (recipe in docs/API.md, "JSON from Figma").
47
+ - Ready-made sets: `@cssints/tokens/open-props`, `/radix`, `/flexoki`, `/reasonable-colors` (data: put a set under a
48
+ group, `createTokens({ radix: light })` gives `--radix-blue-11`). Palettes: `@cssints/ramp`.
49
+
50
+ ## Themes
51
+
52
+ ```ts
53
+ const dark = css.media("(prefers-color-scheme: dark)");
54
+ css.createGlobalTheme(t, dark, { color: { accent: "#60a5fa" } }); // :root under the media query
55
+ css.createGlobalTheme(t, ".brand", { color: { accent: "#db2777" } }); // a selector; hover and link follow
56
+ css.createGlobalTheme(t.color, css.container("(width < 30rem)"), { accent: "#000" }); // a group, a container query
57
+ css.createGlobalTheme(t, ".soft", {
58
+ color: { link: "color-mix(in oklab, {color.accent}, white 20%)" },
59
+ }); // live
60
+ ```
61
+
62
+ - The condition is a `media()`, a `container()` or a selector. Not `hover` or `supports()`.
63
+ - Options: `{ layer: "app" }` (a layer declared with `layers()`), `{ scope: "article.post" }` (an `@scope` around a
64
+ selector theme).
65
+ - A theme re-declares the aliases and derived tokens that depend on what it overrides.
66
+ - Put themes in a module of their own and import it once from the entry (`import "./theme.ts"`); a module that uses a
67
+ token gets the theme rules for that token.
68
+ - Sheet order: tokens and themes are in `_.k`, below every class; a selector theme beats a condition theme.
69
+
70
+ ## Contrast
71
+
72
+ ```ts
73
+ css.check.contrast(t.color.fg, t.color.bg, { min: 4.5 }); // WCAG 2.x, 1 to 21
74
+ ```
75
+
76
+ - Checks the defaults and every theme that sets either token (themes of one selector are read together in cascade
77
+ order). A ratio below `min` is a build error with both colours and the theme.
78
+ - A value it cannot evaluate (`color-mix()` over another token, `currentColor`, `light-dark()`, a translucent colour)
79
+ is a warning, not a pass.
80
+ - A value set at runtime (a slider on `<html>`) is checked at its default only.
81
+ - `surface().ink()` picks black or white by lightness and is not a contrast guarantee: check the pair with tokens.
82
+
83
+ ## Top-level layers
84
+
85
+ ```ts
86
+ css.layers("app", "author", "viewer"); // after the engine's layers: they beat the engine's :root fallbacks
87
+ css.layers("reset", "base", { before: true }); // before the engine's: every cssints style beats them
88
+ ```
89
+
90
+ The same names in another order, or a layer on both sides, is a build error. `@cssints/reset` uses `reset` before the
91
+ engine's layers; `@cssints/mise-en-mode` uses `app < author < viewer`.