boss-css 0.0.11 → 0.0.13

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.
@@ -23,6 +23,7 @@ const STATIC_DOC = `### Syntax
23
23
  - Prefer token keys (color="primary") over $$.token.* when available.
24
24
  - Token overrides can be passed via the tokens prop at runtime.
25
25
  - Prefer token shorthands for shadows (boxShadow="md") when available.
26
+ - In classname-first, dynamic tokens should return $$.token.* from the function.
26
27
 
27
28
  ### Selectors
28
29
  - Use pseudos for state (hover, focus, etc.).
@@ -47,6 +48,7 @@ const STATIC_DOC = `### Syntax
47
48
  - classname-first: smaller inline styles; use function values for dynamics.
48
49
  - runtime-only: client CSS injection only.
49
50
  - compile pruning: keep props static when you want runtime-free output.
51
+ - runtime.globals: inline | file | none (reset/fontsource/token/$$.css output).
50
52
  `;
51
53
  const EVENT_NAMES = [
52
54
  "onBoot",
@@ -21,6 +21,7 @@ const STATIC_DOC = `### Syntax
21
21
  - Prefer token keys (color="primary") over $$.token.* when available.
22
22
  - Token overrides can be passed via the tokens prop at runtime.
23
23
  - Prefer token shorthands for shadows (boxShadow="md") when available.
24
+ - In classname-first, dynamic tokens should return $$.token.* from the function.
24
25
 
25
26
  ### Selectors
26
27
  - Use pseudos for state (hover, focus, etc.).
@@ -45,6 +46,7 @@ const STATIC_DOC = `### Syntax
45
46
  - classname-first: smaller inline styles; use function values for dynamics.
46
47
  - runtime-only: client CSS injection only.
47
48
  - compile pruning: keep props static when you want runtime-free output.
49
+ - runtime.globals: inline | file | none (reset/fontsource/token/$$.css output).
48
50
  `;
