@santi020k/lumen 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -26,8 +26,13 @@ lumen add Button --target elements
26
26
  lumen add scheduler
27
27
  lumen add scheduler --target react
28
28
  lumen add scheduler --target elements
29
+ lumen audit-tokens ./src
29
30
  ```
30
31
 
31
32
  Use `lumen add <component>` for a local Astro wrapper, `--target react` for a React wrapper, or
32
33
  `--target elements` for a custom-elements starter. Bundled recipes support the same Astro, React,
33
34
  and Elements targets.
35
+
36
+ Run `lumen audit-tokens [path]` before incremental adoption when an existing stylesheet may already
37
+ declare names such as `--surface`, `--ink`, or `--line`. The audit reports complete CSS colors that
38
+ are incompatible with Lumen's HSL-channel token format and exits non-zero when it finds conflicts.
package/dist/cli.js CHANGED
@@ -1,4 +1,7 @@
1
+ import { readdir, readFile, stat } from 'node:fs/promises';
2
+ import { resolve } from 'node:path';
1
3
  import { addLumenRegistryItem, getLumenRegistryEntries, getLumenRegistryItem, loadLumenRegistry, lumenRegistry } from './registry.js';
4
+ import { auditLumenTokenCss } from './token-audit.js';
2
5
  const args = process.argv.slice(2);
3
6
  const [command = 'help', name] = args;
4
7
  const dryRun = args.includes('--dry-run');
@@ -83,6 +86,34 @@ const formatAddResult = async (itemName, registry) => {
83
86
  ...result.skipped.map(file => `- skipped existing ${file}`)
84
87
  ].join('\n');
85
88
  };
89
+ const getCssFiles = async (path) => {
90
+ const details = await stat(path);
91
+ if (details.isFile())
92
+ return path.endsWith('.css') ? [path] : [];
93
+ const entries = await readdir(path, { withFileTypes: true });
94
+ const nested = await Promise.all(entries
95
+ .filter(entry => entry.name !== 'node_modules' && !entry.name.startsWith('.'))
96
+ .map(entry => getCssFiles(resolve(path, entry.name))));
97
+ return nested.flat();
98
+ };
99
+ const formatTokenAudit = async (inputPath) => {
100
+ const target = resolve(inputPath);
101
+ const files = await getCssFiles(target);
102
+ const findings = [];
103
+ for (const file of files) {
104
+ findings.push(...auditLumenTokenCss(await readFile(file, 'utf8'), file));
105
+ }
106
+ if (!findings.length)
107
+ return `No incompatible Lumen semantic token declarations found in ${target}.`;
108
+ process.exitCode = 1;
109
+ return [
110
+ `Found ${findings.length} incompatible Lumen semantic token declaration${findings.length === 1 ? '' : 's'}:`,
111
+ ...findings.map(finding => (`${finding.file}:${finding.line}:${finding.column} --${finding.token}: ${finding.value}`)),
112
+ '',
113
+ 'Lumen semantic colors must be HSL channels such as "220 13% 96%", not complete colors such as "#fff" or "hsl(...)".',
114
+ 'Rename legacy variables during incremental migration or convert their values to channels before importing Lumen.'
115
+ ].join('\n');
116
+ };
86
117
  const help = [
87
118
  'Lumen registry helper',
88
119
  '',
@@ -91,6 +122,7 @@ const help = [
91
122
  ' lumen show <name> Show components and files for an item',
92
123
  ' lumen add <name> Install a recipe or component into the current project',
93
124
  ' lumen install Print install commands',
125
+ ' lumen audit-tokens Report incompatible semantic color custom properties',
94
126
  '',
95
127
  'Options:',
96
128
  ' --cwd <path> Target directory for lumen add',
@@ -103,12 +135,17 @@ const help = [
103
135
  ' --target <framework> Generate wrappers or recipe starters for astro, react, or elements',
104
136
  ''
105
137
  ].join('\n');
138
+ // eslint-disable-next-line complexity -- Command dispatch keeps CLI exit behavior and output in one place.
106
139
  const run = async () => {
107
140
  let output = help;
108
141
  const registry = registrySource
109
142
  ? await loadLumenRegistry(registrySource, registryToken ? { token: registryToken } : {})
110
143
  : lumenRegistry;
111
144
  switch (command) {
145
+ case 'audit-tokens': {
146
+ output = await formatTokenAudit(name ?? cwd ?? process.cwd());
147
+ break;
148
+ }
112
149
  case 'add': {
113
150
  if (name)
114
151
  output = await formatAddResult(name, registry);
package/dist/index.d.ts CHANGED
@@ -5,4 +5,5 @@ export declare const lumen: {
5
5
  readonly packageName: "@santi020k/lumen";
6
6
  readonly scope: "@santi020k";
7
7
  };
8
+ export { auditLumenTokenCss, type LumenTokenAuditFinding } from './token-audit.js';
8
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,oBAAoB,EACpB,iBAAiB,EACjB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,aAAa,EACb,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC9B,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,8BAA8B,EAC9B,sBAAsB,EACtB,wBAAwB,EACxB,gBAAgB,EAChB,wBAAwB,EACxB,0BAA0B,EAC1B,2BAA2B,EAC3B,mBAAmB,EACnB,wBAAwB,EACxB,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,EACxB,kBAAkB,EAClB,4BAA4B,EAC5B,qBAAqB,EACrB,uBAAuB,EACvB,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,EACpB,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,KAAK,cAAc,EACnB,wBAAwB,EACxB,KAAK,kBAAkB,EACvB,WAAW,EACX,KAAK,mBAAmB,EACxB,oBAAoB,EACpB,KAAK,kBAAkB,EACvB,mBAAmB,EACnB,KAAK,kBAAkB,EACvB,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,0BAA0B,EAC/B,KAAK,kBAAkB,EACvB,UAAU,EACV,KAAK,wBAAwB,EAC7B,yBAAyB,EACzB,KAAK,yBAAyB,EAC9B,0BAA0B,EAC1B,KAAK,mBAAmB,EACxB,oBAAoB,EACpB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,eAAe,EACf,aAAa,EACb,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACvB,KAAK,2BAA2B,EAChC,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,EAChC,KAAK,uBAAuB,EAC5B,KAAK,0BAA0B,EAC/B,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,2BAA2B,EAChC,4BAA4B,EAC5B,KAAK,kBAAkB,EACvB,mBAAmB,EACnB,KAAK,6BAA6B,EAClC,KAAK,qBAAqB,EAC1B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,EAC5B,KAAK,4BAA4B,EACjC,KAAK,gBAAgB,EACrB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAClB,wBAAwB,EACxB,wBAAwB,EACxB,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,sBAAsB,EACtB,uBAAuB,EACvB,eAAe,EACf,kBAAkB,EAClB,oBAAoB,EACpB,uBAAuB,EACvB,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACpB,MAAM,uBAAuB,CAAA;AAE9B,eAAO,MAAM,KAAK;;;;CAIR,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,oBAAoB,EACpB,iBAAiB,EACjB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,aAAa,EACb,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC9B,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,8BAA8B,EAC9B,sBAAsB,EACtB,wBAAwB,EACxB,gBAAgB,EAChB,wBAAwB,EACxB,0BAA0B,EAC1B,2BAA2B,EAC3B,mBAAmB,EACnB,wBAAwB,EACxB,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,EACxB,kBAAkB,EAClB,4BAA4B,EAC5B,qBAAqB,EACrB,uBAAuB,EACvB,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,EACpB,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,KAAK,cAAc,EACnB,wBAAwB,EACxB,KAAK,kBAAkB,EACvB,WAAW,EACX,KAAK,mBAAmB,EACxB,oBAAoB,EACpB,KAAK,kBAAkB,EACvB,mBAAmB,EACnB,KAAK,kBAAkB,EACvB,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,0BAA0B,EAC/B,KAAK,kBAAkB,EACvB,UAAU,EACV,KAAK,wBAAwB,EAC7B,yBAAyB,EACzB,KAAK,yBAAyB,EAC9B,0BAA0B,EAC1B,KAAK,mBAAmB,EACxB,oBAAoB,EACpB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,eAAe,EACf,aAAa,EACb,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACvB,KAAK,2BAA2B,EAChC,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,EAChC,KAAK,uBAAuB,EAC5B,KAAK,0BAA0B,EAC/B,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,2BAA2B,EAChC,4BAA4B,EAC5B,KAAK,kBAAkB,EACvB,mBAAmB,EACnB,KAAK,6BAA6B,EAClC,KAAK,qBAAqB,EAC1B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,EAC5B,KAAK,4BAA4B,EACjC,KAAK,gBAAgB,EACrB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAClB,wBAAwB,EACxB,wBAAwB,EACxB,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,sBAAsB,EACtB,uBAAuB,EACvB,eAAe,EACf,kBAAkB,EAClB,oBAAoB,EACpB,uBAAuB,EACvB,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACpB,MAAM,uBAAuB,CAAA;AAE9B,eAAO,MAAM,KAAK;;;;CAIR,CAAA;AAEV,OAAO,EACL,kBAAkB,EAClB,KAAK,sBAAsB,EAC5B,MAAM,kBAAkB,CAAA"}
package/dist/index.js CHANGED
@@ -5,3 +5,4 @@ export const lumen = {
5
5
  packageName: '@santi020k/lumen',
6
6
  scope: '@santi020k'
7
7
  };
8
+ export { auditLumenTokenCss } from './token-audit.js';
@@ -39,7 +39,7 @@ export declare const lumenRegistry: {
39
39
  readonly components: readonly [{
40
40
  readonly name: "Accordion";
41
41
  readonly type: "component";
42
- readonly description: "Stacks collapsible content sections.";
42
+ readonly description: "Stacks collapsible content sections as cards or a compact flush list.";
43
43
  readonly category: "Layout";
44
44
  readonly files: readonly ["packages/astro/components/Accordion.astro", "packages/astro/styles/lumen.css"];
45
45
  readonly dependencies: readonly ["styles"];
@@ -697,19 +697,22 @@ export declare const lumenRegistry: {
697
697
  readonly type: "component";
698
698
  readonly category: "Data display";
699
699
  readonly description: "Animates a formatted metric while keeping one stable accessible value.";
700
- readonly files: readonly ["packages/astro/components/AnimatedNumber.astro", "packages/react/src/components.tsx"];
700
+ readonly files: readonly ["packages/astro/components/AnimatedNumber.astro", "packages/astro/runtime/UIPrimitives.astro", "packages/astro/styles/lumen.css", "packages/react/src/components.tsx"];
701
+ readonly dependencies: readonly ["styles", "runtime"];
701
702
  }, {
702
703
  readonly name: "RevealGroup";
703
704
  readonly type: "component";
704
705
  readonly category: "Layout";
705
706
  readonly description: "Reveals a group of direct children with tokenized, reduced-motion-aware staggering.";
706
- readonly files: readonly ["packages/astro/components/RevealGroup.astro", "packages/react/src/components.tsx"];
707
+ readonly files: readonly ["packages/astro/components/RevealGroup.astro", "packages/astro/runtime/UIPrimitives.astro", "packages/astro/styles/lumen.css", "packages/react/src/components.tsx"];
708
+ readonly dependencies: readonly ["styles", "runtime"];
707
709
  }, {
708
710
  readonly name: "ScrollReveal";
709
711
  readonly type: "component";
710
712
  readonly category: "Layout";
711
- readonly description: "A Scroll Reveal component.";
712
- readonly files: readonly ["packages/astro/components/ScrollReveal.astro", "packages/react/src/components.tsx"];
713
+ readonly description: "Animates wrapped content as it first enters the viewport.";
714
+ readonly files: readonly ["packages/astro/components/ScrollReveal.astro", "packages/astro/runtime/UIPrimitives.astro", "packages/astro/styles/lumen.css", "packages/react/src/components.tsx"];
715
+ readonly dependencies: readonly ["styles", "runtime"];
713
716
  }, {
714
717
  readonly name: "Stat";
715
718
  readonly type: "component";
@@ -1 +1 @@
1
- {"version":3,"file":"registry-data.d.ts","sourceRoot":"","sources":["../src/registry-data.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuqDQ,CAAA"}
1
+ {"version":3,"file":"registry-data.d.ts","sourceRoot":"","sources":["../src/registry-data.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyrDQ,CAAA"}
@@ -98,7 +98,7 @@ export const lumenRegistry = {
98
98
  {
99
99
  "name": "Accordion",
100
100
  "type": "component",
101
- "description": "Stacks collapsible content sections.",
101
+ "description": "Stacks collapsible content sections as cards or a compact flush list.",
102
102
  "category": "Layout",
103
103
  "files": [
104
104
  "packages/astro/components/Accordion.astro",
@@ -1417,7 +1417,13 @@ export const lumenRegistry = {
1417
1417
  "description": "Animates a formatted metric while keeping one stable accessible value.",
1418
1418
  "files": [
1419
1419
  "packages/astro/components/AnimatedNumber.astro",
1420
+ "packages/astro/runtime/UIPrimitives.astro",
1421
+ "packages/astro/styles/lumen.css",
1420
1422
  "packages/react/src/components.tsx"
1423
+ ],
1424
+ "dependencies": [
1425
+ "styles",
1426
+ "runtime"
1421
1427
  ]
1422
1428
  },
1423
1429
  {
@@ -1427,17 +1433,29 @@ export const lumenRegistry = {
1427
1433
  "description": "Reveals a group of direct children with tokenized, reduced-motion-aware staggering.",
1428
1434
  "files": [
1429
1435
  "packages/astro/components/RevealGroup.astro",
1436
+ "packages/astro/runtime/UIPrimitives.astro",
1437
+ "packages/astro/styles/lumen.css",
1430
1438
  "packages/react/src/components.tsx"
1439
+ ],
1440
+ "dependencies": [
1441
+ "styles",
1442
+ "runtime"
1431
1443
  ]
1432
1444
  },
1433
1445
  {
1434
1446
  "name": "ScrollReveal",
1435
1447
  "type": "component",
1436
1448
  "category": "Layout",
1437
- "description": "A Scroll Reveal component.",
1449
+ "description": "Animates wrapped content as it first enters the viewport.",
1438
1450
  "files": [
1439
1451
  "packages/astro/components/ScrollReveal.astro",
1452
+ "packages/astro/runtime/UIPrimitives.astro",
1453
+ "packages/astro/styles/lumen.css",
1440
1454
  "packages/react/src/components.tsx"
1455
+ ],
1456
+ "dependencies": [
1457
+ "styles",
1458
+ "runtime"
1441
1459
  ]
1442
1460
  },
1443
1461
  {
@@ -0,0 +1,9 @@
1
+ export interface LumenTokenAuditFinding {
2
+ column: number;
3
+ file: string;
4
+ line: number;
5
+ token: string;
6
+ value: string;
7
+ }
8
+ export declare const auditLumenTokenCss: (source: string, file?: string) => LumenTokenAuditFinding[];
9
+ //# sourceMappingURL=token-audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-audit.d.ts","sourceRoot":"","sources":["../src/token-audit.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd;AAMD,eAAO,MAAM,kBAAkB,GAAI,QAAQ,MAAM,EAAE,aAAgB,KAAG,sBAAsB,EAwB3F,CAAA"}
@@ -0,0 +1,25 @@
1
+ import { lumenSemanticColorTokenNames } from '@santi020k/lumen-core';
2
+ const declarationPattern = /--([a-z][\w-]*)\s*:\s*([^;}\n]+)/giu;
3
+ const channelPattern = /^(?:var\(.+\)|-?(?:\d+(?:\.\d+)?|\.\d+)(?:deg|grad|rad|turn)?\s+-?(?:\d+(?:\.\d+)?|\.\d+)%\s+-?(?:\d+(?:\.\d+)?|\.\d+)%(?:\s*\/\s*(?:\d+(?:\.\d+)?|\.\d+)%?)?)$/iu;
4
+ const semanticTokens = new Set(lumenSemanticColorTokenNames);
5
+ export const auditLumenTokenCss = (source, file = '<input>') => {
6
+ const findings = [];
7
+ for (const match of source.matchAll(declarationPattern)) {
8
+ const token = match[1];
9
+ const value = match[2]?.trim();
10
+ if (!token || !value || !semanticTokens.has(token) || channelPattern.test(value))
11
+ continue;
12
+ const index = match.index;
13
+ const before = source.slice(0, index);
14
+ const line = before.split('\n').length;
15
+ const lineStart = before.lastIndexOf('\n') + 1;
16
+ findings.push({
17
+ column: index - lineStart + 1,
18
+ file,
19
+ line,
20
+ token,
21
+ value
22
+ });
23
+ }
24
+ return findings;
25
+ };
package/layers.css ADDED
@@ -0,0 +1,5 @@
1
+ /*
2
+ * Import before Tailwind and Lumen so Tailwind base styles run before Lumen components while
3
+ * Tailwind utilities remain able to override component presentation.
4
+ */
5
+ @layer theme, base, lumen.tokens, lumen.base, components, lumen.components, lumen.glass, utilities;
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@santi020k/lumen",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
7
  "description": "Umbrella package for the Lumen multi-framework primitive UI system.",
8
8
  "type": "module",
9
9
  "sideEffects": [
10
+ "./layers.css",
10
11
  "./styles.css"
11
12
  ],
12
13
  "main": "./dist/index.js",
@@ -22,11 +23,13 @@
22
23
  "import": "./dist/registry.js"
23
24
  },
24
25
  "./styles.css": "./styles.css",
26
+ "./layers.css": "./layers.css",
25
27
  "./package.json": "./package.json"
26
28
  },
27
29
  "files": [
28
30
  "bin",
29
31
  "dist",
32
+ "layers.css",
30
33
  "styles.css",
31
34
  "README.md",
32
35
  "LICENSE"
@@ -35,16 +38,17 @@
35
38
  "lumen": "./bin/lumen.mjs"
36
39
  },
37
40
  "dependencies": {
38
- "@santi020k/lumen-core": "^0.1.0"
41
+ "@santi020k/lumen-core": "^0.2.0"
39
42
  },
40
43
  "devDependencies": {
41
44
  "@types/node": "^26.1.1",
42
45
  "@vitest/coverage-v8": "^4.1.10",
43
46
  "typescript": "^6.0.3",
47
+ "tailwindcss": "^4.3.3",
44
48
  "vitest": "^4.1.10"
45
49
  },
46
50
  "engines": {
47
- "node": ">=22.18.0"
51
+ "node": ">=22.12.0"
48
52
  },
49
53
  "repository": {
50
54
  "type": "git",
package/styles.css CHANGED
@@ -623,6 +623,11 @@
623
623
  background: hsl(var(--surface-muted) / 0.72);
624
624
  }
625
625
 
626
+ .ui-card--unstyled,
627
+ .ui-button-link--unstyled {
628
+ all: revert-layer;
629
+ }
630
+
626
631
  .ui-card--glass.ui-card--interactive:hover,
627
632
  .ui-card--glass.card-interactive:hover {
628
633
  border-color: hsl(var(--glass-edge));
@@ -881,6 +886,15 @@
881
886
  color: hsl(var(--brand-solid));
882
887
  }
883
888
 
889
+ .ui-link--inherit {
890
+ color: inherit;
891
+ font-weight: inherit;
892
+ }
893
+
894
+ .ui-link--inherit:hover {
895
+ color: inherit;
896
+ }
897
+
884
898
  .ui-prose {
885
899
  max-width: 65ch;
886
900
  color: hsl(var(--ink-soft));
@@ -1714,13 +1728,64 @@
1714
1728
  padding: 0.85rem 1rem;
1715
1729
  }
1716
1730
 
1731
+ .ui-accordion--flush {
1732
+ gap: 0;
1733
+ border-top: 1px solid hsl(var(--line));
1734
+ }
1735
+
1736
+ .ui-accordion--flush details {
1737
+ border: 0;
1738
+ border-bottom: 1px solid hsl(var(--line));
1739
+ border-radius: 0;
1740
+ background: transparent;
1741
+ padding: 1.25rem 0;
1742
+ }
1743
+
1717
1744
  .ui-accordion summary,
1718
1745
  .ui-collapsible summary {
1746
+ display: grid;
1747
+ grid-template-columns: minmax(0, 1fr) auto;
1748
+ align-items: center;
1749
+ gap: 1rem;
1719
1750
  cursor: pointer;
1751
+ list-style: none;
1720
1752
  color: hsl(var(--ink));
1721
1753
  font-weight: 650;
1722
1754
  }
1723
1755
 
1756
+ .ui-accordion summary::-webkit-details-marker,
1757
+ .ui-collapsible summary::-webkit-details-marker {
1758
+ display: none;
1759
+ }
1760
+
1761
+ .ui-accordion summary::after,
1762
+ .ui-collapsible summary::after {
1763
+ width: 0.5rem;
1764
+ height: 0.5rem;
1765
+ border-right: 1.5px solid hsl(var(--ink-muted));
1766
+ border-bottom: 1.5px solid hsl(var(--ink-muted));
1767
+ content: "";
1768
+ transform: rotate(45deg) translate(-0.1rem, -0.1rem);
1769
+ transition: transform var(--ui-duration-fast) var(--ui-ease-standard);
1770
+ }
1771
+
1772
+ .ui-accordion details[open] > summary::after,
1773
+ .ui-collapsible[open] > summary::after {
1774
+ transform: rotate(225deg) translate(-0.1rem, -0.1rem);
1775
+ }
1776
+
1777
+ .ui-accordion summary:hover::after,
1778
+ .ui-collapsible summary:hover::after {
1779
+ border-color: hsl(var(--brand));
1780
+ }
1781
+
1782
+ .ui-accordion summary:focus-visible,
1783
+ .ui-collapsible summary:focus-visible {
1784
+ border-radius: calc(var(--ui-radius) * 0.6);
1785
+ outline: 2px solid hsl(var(--brand));
1786
+ outline-offset: 0.35rem;
1787
+ }
1788
+
1724
1789
  .ui-accordion p,
1725
1790
  .ui-collapsible p {
1726
1791
  margin-block-end: 0;
@@ -1944,6 +2009,19 @@
1944
2009
  color: hsl(var(--ink));
1945
2010
  }
1946
2011
 
2012
+ .ui-navigation-menu--unstyled {
2013
+ all: revert-layer;
2014
+ }
2015
+
2016
+ .ui-navigation-menu--unstyled :where(a, button) {
2017
+ all: revert-layer;
2018
+ }
2019
+
2020
+ .ui-navigation-menu--unstyled :where(a, button):hover,
2021
+ .ui-navigation-menu--unstyled :where(a, button)[aria-current="page"] {
2022
+ all: revert-layer;
2023
+ }
2024
+
1947
2025
  .ui-breadcrumb {
1948
2026
  color: hsl(var(--ink-muted));
1949
2027
  font-size: 0.875rem;
@@ -2314,6 +2392,16 @@
2314
2392
  color: hsl(var(--brand-solid));
2315
2393
  }
2316
2394
 
2395
+ .ui-sidebar--unstyled {
2396
+ all: revert-layer;
2397
+ }
2398
+
2399
+ .ui-sidebar--unstyled a,
2400
+ .ui-sidebar--unstyled a:hover,
2401
+ .ui-sidebar--unstyled a[aria-current="page"] {
2402
+ all: revert-layer;
2403
+ }
2404
+
2317
2405
  .ui-empty {
2318
2406
  display: grid;
2319
2407
  place-items: center;
@@ -2452,6 +2540,9 @@
2452
2540
  .ui-code--block {
2453
2541
  position: relative;
2454
2542
  overflow: hidden;
2543
+ width: 100%;
2544
+ max-width: 100%;
2545
+ min-width: 0;
2455
2546
  margin: 0;
2456
2547
  border: 1px solid var(--ui-code-border);
2457
2548
  border-radius: var(--ui-radius-lg);
@@ -2601,6 +2692,16 @@
2601
2692
  white-space: pre;
2602
2693
  }
2603
2694
 
2695
+ .ui-code--wrap :where(pre) {
2696
+ overflow-x: hidden;
2697
+ }
2698
+
2699
+ .ui-code--wrap :where(code) {
2700
+ min-width: 0;
2701
+ overflow-wrap: anywhere;
2702
+ white-space: pre-wrap;
2703
+ }
2704
+
2604
2705
  .ui-code--block :where(code span) {
2605
2706
  color: var(--shiki-light, var(--ui-code-token, var(--ui-code-fg)));
2606
2707
  font-style: var(--shiki-light-font-style, var(--ui-code-token-font-style, normal));
@@ -4584,6 +4685,16 @@
4584
4685
  font-variant-numeric: tabular-nums;
4585
4686
  }
4586
4687
 
4688
+ a.ui-pill {
4689
+ cursor: pointer;
4690
+ text-decoration: none;
4691
+ }
4692
+
4693
+ a.ui-pill:hover {
4694
+ border-color: hsl(var(--brand) / 0.42);
4695
+ color: hsl(var(--brand-solid));
4696
+ }
4697
+
4587
4698
  /* CoverImage hover lift + bottom gradient overlay */
4588
4699
  .ui-cover-image--hover {
4589
4700
  transition: transform 300ms ease;