@elastic/distillate 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (285) hide show
  1. package/LICENSE.txt +93 -0
  2. package/NOTICE.txt +30 -0
  3. package/README.md +142 -0
  4. package/THIRD_PARTY_LICENSES.md +768 -0
  5. package/dist/cjs/collector.js +420 -0
  6. package/dist/cjs/collector.js.map +1 -0
  7. package/dist/cjs/css_scan.js +186 -0
  8. package/dist/cjs/css_scan.js.map +1 -0
  9. package/dist/cjs/dom_sink.js +40 -0
  10. package/dist/cjs/dom_sink.js.map +1 -0
  11. package/dist/cjs/emotion.js +189 -0
  12. package/dist/cjs/emotion.js.map +1 -0
  13. package/dist/cjs/engine.js +79 -0
  14. package/dist/cjs/engine.js.map +1 -0
  15. package/dist/cjs/environment.js +9 -0
  16. package/dist/cjs/environment.js.map +1 -0
  17. package/dist/cjs/hash.js +86 -0
  18. package/dist/cjs/hash.js.map +1 -0
  19. package/dist/cjs/idents.js +23 -0
  20. package/dist/cjs/idents.js.map +1 -0
  21. package/dist/cjs/index.js +42 -0
  22. package/dist/cjs/index.js.map +1 -0
  23. package/dist/cjs/instance.js +34 -0
  24. package/dist/cjs/instance.js.map +1 -0
  25. package/dist/cjs/local_vars.js +100 -0
  26. package/dist/cjs/local_vars.js.map +1 -0
  27. package/dist/cjs/minify.js +77 -0
  28. package/dist/cjs/minify.js.map +1 -0
  29. package/dist/cjs/names.js +77 -0
  30. package/dist/cjs/names.js.map +1 -0
  31. package/dist/cjs/nesting.js +406 -0
  32. package/dist/cjs/nesting.js.map +1 -0
  33. package/dist/cjs/package.json +3 -0
  34. package/dist/cjs/runtime.js +257 -0
  35. package/dist/cjs/runtime.js.map +1 -0
  36. package/dist/cjs/styles/authoring.js +169 -0
  37. package/dist/cjs/styles/authoring.js.map +1 -0
  38. package/dist/cjs/styles/declarations.js +139 -0
  39. package/dist/cjs/styles/declarations.js.map +1 -0
  40. package/dist/cjs/styles/index.js +32 -0
  41. package/dist/cjs/styles/index.js.map +1 -0
  42. package/dist/cjs/styles/module.js +233 -0
  43. package/dist/cjs/styles/module.js.map +1 -0
  44. package/dist/cjs/styles/registry.js +187 -0
  45. package/dist/cjs/styles/registry.js.map +1 -0
  46. package/dist/cjs/styles/types.js +9 -0
  47. package/dist/cjs/styles/types.js.map +1 -0
  48. package/dist/cjs/testing.js +37 -0
  49. package/dist/cjs/testing.js.map +1 -0
  50. package/dist/cjs/theme.js +249 -0
  51. package/dist/cjs/theme.js.map +1 -0
  52. package/dist/cjs/tokens/contextual_var.js +39 -0
  53. package/dist/cjs/tokens/contextual_var.js.map +1 -0
  54. package/dist/cjs/tokens/index.js +24 -0
  55. package/dist/cjs/tokens/index.js.map +1 -0
  56. package/dist/cjs/tokens/kind.js +14 -0
  57. package/dist/cjs/tokens/kind.js.map +1 -0
  58. package/dist/cjs/tokens/light_dark.js +42 -0
  59. package/dist/cjs/tokens/light_dark.js.map +1 -0
  60. package/dist/cjs/tokens/scale_token.js +28 -0
  61. package/dist/cjs/tokens/scale_token.js.map +1 -0
  62. package/dist/cjs/tokens/theme_token.js +28 -0
  63. package/dist/cjs/tokens/theme_token.js.map +1 -0
  64. package/dist/cjs/var_invariant.js +115 -0
  65. package/dist/cjs/var_invariant.js.map +1 -0
  66. package/dist/collector.d.ts +84 -0
  67. package/dist/collector.d.ts.map +1 -0
  68. package/dist/collector.js +416 -0
  69. package/dist/collector.js.map +1 -0
  70. package/dist/css_scan.d.ts +57 -0
  71. package/dist/css_scan.d.ts.map +1 -0
  72. package/dist/css_scan.js +180 -0
  73. package/dist/css_scan.js.map +1 -0
  74. package/dist/dom_sink.d.ts +27 -0
  75. package/dist/dom_sink.d.ts.map +1 -0
  76. package/dist/dom_sink.js +36 -0
  77. package/dist/dom_sink.js.map +1 -0
  78. package/dist/emotion.d.ts +47 -0
  79. package/dist/emotion.d.ts.map +1 -0
  80. package/dist/emotion.js +184 -0
  81. package/dist/emotion.js.map +1 -0
  82. package/dist/engine.d.ts +41 -0
  83. package/dist/engine.d.ts.map +1 -0
  84. package/dist/engine.js +75 -0
  85. package/dist/engine.js.map +1 -0
  86. package/dist/environment.d.ts +43 -0
  87. package/dist/environment.d.ts.map +1 -0
  88. package/dist/environment.js +8 -0
  89. package/dist/environment.js.map +1 -0
  90. package/dist/hash.d.ts +2 -0
  91. package/dist/hash.d.ts.map +1 -0
  92. package/dist/hash.js +82 -0
  93. package/dist/hash.js.map +1 -0
  94. package/dist/idents.d.ts +9 -0
  95. package/dist/idents.d.ts.map +1 -0
  96. package/dist/idents.js +19 -0
  97. package/dist/idents.js.map +1 -0
  98. package/dist/index.d.ts +9 -0
  99. package/dist/index.d.ts.map +1 -0
  100. package/dist/index.js +14 -0
  101. package/dist/index.js.map +1 -0
  102. package/dist/instance.d.ts +16 -0
  103. package/dist/instance.d.ts.map +1 -0
  104. package/dist/instance.js +30 -0
  105. package/dist/instance.js.map +1 -0
  106. package/dist/local_vars.d.ts +75 -0
  107. package/dist/local_vars.d.ts.map +1 -0
  108. package/dist/local_vars.js +92 -0
  109. package/dist/local_vars.js.map +1 -0
  110. package/dist/minify.d.ts +11 -0
  111. package/dist/minify.d.ts.map +1 -0
  112. package/dist/minify.js +73 -0
  113. package/dist/minify.js.map +1 -0
  114. package/dist/names.d.ts +39 -0
  115. package/dist/names.d.ts.map +1 -0
  116. package/dist/names.js +70 -0
  117. package/dist/names.js.map +1 -0
  118. package/dist/nesting.d.ts +59 -0
  119. package/dist/nesting.d.ts.map +1 -0
  120. package/dist/nesting.js +400 -0
  121. package/dist/nesting.js.map +1 -0
  122. package/dist/runtime.d.ts +36 -0
  123. package/dist/runtime.d.ts.map +1 -0
  124. package/dist/runtime.js +252 -0
  125. package/dist/runtime.js.map +1 -0
  126. package/dist/styles/authoring.d.ts +59 -0
  127. package/dist/styles/authoring.d.ts.map +1 -0
  128. package/dist/styles/authoring.js +155 -0
  129. package/dist/styles/authoring.js.map +1 -0
  130. package/dist/styles/declarations.d.ts +10 -0
  131. package/dist/styles/declarations.d.ts.map +1 -0
  132. package/dist/styles/declarations.js +132 -0
  133. package/dist/styles/declarations.js.map +1 -0
  134. package/dist/styles/index.d.ts +7 -0
  135. package/dist/styles/index.d.ts.map +1 -0
  136. package/dist/styles/index.js +12 -0
  137. package/dist/styles/index.js.map +1 -0
  138. package/dist/styles/module.d.ts +5 -0
  139. package/dist/styles/module.d.ts.map +1 -0
  140. package/dist/styles/module.js +229 -0
  141. package/dist/styles/module.js.map +1 -0
  142. package/dist/styles/registry.d.ts +48 -0
  143. package/dist/styles/registry.d.ts.map +1 -0
  144. package/dist/styles/registry.js +182 -0
  145. package/dist/styles/registry.js.map +1 -0
  146. package/dist/styles/types.d.ts +211 -0
  147. package/dist/styles/types.d.ts.map +1 -0
  148. package/dist/styles/types.js +8 -0
  149. package/dist/styles/types.js.map +1 -0
  150. package/dist/testing.d.ts +10 -0
  151. package/dist/testing.d.ts.map +1 -0
  152. package/dist/testing.js +31 -0
  153. package/dist/testing.js.map +1 -0
  154. package/dist/theme.d.ts +85 -0
  155. package/dist/theme.d.ts.map +1 -0
  156. package/dist/theme.js +240 -0
  157. package/dist/theme.js.map +1 -0
  158. package/dist/tokens/contextual_var.d.ts +35 -0
  159. package/dist/tokens/contextual_var.d.ts.map +1 -0
  160. package/dist/tokens/contextual_var.js +33 -0
  161. package/dist/tokens/contextual_var.js.map +1 -0
  162. package/dist/tokens/index.d.ts +5 -0
  163. package/dist/tokens/index.d.ts.map +1 -0
  164. package/dist/tokens/index.js +11 -0
  165. package/dist/tokens/index.js.map +1 -0
  166. package/dist/tokens/kind.d.ts +2 -0
  167. package/dist/tokens/kind.d.ts.map +1 -0
  168. package/dist/tokens/kind.js +10 -0
  169. package/dist/tokens/kind.js.map +1 -0
  170. package/dist/tokens/light_dark.d.ts +20 -0
  171. package/dist/tokens/light_dark.d.ts.map +1 -0
  172. package/dist/tokens/light_dark.js +37 -0
  173. package/dist/tokens/light_dark.js.map +1 -0
  174. package/dist/tokens/scale_token.d.ts +20 -0
  175. package/dist/tokens/scale_token.d.ts.map +1 -0
  176. package/dist/tokens/scale_token.js +23 -0
  177. package/dist/tokens/scale_token.js.map +1 -0
  178. package/dist/tokens/theme_token.d.ts +20 -0
  179. package/dist/tokens/theme_token.d.ts.map +1 -0
  180. package/dist/tokens/theme_token.js +23 -0
  181. package/dist/tokens/theme_token.js.map +1 -0
  182. package/dist/var_invariant.d.ts +25 -0
  183. package/dist/var_invariant.d.ts.map +1 -0
  184. package/dist/var_invariant.js +108 -0
  185. package/dist/var_invariant.js.map +1 -0
  186. package/docs/concepts/authoring.md +115 -0
  187. package/docs/concepts/collection.md +66 -0
  188. package/docs/concepts/distillery.md +47 -0
  189. package/docs/concepts/index.md +18 -0
  190. package/docs/concepts/naming-and-output.md +123 -0
  191. package/docs/concepts/ordering.md +61 -0
  192. package/docs/concepts/single-copy.md +103 -0
  193. package/docs/concepts/tokens.md +99 -0
  194. package/docs/docset.yml +53 -0
  195. package/docs/examples/01-stylesheet.ts +20 -0
  196. package/docs/examples/02-artifact.ts +26 -0
  197. package/docs/examples/03-variants.ts +40 -0
  198. package/docs/examples/04-emotion.ts +33 -0
  199. package/docs/examples/05-var-invariant.ts +31 -0
  200. package/docs/examples/06-payload.ts +85 -0
  201. package/docs/examples/fixture.ts +22 -0
  202. package/docs/getting-started/index.md +14 -0
  203. package/docs/getting-started/installation.md +30 -0
  204. package/docs/getting-started/quick-start.md +61 -0
  205. package/docs/getting-started/your-first-stylesheet.md +57 -0
  206. package/docs/guides/compact-artifacts.md +87 -0
  207. package/docs/guides/emotion-migration.md +60 -0
  208. package/docs/guides/index.md +17 -0
  209. package/docs/guides/name-collisions.md +37 -0
  210. package/docs/guides/non-css-surfaces.md +73 -0
  211. package/docs/guides/react-renderer.md +158 -0
  212. package/docs/guides/theming.md +107 -0
  213. package/docs/guides/var-invariant.md +24 -0
  214. package/docs/guides/vs-emotion.md +61 -0
  215. package/docs/index.md +73 -0
  216. package/docs/logo.svg +7 -0
  217. package/docs/playground/index.html +12 -0
  218. package/docs/playground/package.json +23 -0
  219. package/docs/playground/src/app/app.tsx +109 -0
  220. package/docs/playground/src/app/css_panel.tsx +119 -0
  221. package/docs/playground/src/app/editor_panel.tsx +87 -0
  222. package/docs/playground/src/app/index.ts +8 -0
  223. package/docs/playground/src/app/monaco_css_editor.tsx +84 -0
  224. package/docs/playground/src/app/monaco_source_editor.tsx +132 -0
  225. package/docs/playground/src/app/preview_panel.tsx +99 -0
  226. package/docs/playground/src/app/tokens_panel.tsx +117 -0
  227. package/docs/playground/src/app/types.ts +10 -0
  228. package/docs/playground/src/examples/snippets.test.ts +28 -0
  229. package/docs/playground/src/examples/snippets.ts +418 -0
  230. package/docs/playground/src/lib/compile.test.ts +157 -0
  231. package/docs/playground/src/lib/compile.ts +242 -0
  232. package/docs/playground/src/lib/demo_environment.test.ts +123 -0
  233. package/docs/playground/src/lib/demo_environment.ts +140 -0
  234. package/docs/playground/src/lib/editor_lib.test.ts +39 -0
  235. package/docs/playground/src/lib/editor_lib.ts +142 -0
  236. package/docs/playground/src/lib/format_css.test.ts +41 -0
  237. package/docs/playground/src/lib/format_css.ts +71 -0
  238. package/docs/playground/src/main.tsx +25 -0
  239. package/docs/playground/src/setup/chrome.test.ts +47 -0
  240. package/docs/playground/src/setup/chrome.ts +365 -0
  241. package/docs/playground/src/setup/monaco_env.ts +64 -0
  242. package/docs/playground/tsconfig.json +22 -0
  243. package/docs/playground/vite.config.ts +37 -0
  244. package/docs/playground/vitest.config.ts +35 -0
  245. package/docs/playground.md +28 -0
  246. package/docs/reference/api/emotion.md +372 -0
  247. package/docs/reference/api/index-1.md +2180 -0
  248. package/docs/reference/api/index.md +9 -0
  249. package/docs/reference/api/testing.md +92 -0
  250. package/docs/reference/api.md +12 -0
  251. package/docs/reference/entry-points.md +125 -0
  252. package/docs/reference/environment.md +60 -0
  253. package/docs/reference/index.md +11 -0
  254. package/docs/reference/okf-map.md +92 -0
  255. package/package.json +127 -4
  256. package/src/collector.ts +526 -0
  257. package/src/css_scan.ts +263 -0
  258. package/src/dom_sink.ts +65 -0
  259. package/src/emotion.ts +311 -0
  260. package/src/engine.ts +191 -0
  261. package/src/environment.ts +56 -0
  262. package/src/hash.ts +111 -0
  263. package/src/idents.ts +21 -0
  264. package/src/index.ts +93 -0
  265. package/src/instance.ts +49 -0
  266. package/src/local_vars.ts +220 -0
  267. package/src/minify.ts +87 -0
  268. package/src/names.ts +129 -0
  269. package/src/nesting.ts +596 -0
  270. package/src/runtime.ts +428 -0
  271. package/src/styles/authoring.ts +244 -0
  272. package/src/styles/declarations.ts +172 -0
  273. package/src/styles/index.ts +58 -0
  274. package/src/styles/module.ts +396 -0
  275. package/src/styles/registry.ts +261 -0
  276. package/src/styles/types.ts +283 -0
  277. package/src/testing.ts +48 -0
  278. package/src/theme.ts +454 -0
  279. package/src/tokens/contextual_var.ts +72 -0
  280. package/src/tokens/index.ts +17 -0
  281. package/src/tokens/kind.ts +13 -0
  282. package/src/tokens/light_dark.ts +58 -0
  283. package/src/tokens/scale_token.ts +41 -0
  284. package/src/tokens/theme_token.ts +42 -0
  285. package/src/var_invariant.ts +147 -0