49
51
  const EVENT_NAMES = [
50
52
  "onBoot",
@@ -16,6 +16,7 @@ Apply Boss CSS props cleanly with tokens and selectors.
16
16
  - Use token keys (for example color="primary") when tokens exist.
17
17
  - Use pseudos and @at for state and breakpoints instead of ad-hoc selectors.
18
18
  - In classname-first, pass dynamic values as functions (prop={() => value}).
19
+ - In classname-first, return $$.token.* from dynamic functions when using tokens.
19
20
  - Use child for nested selectors instead of manual selector strings.
20
21
  - Prefer unitless values (padding:12). Use _ for spaces (padding:10_20).
21
22
  - Prefer arrays for shorthands (padding={[0, 10]}).
@@ -53,8 +54,8 @@ Pick the rendering strategy that matches your app needs.
53
54
 
54
55
  # Guidance
55
56
  - inline-first: safest default; good for mixed dynamic/static props.
56
- - classname-first: smallest runtime styles; use function values for dynamics.
57
- - runtime-only: client-only CSS injection; no server CSS output.
57
+ - classname-first: smallest runtime styles; use function values for dynamics and return $$.token.* for dynamic tokens.
58
+ - runtime-only: client-only CSS injection; reset/fontsource/token/$$.css output is controlled by runtime.globals.
58
59
 
59
60
  # References
60
61
  See .bo$$/LLMS.md for the active runtime strategy.
@@ -246,7 +247,10 @@ Use runtime-only mode safely.
246
247
 
247
248
  # Guidance
248
249
  - runtime.only: true disables server CSS output.
249
- - Runtime-only injects CSS on the client; expect no styles.css output.
250
+ - runtime.globals controls reset/fontsource/token/$$.css output in runtime-only:
251
+ - inline injects globals at runtime.
252
+ - file emits styles.css even in runtime-only.
253
+ - none skips global CSS output.
250
254
  - Keep JSX parser enabled and include the runtime strategy plugin.
251
255
 
252
256
  # References
@@ -15,6 +15,7 @@ Apply Boss CSS props cleanly with tokens and selectors.
15
15
  - Use token keys (for example color="primary") when tokens exist.
16
16
  - Use pseudos and @at for state and breakpoints instead of ad-hoc selectors.
17
17
  - In classname-first, pass dynamic values as functions (prop={() => value}).
18
+ - In classname-first, return $$.token.* from dynamic functions when using tokens.
18
19
  - Use child for nested selectors instead of manual selector strings.
19
20
  - Prefer unitless values (padding:12). Use _ for spaces (padding:10_20).
20
21
  - Prefer arrays for shorthands (padding={[0, 10]}).
@@ -52,8 +53,8 @@ Pick the rendering strategy that matches your app needs.
52
53
 
53
54
  # Guidance
54
55
  - inline-first: safest default; good for mixed dynamic/static props.
55
- - classname-first: smallest runtime styles; use function values for dynamics.
56
- - runtime-only: client-only CSS injection; no server CSS output.
56
+ - classname-first: smallest runtime styles; use function values for dynamics and return $$.token.* for dynamic tokens.
57
+ - runtime-only: client-only CSS injection; reset/fontsource/token/$$.css output is controlled by runtime.globals.
57
58
 
58
59
  # References
59
60
  See .bo$$/LLMS.md for the active runtime strategy.
@@ -245,7 +246,10 @@ Use runtime-only mode safely.
245
246
 
246
247
  # Guidance
247
248
  - runtime.only: true disables server CSS output.
248
- - Runtime-only injects CSS on the client; expect no styles.css output.
249
+ - runtime.globals controls reset/fontsource/token/$$.css output in runtime-only:
250
+ - inline injects globals at runtime.
251
+ - file emits styles.css even in runtime-only.
252
+ - none skips global CSS output.
249
253
  - Keep JSX parser enabled and include the runtime strategy plugin.
250
254
 
251
255
  # References
@@ -42,8 +42,14 @@ async function createApi(config, force = false) {
42
42
  applyChildSelectors: require_names.applyChildSelectors
43
43
  };
44
44
  const runtimeOnly = config?.runtime?.only === true;
45
+ const runtimeGlobals = runtimeOnly && config?.runtime?.globals ? config.runtime.globals : runtimeOnly ? "inline" : "file";
46
+ const shouldCollectGlobals = runtimeOnly && runtimeGlobals !== "none";
47
+ if (runtimeOnly) api.runtime = {
48
+ ...api.runtime ?? {},
49
+ globals: runtimeGlobals
50
+ };
45
51
  api.dictionary = new require_dictionary.Dictionary(api);
46
- api.css = runtimeOnly ? new require_api_noopCss.NoopCSS(api) : new require_css.CSS(api);
52
+ api.css = runtimeOnly ? shouldCollectGlobals ? new require_css.CSS(api) : new require_api_noopCss.NoopCSS(api) : new require_css.CSS(api);
47
53
  api.file.js = new require_js.default(api, { path: "index.js" });
48
54
  api.file.native = new require_js.default(api, { path: "native.js" });
49
55
  const initBaseTypes = (dts, options) => {
@@ -62,8 +68,7 @@ async function createApi(config, force = false) {
62
68
  initBaseTypes(api.file.js.dts, { includeObject: false });
63
69
  initBaseTypes(api.file.native.dts, { includeObject: true });
64
70
  await trigger("onBoot");
65
- const autoLoadCss = config?.css?.autoLoad ?? true;
66
- if (!runtimeOnly && autoLoadCss) api.file.js.import({ from: "./styles.css" });
71
+ if ((config?.css?.autoLoad ?? true) && (!runtimeOnly || runtimeGlobals === "file")) api.file.js.import({ from: "./styles.css" });
67
72
  await trigger("onReady");
68
73
  return api;
69
74
  }
@@ -42,8 +42,14 @@ async function createApi(config, force = false) {
42
42
  applyChildSelectors
43
43
  };
44
44
  const runtimeOnly = config?.runtime?.only === true;
45
+ const runtimeGlobals = runtimeOnly && config?.runtime?.globals ? config.runtime.globals : runtimeOnly ? "inline" : "file";
46
+ const shouldCollectGlobals = runtimeOnly && runtimeGlobals !== "none";
47
+ if (runtimeOnly) api.runtime = {
48
+ ...api.runtime ?? {},
49
+ globals: runtimeGlobals
50
+ };
45
51
  api.dictionary = new Dictionary(api);
46
- api.css = runtimeOnly ? new NoopCSS(api) : new CSS(api);
52
+ api.css = runtimeOnly ? shouldCollectGlobals ? new CSS(api) : new NoopCSS(api) : new CSS(api);
47
53
  api.file.js = new JS(api, { path: "index.js" });
48
54
  api.file.native = new JS(api, { path: "native.js" });
49
55
  const initBaseTypes = (dts, options) => {
@@ -62,8 +68,7 @@ async function createApi(config, force = false) {
62
68
  initBaseTypes(api.file.js.dts, { includeObject: false });
63
69
  initBaseTypes(api.file.native.dts, { includeObject: true });
64
70
  await trigger("onBoot");
65
- const autoLoadCss = config?.css?.autoLoad ?? true;
66
- if (!runtimeOnly && autoLoadCss) api.file.js.import({ from: "./styles.css" });
71
+ if ((config?.css?.autoLoad ?? true) && (!runtimeOnly || runtimeGlobals === "file")) api.file.js.import({ from: "./styles.css" });
67
72
  await trigger("onReady");
68
73
  return api;
69
74
  }
@@ -173,7 +173,9 @@ async function compileProject(options) {
173
173
  let cssBytes = 0;
174
174
  let cssWritten = false;
175
175
  let stylesheetPath = null;
176
- if (!prod && api.css.text.trim()) {
176
+ const runtimeOnly = config.runtime?.only === true;
177
+ const runtimeGlobals = runtimeOnly ? config.runtime?.globals ?? "inline" : "file";
178
+ if (!prod && (!runtimeOnly || runtimeGlobals === "file") && api.css.text.trim()) {
177
179
  const resolvedStylesheetPath = config.stylesheetPath ?? node_path.default.join(root, config.configDir ?? ".bo$$", "styles.css");
178
180
  const outputs = (await require_boundaries.resolveBoundaryOutputs(api, {
179
181
  rootDir: root,
@@ -170,7 +170,9 @@ async function compileProject(options) {
170
170
  let cssBytes = 0;
171
171
  let cssWritten = false;
172
172
  let stylesheetPath = null;
173
- if (!prod && api.css.text.trim()) {
173
+ const runtimeOnly = config.runtime?.only === true;
174
+ const runtimeGlobals = runtimeOnly ? config.runtime?.globals ?? "inline" : "file";
175
+ if (!prod && (!runtimeOnly || runtimeGlobals === "file") && api.css.text.trim()) {
174
176
  const resolvedStylesheetPath = config.stylesheetPath ?? path.join(root, config.configDir ?? ".bo$$", "styles.css");
175
177
  const outputs = (await resolveBoundaryOutputs(api, {
176
178
  rootDir: root,
@@ -238,7 +238,7 @@ const addFontFaceBlocks = (api, cssText, cssUrl, downloads, axes, fontFolder) =>
238
238
  }
239
239
  };
240
240
  const onBoot = async (api) => {
241
- if (api.runtime?.only) return;
241
+ if (api.runtime?.only && api.runtime?.globals === "none") return;
242
242
  const fonts = api.fonts;
243
243
  if (!Array.isArray(fonts) || fonts.length === 0) return;
244
244
  const importUrls = [];
@@ -236,7 +236,7 @@ const addFontFaceBlocks = (api, cssText, cssUrl, downloads, axes, fontFolder) =>
236
236
  }
237
237
  };
238
238
  const onBoot = async (api) => {
239
- if (api.runtime?.only) return;
239
+ if (api.runtime?.only && api.runtime?.globals === "none") return;
240
240
  const fonts = api.fonts;
241
241
  if (!Array.isArray(fonts) || fonts.length === 0) return;
242
242
  const importUrls = [];
@@ -1,7 +1,7 @@
1
1
  import { createRequire } from "node:module";
2
2
  import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
- import fastGlob from "fast-glob";
4
+ import fg from "fast-glob";
5
5
 
6
6
  //#region src/native/styleTypes.ts
7
7
  const interfaceNames = new Set([
@@ -129,7 +129,7 @@ const resolveReactNativeRoot = async () => {
129
129
  };
130
130
  };
131
131
  const findStyleSheetTypesFile = async (root, typesEntry) => {
132
- const candidates = await fastGlob(["**/StyleSheetTypes.d.ts", "**/StyleSheetTypes.ts"], {
132
+ const candidates = await fg(["**/StyleSheetTypes.d.ts", "**/StyleSheetTypes.ts"], {
133
133
  cwd: root,
134
134
  absolute: true,
135
135
  suppressErrors: true
@@ -139,7 +139,7 @@ const findStyleSheetTypesFile = async (root, typesEntry) => {
139
139
  const entryPath = path.join(root, typesEntry);
140
140
  if ((await fs.readFile(entryPath, "utf-8").catch(() => "")).includes("interface ViewStyle")) return entryPath;
141
141
  }
142
- const fallback = await fastGlob(["**/*.d.ts"], {
142
+ const fallback = await fg(["**/*.d.ts"], {
143
143
  cwd: root,
144
144
  absolute: true,
145
145
  suppressErrors: true
@@ -1,5 +1,5 @@
1
1
  import path from "node:path";
2
- import fastGlob from "fast-glob";
2
+ import fg from "fast-glob";
3
3
 
4
4
  //#region src/shared/boundaries.ts
5
5
  const DEFAULT_CRITICALITY = 2;
@@ -13,7 +13,7 @@ const normalizeCssText = (value) => {
13
13
  const findBoundaryFiles = async (rootDir, config = {}) => {
14
14
  const ignore = [...config.ignore ?? []];
15
15
  ignore.push("**/node_modules/**");
16
- return (await fastGlob(BOUNDARY_PATTERN, {
16
+ return (await fg(BOUNDARY_PATTERN, {
17
17
  cwd: rootDir,
18
18
  absolute: true,
19
19
  onlyFiles: true,
@@ -1,4 +1,4 @@
1
- import fastGlob from "fast-glob";
1
+ import fg from "fast-glob";
2
2
 
3
3
  //#region src/shared/file.ts
4
4
  const DEFAULT_IGNORE = [
@@ -7,7 +7,7 @@ const DEFAULT_IGNORE = [
7
7
  "**/dist/**"
8
8
  ];
9
9
  const resolveContentPaths = async (content) => {
10
- return fastGlob(content, {
10
+ return fg(content, {
11
11
  onlyFiles: false,
12
12
  absolute: true,
13
13
  cwd: process.cwd(),
@@ -19,6 +19,7 @@ const onBrowserObjectStart = (api, payload) => {
19
19
  };
20
20
 
21
21
  //#endregion
22
+ exports.applyGlobals = require_strategy_runtime_only_css.applyGlobals;
22
23
  exports.name = name;
23
24
  exports.onBrowserObjectStart = onBrowserObjectStart;
24
25
  exports.onInit = onInit;
@@ -1,5 +1,5 @@
1
1
  import { onBrowserObjectStart as onBrowserObjectStart$1 } from "../inline-first/runtime-only.mjs";
2
- import { onInit as onInit$1 } from "../runtime-only/css.mjs";
2
+ import { applyGlobals, onInit as onInit$1 } from "../runtime-only/css.mjs";
3
3
  import { onBrowserObjectStart as onBrowserObjectStart$2 } from "../classname-first/runtime-only.mjs";
4
4
  import { onBrowserObjectStart as onBrowserObjectStart$3 } from "../classic/runtime-only.mjs";
5
5
 
@@ -19,4 +19,4 @@ const onBrowserObjectStart = (api, payload) => {
19
19
  };
20
20
 
21
21
  //#endregion
22
- export { name, onBrowserObjectStart, onInit };
22
+ export { applyGlobals, name, onBrowserObjectStart, onInit };
@@ -13,6 +13,10 @@ const name = "runtime";
13
13
  const settings = new Map([["emitRuntime", false], ["emitAllTokens", false]]);
14
14
  let needsRuntime = false;
15
15
  const resolveRuntimeStrategy = (api) => api.runtime?.strategy ?? "inline-first";
16
+ const resolveRuntimeGlobals = (api) => {
17
+ if (!api.runtime?.only) return null;
18
+ return api.runtime?.globals ?? "inline";
19
+ };
16
20
  const resolveServerStrategy = (strategy) => {
17
21
  if (strategy === "classname-first") return require_strategy_classname_first_server.server_exports;
18
22
  return require_strategy_inline_first_server.server_exports;
@@ -20,7 +24,8 @@ const resolveServerStrategy = (strategy) => {
20
24
  const onBoot = async (api) => {
21
25
  const runtimeStrategy = resolveRuntimeStrategy(api);
22
26
  const runtimeConfig = api.runtime ?? {};
23
- const shouldEmitRuntime = () => needsRuntime || settings.get("emitRuntime") === true;
27
+ const shouldInlineGlobals = resolveRuntimeGlobals(api) === "inline";
28
+ const shouldEmitRuntime = () => needsRuntime || settings.get("emitRuntime") === true || shouldInlineGlobals;
24
29
  const runtimeModule = "boss-css/strategy/runtime/runtime-only";
25
30
  const onBrowserObjectStartVar = api.file.js.import({
26
31
  name: "onBrowserObjectStart",
@@ -30,6 +35,10 @@ const onBoot = async (api) => {
30
35
  name: "onInit",
31
36
  from: runtimeModule
32
37
  }, shouldEmitRuntime);
38
+ const applyGlobalsVar = shouldInlineGlobals ? api.file.js.import({
39
+ name: "applyGlobals",
40
+ from: runtimeModule
41
+ }, shouldEmitRuntime) : null;
33
42
  api.file.js.config({
34
43
  from: runtimeModule,
35
44
  config: { plugin: {
@@ -46,6 +55,11 @@ const onBoot = async (api) => {
46
55
  runtime: runtimeConfig
47
56
  }
48
57
  }, shouldEmitRuntime);
58
+ if (shouldInlineGlobals && applyGlobalsVar) api.file.js.set("body", "runtime:globals", () => {
59
+ const cssText = api.css?.text?.trim();
60
+ if (!cssText) return "";
61
+ return `${applyGlobalsVar}(${JSON.stringify({ cssText })});`;
62
+ }, shouldEmitRuntime);
49
63
  api.strategy = runtimeStrategy;
50
64
  if (runtimeStrategy === "classname-first") {
51
65
  if (settings.get("emitAllTokens") === true || process.env.NODE_ENV !== "production") api.emitAllTokens = true;
@@ -13,6 +13,10 @@ const name = "runtime";
13
13
  const settings = new Map([["emitRuntime", false], ["emitAllTokens", false]]);
14
14
  let needsRuntime = false;
15
15
  const resolveRuntimeStrategy = (api) => api.runtime?.strategy ?? "inline-first";
16
+ const resolveRuntimeGlobals = (api) => {
17
+ if (!api.runtime?.only) return null;
18
+ return api.runtime?.globals ?? "inline";
19
+ };
16
20
  const resolveServerStrategy = (strategy) => {
17
21
  if (strategy === "classname-first") return server_exports$2;
18
22
  return server_exports$1;
@@ -20,7 +24,8 @@ const resolveServerStrategy = (strategy) => {
20
24
  const onBoot = async (api) => {
21
25
  const runtimeStrategy = resolveRuntimeStrategy(api);
22
26
  const runtimeConfig = api.runtime ?? {};
23
- const shouldEmitRuntime = () => needsRuntime || settings.get("emitRuntime") === true;
27
+ const shouldInlineGlobals = resolveRuntimeGlobals(api) === "inline";
28
+ const shouldEmitRuntime = () => needsRuntime || settings.get("emitRuntime") === true || shouldInlineGlobals;
24
29
  const runtimeModule = "boss-css/strategy/runtime/runtime-only";
25
30
  const onBrowserObjectStartVar = api.file.js.import({
26
31
  name: "onBrowserObjectStart",
@@ -30,6 +35,10 @@ const onBoot = async (api) => {
30
35
  name: "onInit",
31
36
  from: runtimeModule
32
37
  }, shouldEmitRuntime);
38
+ const applyGlobalsVar = shouldInlineGlobals ? api.file.js.import({
39
+ name: "applyGlobals",
40
+ from: runtimeModule
41
+ }, shouldEmitRuntime) : null;
33
42
  api.file.js.config({
34
43
  from: runtimeModule,
35
44
  config: { plugin: {
@@ -46,6 +55,11 @@ const onBoot = async (api) => {
46
55
  runtime: runtimeConfig
47
56
  }
48
57
  }, shouldEmitRuntime);
58
+ if (shouldInlineGlobals && applyGlobalsVar) api.file.js.set("body", "runtime:globals", () => {
59
+ const cssText = api.css?.text?.trim();
60
+ if (!cssText) return "";
61
+ return `${applyGlobalsVar}(${JSON.stringify({ cssText })});`;
62
+ }, shouldEmitRuntime);
49
63
  api.strategy = runtimeStrategy;
50
64
  if (runtimeStrategy === "classname-first") {
51
65
  if (settings.get("emitAllTokens") === true || process.env.NODE_ENV !== "production") api.emitAllTokens = true;
@@ -8,6 +8,7 @@ let atRuleIndex = 0;
8
8
  const atRules = [];
9
9
  const inserted = /* @__PURE__ */ new Set();
10
10
  const canUseDom = () => typeof document !== "undefined" && typeof document.createElement === "function";
11
+ const GLOBAL_STYLE_ATTR = "data-boss-globals";
11
12
  const ensureBaseSheet = () => {
12
13
  if (baseSheet || !canUseDom()) return baseSheet;
13
14
  baseStyleElement = document.querySelector("style[data-boss-runtime]");
@@ -19,6 +20,18 @@ const ensureBaseSheet = () => {
19
20
  baseSheet = baseStyleElement.sheet;
20
21
  return baseSheet;
21
22
  };
23
+ const applyGlobals = (payload) => {
24
+ if (!payload || !canUseDom()) return;
25
+ const cssText = typeof payload === "string" ? payload : payload.cssText;
26
+ if (!cssText) return;
27
+ let style = document.querySelector(`style[${GLOBAL_STYLE_ATTR}]`);
28
+ if (!style) {
29
+ style = document.createElement("style");
30
+ style.setAttribute(GLOBAL_STYLE_ATTR, "");
31
+ document.head.appendChild(style);
32
+ }
33
+ if (style.textContent !== cssText) style.textContent = cssText;
34
+ };
22
35
  const ensureAtSheet = () => {
23
36
  if (atSheet || !canUseDom()) return atSheet;
24
37
  ensureBaseSheet();
@@ -124,7 +137,14 @@ var RuntimeCSS = class {
124
137
  return "";
125
138
  }
126
139
  addImport(_url) {}
127
- addRoot(_value, _source) {}
140
+ addRoot(value, _source) {
141
+ if (!value) return;
142
+ if (this.root.has(value)) return;
143
+ this.root.add(value);
144
+ if (!canUseDom()) return;
145
+ const selector = typeof this.api.selectorScope === "string" && this.api.selectorScope.length > 0 ? this.api.selectorScope : ":root";
146
+ insertRule(`${selector} { ${value} }`, `root|${selector}|${value}`);
147
+ }
128
148
  removeSource(_source) {}
129
149
  reset() {}
130
150
  selector({ className, selector = null, pseudos = [], query = null }) {
@@ -180,4 +200,5 @@ const onInit = (api) => {
180
200
 
181
201
  //#endregion
182
202
  exports.RuntimeCSS = RuntimeCSS;
203
+ exports.applyGlobals = applyGlobals;
183
204
  exports.onInit = onInit;
@@ -7,6 +7,7 @@ let atRuleIndex = 0;
7
7
  const atRules = [];
8
8
  const inserted = /* @__PURE__ */ new Set();
9
9
  const canUseDom = () => typeof document !== "undefined" && typeof document.createElement === "function";
10
+ const GLOBAL_STYLE_ATTR = "data-boss-globals";
10
11
  const ensureBaseSheet = () => {
11
12
  if (baseSheet || !canUseDom()) return baseSheet;
12
13
  baseStyleElement = document.querySelector("style[data-boss-runtime]");
@@ -18,6 +19,18 @@ const ensureBaseSheet = () => {
18
19
  baseSheet = baseStyleElement.sheet;
19
20
  return baseSheet;
20
21
  };
22
+ const applyGlobals = (payload) => {
23
+ if (!payload || !canUseDom()) return;
24
+ const cssText = typeof payload === "string" ? payload : payload.cssText;
25
+ if (!cssText) return;
26
+ let style = document.querySelector(`style[${GLOBAL_STYLE_ATTR}]`);
27
+ if (!style) {
28
+ style = document.createElement("style");
29
+ style.setAttribute(GLOBAL_STYLE_ATTR, "");
30
+ document.head.appendChild(style);
31
+ }
32
+ if (style.textContent !== cssText) style.textContent = cssText;
33
+ };
21
34
  const ensureAtSheet = () => {
22
35
  if (atSheet || !canUseDom()) return atSheet;
23
36
  ensureBaseSheet();
@@ -123,7 +136,14 @@ var RuntimeCSS = class {
123
136
  return "";
124
137
  }
125
138
  addImport(_url) {}
126
- addRoot(_value, _source) {}
139
+ addRoot(value, _source) {
140
+ if (!value) return;
141
+ if (this.root.has(value)) return;
142
+ this.root.add(value);
143
+ if (!canUseDom()) return;
144
+ const selector = typeof this.api.selectorScope === "string" && this.api.selectorScope.length > 0 ? this.api.selectorScope : ":root";
145
+ insertRule(`${selector} { ${value} }`, `root|${selector}|${value}`);
146
+ }
127
147
  removeSource(_source) {}
128
148
  reset() {}
129
149
  selector({ className, selector = null, pseudos = [], query = null }) {
@@ -178,4 +198,4 @@ const onInit = (api) => {
178
198
  };
179
199
 
180
200
  //#endregion
181
- export { RuntimeCSS, onInit };
201
+ export { RuntimeCSS, applyGlobals, onInit };
@@ -19,6 +19,8 @@ const resolveBuildConfig = (userConfig, baseDir = process.cwd()) => {
19
19
  const configDir = userConfig.configDir ?? ".bo$$";
20
20
  const folder = userConfig.folder ?? configDir;
21
21
  const stylesheetPath = userConfig.stylesheetPath ?? node_path.default.join(baseDir, configDir, "styles.css");
22
+ const runtimeOnly = userConfig.runtime?.only === true;
23
+ const runtimeGlobals = runtimeOnly ? userConfig.runtime?.globals ?? "inline" : "file";
22
24
  return {
23
25
  config: {
24
26
  ...userConfig,
@@ -28,13 +30,14 @@ const resolveBuildConfig = (userConfig, baseDir = process.cwd()) => {
28
30
  },
29
31
  folderPath: node_path.default.resolve(baseDir, folder),
30
32
  stylesheetPath: node_path.default.isAbsolute(stylesheetPath) ? stylesheetPath : node_path.default.resolve(baseDir, stylesheetPath),
31
- runtimeOnly: userConfig.runtime?.only === true
33
+ runtimeOnly,
34
+ runtimeGlobals
32
35
  };
33
36
  };
34
37
  const runBuild = async (userConfig, options = {}) => {
35
38
  const baseDir = options.baseDir ?? process.cwd();
36
39
  const start = process.hrtime.bigint();
37
- const { config, folderPath, stylesheetPath, runtimeOnly } = resolveBuildConfig(userConfig, baseDir);
40
+ const { config, folderPath, stylesheetPath, runtimeOnly, runtimeGlobals } = resolveBuildConfig(userConfig, baseDir);
38
41
  if (!config.content) throw new Error("boss build requires config.content in .bo$$/config.js or package.json.");
39
42
  const api = await require_api_server.createApi(config, true);
40
43
  api.baseDir = baseDir;
@@ -86,6 +89,21 @@ const runBuild = async (userConfig, options = {}) => {
86
89
  const globalOutput = boundaryResult.outputs.find((output) => output.path === stylesheetPath);
87
90
  cssPath = stylesheetPath;
88
91
  cssBytes = Buffer.byteLength(globalOutput?.text ?? "");
92
+ } else if (runtimeGlobals === "file") {
93
+ await api.trigger("onMetaData", {
94
+ kind: "ai",
95
+ replace: true,
96
+ data: {
97
+ section: "boundaries",
98
+ title: "CSS boundaries",
99
+ content: formatBoundaryLines([], baseDir)
100
+ }
101
+ });
102
+ const text = api.css?.text ?? "";
103
+ await node_fs_promises.default.mkdir(node_path.default.dirname(stylesheetPath), { recursive: true });
104
+ await node_fs_promises.default.writeFile(stylesheetPath, text, "utf8");
105
+ cssPath = stylesheetPath;
106
+ cssBytes = Buffer.byteLength(text);
89
107
  } else await api.trigger("onMetaData", {
90
108
  kind: "ai",
91
109
  replace: true,
@@ -16,6 +16,8 @@ const resolveBuildConfig = (userConfig, baseDir = process.cwd()) => {
16
16
  const configDir = userConfig.configDir ?? ".bo$$";
17
17
  const folder = userConfig.folder ?? configDir;
18
18
  const stylesheetPath = userConfig.stylesheetPath ?? path.join(baseDir, configDir, "styles.css");
19
+ const runtimeOnly = userConfig.runtime?.only === true;
20
+ const runtimeGlobals = runtimeOnly ? userConfig.runtime?.globals ?? "inline" : "file";
19
21
  return {
20
22
  config: {
21
23
  ...userConfig,
@@ -25,13 +27,14 @@ const resolveBuildConfig = (userConfig, baseDir = process.cwd()) => {
25
27
  },
26
28
  folderPath: path.resolve(baseDir, folder),
27
29
  stylesheetPath: path.isAbsolute(stylesheetPath) ? stylesheetPath : path.resolve(baseDir, stylesheetPath),
28
- runtimeOnly: userConfig.runtime?.only === true
30
+ runtimeOnly,
31
+ runtimeGlobals
29
32
  };
30
33
  };
31
34
  const runBuild = async (userConfig, options = {}) => {
32
35
  const baseDir = options.baseDir ?? process.cwd();
33
36
  const start = process.hrtime.bigint();
34
- const { config, folderPath, stylesheetPath, runtimeOnly } = resolveBuildConfig(userConfig, baseDir);
37
+ const { config, folderPath, stylesheetPath, runtimeOnly, runtimeGlobals } = resolveBuildConfig(userConfig, baseDir);
35
38
  if (!config.content) throw new Error("boss build requires config.content in .bo$$/config.js or package.json.");
36
39
  const api = await createApi(config, true);
37
40
  api.baseDir = baseDir;
@@ -83,6 +86,21 @@ const runBuild = async (userConfig, options = {}) => {
83
86
  const globalOutput = boundaryResult.outputs.find((output) => output.path === stylesheetPath);
84
87
  cssPath = stylesheetPath;
85
88
  cssBytes = Buffer.byteLength(globalOutput?.text ?? "");
89
+ } else if (runtimeGlobals === "file") {
90
+ await api.trigger("onMetaData", {
91
+ kind: "ai",
92
+ replace: true,
93
+ data: {
94
+ section: "boundaries",
95
+ title: "CSS boundaries",
96
+ content: formatBoundaryLines([], baseDir)
97
+ }
98
+ });
99
+ const text = api.css?.text ?? "";
100
+ await fs.mkdir(path.dirname(stylesheetPath), { recursive: true });
101
+ await fs.writeFile(stylesheetPath, text, "utf8");
102
+ cssPath = stylesheetPath;
103
+ cssBytes = Buffer.byteLength(text);
86
104
  } else await api.trigger("onMetaData", {
87
105
  kind: "ai",
88
106
  replace: true,
@@ -143,6 +143,11 @@ const getTokenPath = (api, prop, value) => {
143
143
  selectorValue: value
144
144
  };
145
145
  }
146
+ if (resolveTokenParts(values, value.split(".")) !== void 0) return {
147
+ path: `${groupName}.${value}`,
148
+ source: "prop",
149
+ values
150
+ };
146
151
  }
147
152
  if (value in values) return {
148
153
  path: `${groupName}.${value}`,
@@ -166,9 +171,22 @@ const resolveRuntimeToken = (api, prop, value) => {
166
171
  const runtimeOnly = api.runtime?.only === true;
167
172
  const hasServerToken = tokenPaths.has(tokenData.path);
168
173
  const tokenKey = tokenData.selectorValue ?? tokenData.path.split(".").slice(1).join(".");
169
- const varValue = `var(${`--${api.selectorPrefix ?? ""}${tokenData.path.replace(/\./g, "-")}`})`;
170
- const mixValue = tokenData.alpha === void 0 ? null : `color-mix(in oklab, ${hasServerToken && !runtimeOnly ? varValue : tokenValue} ${tokenData.alpha}%, transparent)`;
171
- if (runtimeOnly || !hasServerToken) return {
174
+ const varName = `--${api.selectorPrefix ?? ""}${tokenData.path.replace(/\./g, "-")}`;
175
+ const varValue = `var(${varName})`;
176
+ const mixValue = tokenData.alpha === void 0 ? null : `color-mix(in oklab, ${runtimeOnly || hasServerToken ? varValue : tokenValue} ${tokenData.alpha}%, transparent)`;
177
+ if (runtimeOnly) {
178
+ if (api.css && typeof api.css.addRoot === "function") {
179
+ const resolvedValue = api.dictionary.toValue(tokenValue);
180
+ if (resolvedValue !== void 0 && resolvedValue !== null) api.css.addRoot(`${varName}: ${resolvedValue};`);
181
+ }
182
+ return {
183
+ value: mixValue ?? varValue,
184
+ selectorValue: tokenKey,
185
+ tokenKey,
186
+ tokenPath: tokenData.path
187
+ };
188
+ }
189
+ if (!hasServerToken) return {
172
190
  value: mixValue ?? tokenValue,
173
191
  selectorValue: tokenData.alpha === void 0 ? tokenValue : tokenKey,
174
192
  tokenKey,
@@ -143,6 +143,11 @@ const getTokenPath = (api, prop, value) => {
143
143
  selectorValue: value
144
144
  };
145
145
  }
146
+ if (resolveTokenParts(values, value.split(".")) !== void 0) return {
147
+ path: `${groupName}.${value}`,
148
+ source: "prop",
149
+ values
150
+ };
146
151
  }
147
152
  if (value in values) return {
148
153
  path: `${groupName}.${value}`,
@@ -166,9 +171,22 @@ const resolveRuntimeToken = (api, prop, value) => {
166
171
  const runtimeOnly = api.runtime?.only === true;
167
172
  const hasServerToken = tokenPaths.has(tokenData.path);
168
173
  const tokenKey = tokenData.selectorValue ?? tokenData.path.split(".").slice(1).join(".");
169
- const varValue = `var(${`--${api.selectorPrefix ?? ""}${tokenData.path.replace(/\./g, "-")}`})`;
170
- const mixValue = tokenData.alpha === void 0 ? null : `color-mix(in oklab, ${hasServerToken && !runtimeOnly ? varValue : tokenValue} ${tokenData.alpha}%, transparent)`;
171
- if (runtimeOnly || !hasServerToken) return {
174
+ const varName = `--${api.selectorPrefix ?? ""}${tokenData.path.replace(/\./g, "-")}`;
175
+ const varValue = `var(${varName})`;
176
+ const mixValue = tokenData.alpha === void 0 ? null : `color-mix(in oklab, ${runtimeOnly || hasServerToken ? varValue : tokenValue} ${tokenData.alpha}%, transparent)`;
177
+ if (runtimeOnly) {
178
+ if (api.css && typeof api.css.addRoot === "function") {
179
+ const resolvedValue = api.dictionary.toValue(tokenValue);
180
+ if (resolvedValue !== void 0 && resolvedValue !== null) api.css.addRoot(`${varName}: ${resolvedValue};`);
181
+ }
182
+ return {
183
+ value: mixValue ?? varValue,
184
+ selectorValue: tokenKey,
185
+ tokenKey,
186
+ tokenPath: tokenData.path
187
+ };
188
+ }
189
+ if (!hasServerToken) return {
172
190
  value: mixValue ?? tokenValue,
173
191
  selectorValue: tokenData.alpha === void 0 ? tokenValue : tokenKey,
174
192
  tokenKey,
@@ -79,6 +79,22 @@ const warnAlpha = (message) => {
79
79
  alphaWarnings.add(message);
80
80
  console.warn(`[boss-css] ${message}`);
81
81
  };
82
+ const emitAllTokenVars = (api) => {
83
+ if (emittedAllTokens) return;
84
+ emittedAllTokens = true;
85
+ hasTokens = true;
86
+ const addTokenVars = (prefix, value) => {
87
+ if (value && typeof value === "object" && !Array.isArray(value)) {
88
+ for (const [key, next] of Object.entries(value)) addTokenVars(prefix ? `${prefix}.${key}` : key, next);
89
+ return;
90
+ }
91
+ if (!prefix) return;
92
+ const varName = `--${api.selectorPrefix ?? ""}${prefix.replace(/\./g, "-")}`;
93
+ api.css.addRoot(`${varName}: ${api.dictionary.toValue(value)};`);
94
+ browserResolvedPaths.add(prefix);
95
+ };
96
+ for (const [group, value] of Object.entries(originals)) addTokenVars(group, value);
97
+ };
82
98
  const onBoot = async (api) => {
83
99
  hasTokens = false;
84
100
  hasProxyTokens = false;
@@ -230,21 +246,7 @@ const onPropTree = async (api, { tree, parser }) => {
230
246
  hasTokenOverrides = true;
231
247
  hasTokens = true;
232
248
  }
233
- if (api.emitAllTokens && !emittedAllTokens) {
234
- emittedAllTokens = true;
235
- hasTokens = true;
236
- const addTokenVars = (prefix, value) => {
237
- if (value && typeof value === "object" && !Array.isArray(value)) {
238
- for (const [key, next] of Object.entries(value)) addTokenVars(prefix ? `${prefix}.${key}` : key, next);
239
- return;
240
- }
241
- if (!prefix) return;
242
- const varName = `--${api.selectorPrefix ?? ""}${prefix.replace(/\./g, "-")}`;
243
- api.css.addRoot(`${varName}: ${api.dictionary.toValue(value)};`);
244
- browserResolvedPaths.add(prefix);
245
- };
246
- for (const [group, value] of Object.entries(originals)) addTokenVars(group, value);
247
- }
249
+ if (api.emitAllTokens) emitAllTokenVars(api);
248
250
  const groupsSizeBefore = usedTokenGroups.size;
249
251
  api.walkPropTree(tree, (name, prop) => {
250
252
  const descriptor = api.dictionary.get(name);
@@ -79,6 +79,22 @@ const warnAlpha = (message) => {
79
79
  alphaWarnings.add(message);
80
80
  console.warn(`[boss-css] ${message}`);
81
81
  };
82
+ const emitAllTokenVars = (api) => {
83
+ if (emittedAllTokens) return;
84
+ emittedAllTokens = true;
85
+ hasTokens = true;
86
+ const addTokenVars = (prefix, value) => {
87
+ if (value && typeof value === "object" && !Array.isArray(value)) {
88
+ for (const [key, next] of Object.entries(value)) addTokenVars(prefix ? `${prefix}.${key}` : key, next);
89
+ return;
90
+ }
91
+ if (!prefix) return;
92
+ const varName = `--${api.selectorPrefix ?? ""}${prefix.replace(/\./g, "-")}`;
93
+ api.css.addRoot(`${varName}: ${api.dictionary.toValue(value)};`);
94
+ browserResolvedPaths.add(prefix);
95
+ };
96
+ for (const [group, value] of Object.entries(originals)) addTokenVars(group, value);
97
+ };
82
98
  const onBoot = async (api) => {
83
99
  hasTokens = false;
84
100
  hasProxyTokens = false;
@@ -230,21 +246,7 @@ const onPropTree = async (api, { tree, parser }) => {
230
246
  hasTokenOverrides = true;
231
247
  hasTokens = true;
232
248
  }
233
- if (api.emitAllTokens && !emittedAllTokens) {
234
- emittedAllTokens = true;
235
- hasTokens = true;
236
- const addTokenVars = (prefix, value) => {
237
- if (value && typeof value === "object" && !Array.isArray(value)) {
238
- for (const [key, next] of Object.entries(value)) addTokenVars(prefix ? `${prefix}.${key}` : key, next);
239
- return;
240
- }
241
- if (!prefix) return;
242
- const varName = `--${api.selectorPrefix ?? ""}${prefix.replace(/\./g, "-")}`;
243
- api.css.addRoot(`${varName}: ${api.dictionary.toValue(value)};`);
244
- browserResolvedPaths.add(prefix);
245
- };
246
- for (const [group, value] of Object.entries(originals)) addTokenVars(group, value);
247
- }
249
+ if (api.emitAllTokens) emitAllTokenVars(api);
248
250
  const groupsSizeBefore = usedTokenGroups.size;
249
251
  api.walkPropTree(tree, (name, prop) => {
250
252
  const descriptor = api.dictionary.get(name);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "boss-css",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "description": "",
5
5
  "bin": {
6
6
  "boss": "./dist/cli/index.cjs",