@@ -0,0 +1,25 @@
1
+ /** A `var(...)` whose custom property is never declared in the same CSS text. */
2
+ export interface VarInvariantViolation {
3
+ /** The undeclared custom property name (e.g. `--aui-color-bg`). */
4
+ readonly reference: string;
5
+ /** Surrounding CSS text for error reporting. */
6
+ readonly context: string;
7
+ }
8
+ /** Collects every custom property declared in a declaration position in `css`. */
9
+ export declare const collectDeclaredCustomProperties: (css: string) => Set<string>;
10
+ /** Collects all custom property names referenced inside `var(...)` calls in `css`. */
11
+ export declare const collectReferencedCustomProperties: (css: string) => readonly string[];
12
+ /**
13
+ * Returns all `var(...)` references whose custom property is never declared in `css`.
14
+ *
15
+ * @param css Full CSS text to scan.
16
+ */
17
+ export declare const findVarRefViolations: (css: string) => readonly VarInvariantViolation[];
18
+ /**
19
+ * Throws if any `var(...)` in `css` has no matching custom-property declaration.
20
+ *
21
+ * @param css Full CSS text to scan.
22
+ * @throws If any reference has no declaration in the same text.
23
+ */
24
+ export declare const assertVarRefsHaveDeclarations: (css: string) => void;
25
+ //# sourceMappingURL=var_invariant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"var_invariant.d.ts","sourceRoot":"","sources":["../src/var_invariant.ts"],"names":[],"mappings":"AAYA,iFAAiF;AACjF,MAAM,WAAW,qBAAqB;IACpC,mEAAmE;IACnE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,gDAAgD;IAChD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAyDD,kFAAkF;AAClF,eAAO,MAAM,+BAA+B,GAAI,KAAK,MAAM,KAAG,GAAG,CAAC,MAAM,CAUvE,CAAC;AAEF,sFAAsF;AACtF,eAAO,MAAM,iCAAiC,GAC5C,KAAK,MAAM,KACV,SAAS,MAAM,EAUjB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAC/B,KAAK,MAAM,KACV,SAAS,qBAAqB,EAgBhC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,GAAI,KAAK,MAAM,KAAG,IAY3D,CAAC"}
@@ -0,0 +1,108 @@
1
+ /*
2
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3
+ * or more contributor license agreements. Licensed under the Elastic License
4
+ * 2.0; you may not use this file except in compliance with the Elastic License
5
+ * 2.0.
6
+ */
7
+ import { isOpaque, scanCss } from './css_scan.js';
8
+ const DECLARATION_RE = /(--[A-Za-z_][\w-]*)\s*:/g;
9
+ const REFERENCE_RE = /var\(\s*(--[A-Za-z_][\w-]*)/g;
10
+ const scan = (css) => {
11
+ const masked = [];
12
+ const blockAt = [];
13
+ for (const span of scanCss(css)) {
14
+ const blank = isOpaque(span);
15
+ for (let offset = 0; offset < span.text.length; offset += 1) {
16
+ masked.push(blank ? ' ' : (span.text[offset] ?? ''));
17
+ blockAt.push(span.block);
18
+ }
19
+ }
20
+ return { masked: masked.join(''), blockAt };
21
+ };
22
+ /**
23
+ * A custom property is declared where a declaration can start: inside a
24
+ * declarations block, as the first thing after `{` or `;`. A selector carrying
25
+ * a BEM-style modifier and a pseudo-class (`.button--active:hover`) matches the
26
+ * same characters and declares nothing.
27
+ */
28
+ const isDeclarationStart = ({ masked, blockAt }, index) => {
29
+ if (blockAt[index] !== 'declarations') {
30
+ return false;
31
+ }
32
+ for (let cursor = index - 1; cursor >= 0; cursor -= 1) {
33
+ const char = masked[cursor];
34
+ if (char === undefined || !/\s/.test(char)) {
35
+ return char === '{' || char === ';';
36
+ }
37
+ }
38
+ return false;
39
+ };
40
+ /**
41
+ * `var` has to open a function token rather than end a longer identifier:
42
+ * `--expr:myvar(--x)` names some other function.
43
+ */
44
+ const isFunctionTokenStart = (masked, index) => {
45
+ const before = masked[index - 1];
46
+ return before === undefined || !/[\w\\-]/.test(before);
47
+ };
48
+ /** Collects every custom property declared in a declaration position in `css`. */
49
+ export const collectDeclaredCustomProperties = (css) => {
50
+ const scanned = scan(css);
51
+ const declared = new Set();
52
+ for (const match of scanned.masked.matchAll(DECLARATION_RE)) {
53
+ const name = match[1];
54
+ if (name && isDeclarationStart(scanned, match.index ?? 0)) {
55
+ declared.add(name);
56
+ }
57
+ }
58
+ return declared;
59
+ };
60
+ /** Collects all custom property names referenced inside `var(...)` calls in `css`. */
61
+ export const collectReferencedCustomProperties = (css) => {
62
+ const { masked } = scan(css);
63
+ const names = [];
64
+ for (const match of masked.matchAll(REFERENCE_RE)) {
65
+ const name = match[1];
66
+ if (name && isFunctionTokenStart(masked, match.index ?? 0)) {
67
+ names.push(name);
68
+ }
69
+ }
70
+ return names;
71
+ };
72
+ /**
73
+ * Returns all `var(...)` references whose custom property is never declared in `css`.
74
+ *
75
+ * @param css Full CSS text to scan.
76
+ */
77
+ export const findVarRefViolations = (css) => {
78
+ const { masked } = scan(css);
79
+ const declared = collectDeclaredCustomProperties(css);
80
+ const violations = [];
81
+ for (const match of masked.matchAll(REFERENCE_RE)) {
82
+ const ref = match[1];
83
+ const index = match.index ?? 0;
84
+ if (!ref || declared.has(ref) || !isFunctionTokenStart(masked, index)) {
85
+ continue;
86
+ }
87
+ violations.push({
88
+ reference: ref,
89
+ context: css.slice(Math.max(0, index - 40), index + ref.length + 8),
90
+ });
91
+ }
92
+ return violations;
93
+ };
94
+ /**
95
+ * Throws if any `var(...)` in `css` has no matching custom-property declaration.
96
+ *
97
+ * @param css Full CSS text to scan.
98
+ * @throws If any reference has no declaration in the same text.
99
+ */
100
+ export const assertVarRefsHaveDeclarations = (css) => {
101
+ const violations = findVarRefViolations(css);
102
+ if (violations.length === 0) {
103
+ return;
104
+ }
105
+ const lines = violations.map(({ reference, context }) => `- ${reference} referenced near "${context.replace(/\s+/g, ' ')}"`);
106
+ throw new Error(`${violations.length} var(...) reference(s) without a matching declaration:\n${lines.join('\n')}`);
107
+ };
108
+ //# sourceMappingURL=var_invariant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"var_invariant.js","sourceRoot":"","sources":["../src/var_invariant.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one\n * or more contributor license agreements. Licensed under the Elastic License\n * 2.0; you may not use this file except in compliance with the Elastic License\n * 2.0.\n */\n\nimport { type CssBlockKind, isOpaque, scanCss } from './css_scan';\n\nconst DECLARATION_RE = /(--[A-Za-z_][\\w-]*)\\s*:/g;\nconst REFERENCE_RE = /var\\(\\s*(--[A-Za-z_][\\w-]*)/g;\n\n/** A `var(...)` whose custom property is never declared in the same CSS text. */\nexport interface VarInvariantViolation {\n /** The undeclared custom property name (e.g. `--aui-color-bg`). */\n readonly reference: string;\n /** Surrounding CSS text for error reporting. */\n readonly context: string;\n}\n\ninterface ScannedCss {\n /**\n * `css` with comments, strings, `url(...)` tokens, and escapes blanked to\n * spaces, positions preserved. A `var(` inside a string is text, a `;`\n * inside an escape is part of a value, and neither should match here.\n */\n readonly masked: string;\n /** The enclosing block kind at each character offset. */\n readonly blockAt: readonly CssBlockKind[];\n}\n\nconst scan = (css: string): ScannedCss => {\n const masked: string[] = [];\n const blockAt: CssBlockKind[] = [];\n for (const span of scanCss(css)) {\n const blank = isOpaque(span);\n for (let offset = 0; offset < span.text.length; offset += 1) {\n masked.push(blank ? ' ' : (span.text[offset] ?? ''));\n blockAt.push(span.block);\n }\n }\n return { masked: masked.join(''), blockAt };\n};\n\n/**\n * A custom property is declared where a declaration can start: inside a\n * declarations block, as the first thing after `{` or `;`. A selector carrying\n * a BEM-style modifier and a pseudo-class (`.button--active:hover`) matches the\n * same characters and declares nothing.\n */\nconst isDeclarationStart = (\n { masked, blockAt }: ScannedCss,\n index: number\n): boolean => {\n if (blockAt[index] !== 'declarations') {\n return false;\n }\n for (let cursor = index - 1; cursor >= 0; cursor -= 1) {\n const char = masked[cursor];\n if (char === undefined || !/\\s/.test(char)) {\n return char === '{' || char === ';';\n }\n }\n return false;\n};\n\n/**\n * `var` has to open a function token rather than end a longer identifier:\n * `--expr:myvar(--x)` names some other function.\n */\nconst isFunctionTokenStart = (masked: string, index: number): boolean => {\n const before = masked[index - 1];\n return before === undefined || !/[\\w\\\\-]/.test(before);\n};\n\n/** Collects every custom property declared in a declaration position in `css`. */\nexport const collectDeclaredCustomProperties = (css: string): Set<string> => {\n const scanned = scan(css);\n const declared = new Set<string>();\n for (const match of scanned.masked.matchAll(DECLARATION_RE)) {\n const name = match[1];\n if (name && isDeclarationStart(scanned, match.index ?? 0)) {\n declared.add(name);\n }\n }\n return declared;\n};\n\n/** Collects all custom property names referenced inside `var(...)` calls in `css`. */\nexport const collectReferencedCustomProperties = (\n css: string\n): readonly string[] => {\n const { masked } = scan(css);\n const names: string[] = [];\n for (const match of masked.matchAll(REFERENCE_RE)) {\n const name = match[1];\n if (name && isFunctionTokenStart(masked, match.index ?? 0)) {\n names.push(name);\n }\n }\n return names;\n};\n\n/**\n * Returns all `var(...)` references whose custom property is never declared in `css`.\n *\n * @param css Full CSS text to scan.\n */\nexport const findVarRefViolations = (\n css: string\n): readonly VarInvariantViolation[] => {\n const { masked } = scan(css);\n const declared = collectDeclaredCustomProperties(css);\n const violations: VarInvariantViolation[] = [];\n for (const match of masked.matchAll(REFERENCE_RE)) {\n const ref = match[1];\n const index = match.index ?? 0;\n if (!ref || declared.has(ref) || !isFunctionTokenStart(masked, index)) {\n continue;\n }\n violations.push({\n reference: ref,\n context: css.slice(Math.max(0, index - 40), index + ref.length + 8),\n });\n }\n return violations;\n};\n\n/**\n * Throws if any `var(...)` in `css` has no matching custom-property declaration.\n *\n * @param css Full CSS text to scan.\n * @throws If any reference has no declaration in the same text.\n */\nexport const assertVarRefsHaveDeclarations = (css: string): void => {\n const violations = findVarRefViolations(css);\n if (violations.length === 0) {\n return;\n }\n const lines = violations.map(\n ({ reference, context }) =>\n `- ${reference} referenced near \"${context.replace(/\\s+/g, ' ')}\"`\n );\n throw new Error(\n `${violations.length} var(...) reference(s) without a matching declaration:\\n${lines.join('\\n')}`\n );\n};\n"]}
@@ -0,0 +1,115 @@
1
+ ---
2
+ navigation_title: Authoring
3
+ description: Style modules, handles, nested templates, rules, media, and variants.
4
+ ---
5
+
6
+ # Authoring
7
+
8
+ A style module is a named group of entries. The factory runs once; the returned handles are static. The bind matches [quick start](../getting-started/quick-start.md).
9
+
10
+ ```ts
11
+ import {
12
+ createDistillery,
13
+ cq,
14
+ lightDark,
15
+ rule,
16
+ variants,
17
+ } from '@elastic/distillate';
18
+
19
+ const distillery = createDistillery({
20
+ prefix: 'eui',
21
+ themeScope: '.eui-view',
22
+ theme: {
23
+ colors: {
24
+ ink: lightDark('#111', '#eee'),
25
+ accent: lightDark('#06c', '#8cf'),
26
+ surface: lightDark('#fff', '#000'),
27
+ },
28
+ gap: cq('8px', '2cqi'),
29
+ },
30
+ });
31
+
32
+ const callout = distillery.createStyleModule('callout', ({ css, decls, tokens }) => ({
33
+ root: css`
34
+ padding: ${tokens.gap};
35
+ color: ${tokens.colors.ink};
36
+ &:hover {
37
+ color: ${tokens.colors.accent};
38
+ }
39
+ @media (min-width: 600px) {
40
+ padding: 16px;
41
+ }
42
+ `, <1>
43
+ title: css`
44
+ font-weight: 600;
45
+ `,
46
+ tone: variants(
47
+ ['calm', 'loud'] as const,
48
+ (tone) =>
49
+ css`
50
+ outline-color: ${tone === 'calm' ? tokens.colors.surface : tokens.colors.accent};
51
+ `
52
+ ), <2>
53
+ hoverTitle: rule(
54
+ (h) => `${h.root}:hover ${h.title}`,
55
+ decls`
56
+ color: ${tokens.colors.accent};
57
+ `
58
+ ), <3>
59
+ }));
60
+ ```
61
+
62
+ 1. Nested `&:hover` and `@media` stay on this handle and collect with it.
63
+ 2. Variant keys. `collector.use(callout)` skips these until you name `tone.loud`.
64
+ 3. Combines two handles. Auto-collects only when both `root` and `title` are live.
65
+
66
+ What you get back is not a class name. A **handle** carries a `key`, a `readableName`, and the declarations the collector needs. The same handle can emit `.callout-root` or `.a` depending on the resolver.
67
+
68
+ ## Templates
69
+
70
+ | Helper | Role |
71
+ | --------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
72
+ | `css` | Handle. Nested `&` and `@media` flatten into sibling rules. Also a top-level export. |
73
+ | `decls` | Declaration block for `rule(...)`. Also a top-level export. |
74
+ | `rule(selector, decls, options?)` | Selector plus declarations. `auto` (default `true`) self-collects when every handle the selector reads is collected. |
75
+ | `media(query, rules)` | `@media` block. |
76
+ | `container(query, rules)` | `@container` block. Same collector rank as `media`. |
77
+ | `variants(keys, factory)` | One entry per key, marked `variant` so `collector.use(module)` skips it. |
78
+ | `mapDomain(keys, factory)` | Same mapping as `variants`, with no collector side effect. Use it on non-CSS surfaces. |
79
+
80
+ `@container` inside a `css` template is rejected; use the `container(...)` factory. `@supports` and `keyframes` are not supported. Object styles (`css({ color: 'red' })`) are not supported on the emotion entry.
81
+
82
+ ### When to reach for each helper
83
+
84
+ **`css` with nesting** is the default. Nested `&` and `@media` blocks inside `css` are all part of that handle — they collect together and share its class name.
85
+
86
+ **`rule`** is for selectors that combine two handles (`${h.root}:hover ${h.title}`). The key behaviour: `rule` records every handle the selector reads as a dependency, and only auto-collects when all of them are live. Use `{ auto: false }` to require an explicit `collector.use(ruleEntry)` instead of piggybacking on dependency collection.
87
+
88
+ **`media` / `container`** as explicit factories wrap multiple handles in one block and collect independently. Prefer nesting `@media` inside `css` when a block is local to one handle. Reach for `media(...)` when the block spans multiple entries or needs to be toggled separately from them. The `container(...)` factory is always required for `@container` — it cannot be nested inside `css`.
89
+
90
+ ## Static after construction
91
+
92
+ Anything that varies at runtime belongs on a CSS custom property (`vars`, theme tokens), not on a new template per render. Immutable modules are what makes dependency capture and reachability cheap.
93
+
94
+ ## Variants
95
+
96
+ `collector.use(callout)` does not collect `callout.handles.tone.loud`. Name the variant you rendered:
97
+
98
+ ```ts
99
+ const collector = distillery.artifactCollector('compact');
100
+ collector.useHandles([callout.handles.root, callout.handles.tone.loud]);
101
+ ```
102
+
103
+ `stylesheetCollector()` includes every variant because it calls `useAllEntries`.
104
+
105
+ ## Selector rules
106
+
107
+ `hoverTitle` above records `root` and `title` as dependencies. Auto-collection fires only when every dependency is already collected — a `button + other` rule stays out until both handles are in.
108
+
109
+ ```ts
110
+ rule((h) => `${h.button}:hover`, decls`color: red;`, { auto: false }); // requires collector.use(ruleEntry)
111
+ ```
112
+
113
+ ## Combining class names
114
+
115
+ `combineClassNames(context, ...handles)` forwards to `context.resolveClassName`. In a React renderer, that context is the collector-backed resolver you pass down the tree. See [Integrate with a React renderer](../guides/react-renderer.md).
@@ -0,0 +1,66 @@
1
+ ---
2
+ navigation_title: Reachability collection
3
+ description: StylesCollector, use versus useHandles, theme-var reachability, and per-handle default narrowing.
4
+ ---
5
+
6
+ # Reachability collection
7
+
8
+ A `StylesCollector` is the set of entries that will ship. Class-name resolution collects as a side effect: the renderer names a handle, the handle's rules come along, and unread variables drop out.
9
+
10
+ Prefer the distillery helpers over constructing a collector yourself:
11
+
12
+ ```ts
13
+ const collector = distillery.artifactCollector('compact');
14
+ collector.use(demo.handles.root); // only this handle ships
15
+ const css = distillery.renderStyles(collector);
16
+ ```
17
+
18
+ `artifactCollector` starts empty. `stylesheetCollector(names?)` defaults to `'readable'` and preloads every registered module via `useAllEntries`. Both accept an optional `{ warn }` so hosts can capture no-op-handle warnings when `createDistillery({ dev: true })`.
19
+
20
+ ## What each method collects
21
+
22
+ | Method | Effect |
23
+ | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
24
+ | `use(module)` | Non-variant entries on the module. Variants stay out. |
25
+ | `use(entry)` | One handle, rule, or media block. |
26
+ | `useHandles(handles)` | Those handles, then any `auto` rule or media-inner-rule whose selector deps are all present. Media blocks include only the live inner rules. |
27
+ | `useAllEntries(module)` | Every entry, including variants. Used by the stylesheet target. |
28
+ | `useThemeVar(path)` | Marks a theme token path reachable even if no collected declaration reads it. |
29
+
30
+ Paths are slash-delimited (`colors/ink`), matching `themeVars` keys. There is no `StylesCollector.artifact(...)` static factory.
31
+
32
+ ## Strict selector deps
33
+
34
+ A rule that reads two handles (`button + other`) does not auto-collect when only `button` is named. Nested `&:hover` on a handle auto-collects with that handle. A nested block on an uncollected handle is dropped from artifacts.
35
+
36
+ ## Theme-var reachability
37
+
38
+ `renderThemeVars` emits a declaration for every path in `collectedThemeDeps`. Those paths come from tokens interpolated into collected declarations, default-marker value deps that survive [per-handle narrowing](#per-handle-default-narrowing), and explicit `useThemeVar(path)` calls.
39
+
40
+ The emitted body CSS is not consulted. A collected path still ships if no remaining rule writes a textual `var(...)` for it. `useThemeVar` is the supported way to force a theme declaration that the body does not read.
41
+
42
+ Named variations, media variations, and `{ alternates }` still render through `renderThemeVars`. They change which values and extra blocks are written for the already-collected paths; they do not decide reachability.
43
+
44
+ ## Per-handle default narrowing
45
+
46
+ `reachableDefaults(handle, groupPath)` is the finest pruning layer. When a handle declaration contains a local-var default group (`${look}`), emission keeps only the keys that handle actually reads — plus keys read by any collected rule whose selector targets that handle.
47
+
48
+ A default key that is declared but never referenced does not ship, and neither does its theme dep. `names module-local var groups with the environment prefix and prunes unreachable defaults` in `src/distillery.test.ts` is the executable spec.
49
+
50
+ ### Rule-level defaults emit every key
51
+
52
+ `renderRule` has no host handle. Default markers on a rule (or a media-inner-rule) therefore emit **every** listed key, and `finalizeDeps` mirrors that by including all of them in the compact registry. Authors normally put `${group}` on handle declarations; if a rule ever carries a default marker, nothing inside that marker is pruned.
53
+
54
+ ## No-op handle pruning
55
+
56
+ A handle is no-op when its declarations produce nothing **and** no rule targets it. With `createDistillery({ dev: true })`, Distillate warns once per such handle (process-wide) so the author can delete the empty template. Production stays silent: `dev` defaults to `false`. `{ warn }` on a collector overrides the sink; it does not turn warnings on. The single-copy guard always warns and is not gated by `dev`.
57
+
58
+ Empty blocks never ship: `renderStyles` drops a handle or rule whose rendered body is blank, and a media block whose inner fragments are all blank.
59
+
60
+ Dropping the class name itself happens only in `compact` mode. Readable names are stable, so a consumer stylesheet or test may scope on one. Compact names have no such contract, and no-op handles do not consume a compact slot. `useHandles` returns the retained handles so hosts can build the class string without changing `combineClassNames`.
61
+
62
+ A handle is targeted when some rule, or some inner rule of an `@media` / `@container` block, reads it in a selector. `recordRuleDeps` fills that in for nested `&` rules too, so a handle with an empty self block and a real `& h2` stays. A nested block with no rules inside it (`@media (...) {}`) targets nothing and does not rescue its owner. A local-var marker counts as content, so marker-bearing handles are never pruned.
63
+
64
+ ## Resolvers
65
+
66
+ `collector.createResolver()` snapshots the current handle and var keys. In compact mode, names are assigned from the **sorted** key set, so call it after collection is complete. `renderStyles(collector)` does that for you when you omit the resolver argument.
@@ -0,0 +1,47 @@
1
+ ---
2
+ navigation_title: The distillery
3
+ description: Bind prefix and a theme tree with createDistillery.
4
+ ---
5
+
6
+ # The distillery
7
+
8
+ `createDistillery(options)` binds the engine to one component library. The returned object is destructure-safe (no `this`): `environment`, `tokens`, `themeVars`, `resolveValues`, `registry`, `dev`, `createStyleModule`, `primitiveStyles`, `artifactCollector`, `stylesheetCollector`, `renderStyles`, `createNameResolver`.
9
+
10
+ ```ts
11
+ import { createDistillery, cq, lightDark } from '@elastic/distillate';
12
+
13
+ const distillery = createDistillery({
14
+ prefix: 'eui',
15
+ themeScope: '.eui-view',
16
+ theme: {
17
+ colors: {
18
+ ink: lightDark('#111', '#eee'),
19
+ },
20
+ gap: cq('8px', '2cqi'),
21
+ },
22
+ sharedVars: ['vars/app/tone/foreground'],
23
+ });
24
+ ```
25
+
26
+ `environment` is the **resolved** environment: derived `themeVars` and `tokens`, plus `prefix` and `themeScope`. `distillery.tokens` aliases `environment.tokens`; `distillery.themeVars` aliases `environment.themeVars`. Input is `DistilleryOptions`.
27
+
28
+ Each distillery owns a private `StyleRegistry`. Two libraries in one process should each call `createDistillery` with their own prefix so readable names cannot collide across brands. A second brand that can diverge structurally is a second distillery, not a [variation](../guides/theming.md#when-not-to-use-a-variation).
29
+
30
+ ## Options
31
+
32
+ | Field | Role |
33
+ | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ |
34
+ | `prefix` | CSS identifier segment used in `cssVarName(prefix, path)` readable custom properties. |
35
+ | `themeScope` | Selector wrapping the emitted theme-variable block (`.eui-view`, `:host`, …). |
36
+ | `theme` | Nested value tree. Strings and `lightDark` leaves become theme vars; `cq` / `scaleToken` leaves inline. Keys match `/^[A-Za-z_][A-Za-z0-9_]*$/`. |
37
+ | `sharedVars` | Optional cross-module contextual-var paths. Names derive via `cssVarName`. Module-local `vars(...)` groups are not listed here. |
38
+ | `variations` | Optional named value-only diffs of `theme`. Declaring a variation does not emit it. See [theming](../guides/theming.md). |
39
+ | `dev` | Optional. When `true`, collectors warn about no-op handles. Default `false`. Does not affect the single-copy guard. |
40
+
41
+ `prefix` is validated at construction. Theme-tree keys that contain `-` throw. `themeVars` and `sharedVars` that hyphenate to the same readable custom-property name throw. `lightDark` values that are not CSS `<color>` throw. Named `variations` that introduce unknown paths or disagree on leaf kind throw.
42
+
43
+ See [declare and select variations](../guides/theming.md) for render-time `flatten`, `scheme`, and `alternates`. `resolveValues(scheme, variation?)` returns the same tree as nested literal strings for a non-CSS surface; `renderStyles({ scheme })` emits the stylesheet with that scheme already chosen. See [read values outside CSS](../guides/non-css-surfaces.md).
44
+
45
+ `primitiveStyles(name, factory)` is the same registry path as `createStyleModule`, with `factory` receiving `{ style, tokens }` instead of the full authoring API. Use it when a module only needs handles and tokens.
46
+
47
+ See [environment reference](../reference/environment.md) for field-level types.
@@ -0,0 +1,18 @@
1
+ ---
2
+ navigation_title: Concepts
3
+ description: Distillery, tokens, authoring, collection, naming, and the single-copy invariant.
4
+ ---
5
+
6
+ # Concepts
7
+
8
+ How Distillate turns tagged templates into CSS, in engine order: bind, author, collect, emit.
9
+
10
+ - [The distillery](distillery.md) — `createDistillery` and `DistilleryOptions`
11
+ - [Tokens and vars](tokens.md) — theme trees, `lightDark`, `cq`, `contextualVar`, `vars`
12
+ - [Authoring](authoring.md) — modules, handles, `css`, `rule`, `media`, `variants`
13
+ - [Reachability collection](collection.md) — `StylesCollector`
14
+ - [Naming and output](naming-and-output.md) — the four-way matrix
15
+ - [Ordering and specificity](ordering.md) — base rules before at-rules
16
+ - [Single-copy invariant](single-copy.md) — one loaded copy of the package
17
+
18
+ Named variations are a guide: [Declare and select variations](../guides/theming.md). Literal values for non-CSS surfaces: [Read values outside CSS](../guides/non-css-surfaces.md).
@@ -0,0 +1,123 @@
1
+ ---
2
+ navigation_title: Naming and output
3
+ description: Artifact versus stylesheet, compact versus readable, with a worked example per quadrant.
4
+ ---
5
+
6
+ # Naming and output
7
+
8
+ Two independent axes. **Target** is how much CSS you want. **Name mode** is how classes and custom properties are spelled.
9
+
10
+ | | `readable` | `compact` |
11
+ | ---------------- | ------------------------------------------------- | -------------------------------------------------------------- |
12
+ | **`stylesheet`** | Public sheet. `.demo-root`, `--eui-colors-ink`. | Minified names for the full sheet. Unusual. |
13
+ | **`artifact`** | Readable names, tree-shaken to collected handles. | Minimal payload. `.a`, `--b`. HTML and CSS must ship together. |
14
+
15
+ Empty declaration blocks never ship in any quadrant. In the two `compact` quadrants, a handle that is empty and untargeted is also dropped from collection, so it consumes no compact name and is omitted from `useHandles`'s return value. Readable collectors keep the class name so external selectors can still target it.
16
+
17
+ Same module for every quadrant:
18
+
19
+ ```ts
20
+ const demo = distillery.createStyleModule('demo', ({ css, tokens }) => ({
21
+ root: css`
22
+ color: ${tokens.colors.ink};
23
+ padding: ${tokens.gap};
24
+ `,
25
+ unused: css`
26
+ color: ${tokens.colors.accent};
27
+ `, <1>
28
+ }));
29
+ ```
30
+
31
+ 1. Collected only by `stylesheetCollector()`. Artifact collectors that `use(root)` drop this handle and `--eui-colors-accent`.
32
+
33
+ ## Stylesheet + readable
34
+
35
+ ```ts
36
+ distillery.renderStyles(distillery.stylesheetCollector());
37
+ ```
38
+
39
+ Includes `unused`. Theme vars use `cssVarName(prefix, path)`:
40
+
41
+ ```css
42
+ .eui-view {
43
+ --eui-colors-ink: light-dark(#111, #eee);
44
+ --eui-colors-accent: light-dark(#06c, #8cf);
45
+ }
46
+ .demo-root {
47
+ color: var(--eui-colors-ink);
48
+ padding: 8px;
49
+ }
50
+ .demo-unused {
51
+ color: var(--eui-colors-accent);
52
+ }
53
+ ```
54
+
55
+ Use this in an app that loads one CSS file and writes `class={handle.readableName}`.
56
+
57
+ ## Stylesheet + compact
58
+
59
+ ```ts
60
+ distillery.renderStyles(distillery.stylesheetCollector('compact'));
61
+ ```
62
+
63
+ Still every entry, but classes and compacted theme vars are short identifiers assigned from sorted keys. Only useful if the markup that refers to those classes was emitted with the same resolver.
64
+
65
+ ## Artifact + readable
66
+
67
+ ```ts
68
+ const collector = distillery.artifactCollector('readable');
69
+ collector.use(demo.handles.root);
70
+ distillery.renderStyles(collector);
71
+ ```
72
+
73
+ `unused` and `--eui-colors-accent` are gone. Class names stay stable:
74
+
75
+ ```css
76
+ .eui-view {
77
+ --eui-colors-ink: light-dark(#111, #eee);
78
+ }
79
+ .demo-root {
80
+ color: var(--eui-colors-ink);
81
+ padding: 8px;
82
+ }
83
+ ```
84
+
85
+ ## Artifact + compact
86
+
87
+ ```ts
88
+ const collector = distillery.artifactCollector('compact');
89
+ collector.use(demo.handles.root);
90
+ distillery.renderStyles(collector);
91
+ ```
92
+
93
+ ```css
94
+ .eui-view {
95
+ --a: light-dark(#111, #eee);
96
+ }
97
+ .a {
98
+ color: var(--a);
99
+ padding: 8px;
100
+ }
101
+ ```
102
+
103
+ This is the payload target: emails, SVG, exported HTML, agent replies. Compact names are **not** stable across different collected sets. Always emit HTML and CSS from the same collector/resolver pair.
104
+
105
+ ## Render options
106
+
107
+ ```ts
108
+ distillery.renderStyles(collector, resolver, {
109
+ flatten: 'muted', <1>
110
+ alternates: [
111
+ { variation: 'muted', selector: '[data-eui-theme="muted"]' }, <2>
112
+ ],
113
+ themeValueOverrides: { 'colors/ink': '#000' }, <3>
114
+ });
115
+ ```
116
+
117
+ 1. Flatten this variation into `themeScope`. Same declaration count as the base; values change. A media variation does not replace the primary block.
118
+ 2. Emit only this variation's diff under a selector the consuming page supplies.
119
+ 3. Replaces the light/dark pair with a single value. Wins over the flattened variation.
120
+
121
+ Custom-property names always follow `cssVarName(prefix, path)`: `--${prefix}-${path}` with `/` joined on `-` and a leading `vars/` stripped.
122
+
123
+ See [declare and select variations](../guides/theming.md).
@@ -0,0 +1,61 @@
1
+ ---
2
+ navigation_title: Ordering and specificity
3
+ description: Base rules emit before every media and container block.
4
+ ---
5
+
6
+ # Ordering and specificity
7
+
8
+ `@media` and `@container` add no specificity. When a base rule and a conditional override target the same selector, **source order** decides the winner. Distillate therefore emits every non-conditional entry first, then every `@media` / `@container` block, globally — not per module.
9
+
10
+ To override a base declaration at a breakpoint, put the override in `media(...)` or `container(...)` (or nest `@media` inside `css`). Do not rely on module registration order.
11
+
12
+ ```ts
13
+ import {
14
+ createDistillery,
15
+ media,
16
+ rule,
17
+ } from '@elastic/distillate';
18
+
19
+ const distillery = createDistillery({
20
+ prefix: 'eui',
21
+ themeScope: '.eui-view',
22
+ theme: {},
23
+ });
24
+
25
+ distillery.createStyleModule('zebra', ({ css, decls }) => ({
26
+ root: css`
27
+ grid-template-columns: repeat(4, 1fr);
28
+ `,
29
+ responsive: media('(max-width: 620px)', [
30
+ rule(
31
+ (selectors) => `${selectors.root}`,
32
+ decls`
33
+ grid-template-columns: 1fr;
34
+ `
35
+ ),
36
+ ]),
37
+ }));
38
+
39
+ distillery.renderStyles(distillery.stylesheetCollector());
40
+ ```
41
+
42
+ ```css
43
+ .zebra-root {
44
+ grid-template-columns: repeat(4, 1fr);
45
+ }
46
+ @media (max-width: 620px) {
47
+ .zebra-root {
48
+ grid-template-columns: 1fr;
49
+ }
50
+ }
51
+ ```
52
+
53
+ `container(...)` keeps `kind: 'media'` so it shares that rank. Nested `@media` inside `css` flattens to the same at-rule bucket.
54
+
55
+ ## Local var overlays
56
+
57
+ `${group}` defaults and `${group.set()}` overrides have equal specificity when both classes are on the same element. Distillate emits every non-override entry first, then every `.set()` override, globally — not per handle name — so the stacked modifier wins.
58
+
59
+ ## Emotion composition
60
+
61
+ Independent `css` calls have no guaranteed order relative to each other. `cx(a, b)` only joins class names; it does not order the stylesheet. To override, interpolate (`css\`${base} color: red;\``). The composed declarations re-target the composing class in source order (last wins).