@ethlete/core 5.0.0-next.44 → 5.0.0-next.46

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.
@@ -0,0 +1,113 @@
1
+ import { formatFiles, joinPathFragments, logger, readProjectConfiguration, updateProjectConfiguration, visitNotIgnoredFiles, } from '@nx/devkit';
2
+ const SCRIPT_PATH = 'tools/generate-build-info.js';
3
+ const SCRIPT = `const { execSync } = require('child_process');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+
7
+ const [, , outputPath, packageJsonPath] = process.argv;
8
+
9
+ const read = (command) => {
10
+ try {
11
+ return execSync(command, { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
12
+ } catch {
13
+ return null;
14
+ }
15
+ };
16
+
17
+ const version = fs.existsSync(packageJsonPath)
18
+ ? JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')).version
19
+ : null;
20
+
21
+ const info = {
22
+ version: version ?? 'unknown',
23
+ sha: read('git rev-parse --short HEAD') ?? 'unknown',
24
+ branch: read('git rev-parse --abbrev-ref HEAD') ?? 'unknown',
25
+ builtAt: new Date().toISOString(),
26
+ };
27
+
28
+ const contents =
29
+ '// Generated by ' +
30
+ ${JSON.stringify(SCRIPT_PATH)} +
31
+ ' - do not edit.\\n\\nexport const APP_BUILD_INFO = ' +
32
+ JSON.stringify(info, null, 2) +
33
+ ' as const;\\n';
34
+
35
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
36
+ fs.writeFileSync(outputPath, contents);
37
+ `;
38
+ const PLACEHOLDER = `// Generated by ${SCRIPT_PATH} - do not edit.
39
+
40
+ export const APP_BUILD_INFO = {
41
+ version: 'unknown',
42
+ sha: 'unknown',
43
+ branch: 'unknown',
44
+ builtAt: '1970-01-01T00:00:00.000Z',
45
+ } as const;
46
+ `;
47
+ const relativeImport = (from, to) => {
48
+ const fromParts = from.split('/').slice(0, -1);
49
+ const toParts = to.replace(/\.ts$/, '').split('/');
50
+ while (fromParts.length && toParts.length && fromParts[0] === toParts[0]) {
51
+ fromParts.shift();
52
+ toParts.shift();
53
+ }
54
+ const up = fromParts.length ? fromParts.map(() => '..') : ['.'];
55
+ return [...up, ...toParts].join('/');
56
+ };
57
+ /** Wires the `provideQueryDevtools({ about })` call up to the generated constant, if it can be found. */
58
+ const wireProvider = (tree, projectRoot, outputPath) => {
59
+ let wired = false;
60
+ visitNotIgnoredFiles(tree, projectRoot, (file) => {
61
+ if (wired || !file.endsWith('.ts'))
62
+ return;
63
+ const contents = tree.read(file, 'utf-8');
64
+ if (!contents?.includes('provideQueryDevtools('))
65
+ return;
66
+ if (!contents.includes('provideQueryDevtools()')) {
67
+ logger.warn(`${file} already passes options to provideQueryDevtools() - add \`about: APP_BUILD_INFO\` to them yourself.`);
68
+ wired = true;
69
+ return;
70
+ }
71
+ const importPath = relativeImport(file, outputPath);
72
+ tree.write(file, `import { APP_BUILD_INFO } from '${importPath}';\n` +
73
+ contents.replace('provideQueryDevtools()', 'provideQueryDevtools({ about: APP_BUILD_INFO })'));
74
+ wired = true;
75
+ });
76
+ return wired;
77
+ };
78
+ export const devtoolsAboutGenerator = async (tree, options) => {
79
+ const project = readProjectConfiguration(tree, options.project);
80
+ const outputPath = options.outputPath ?? joinPathFragments(project.sourceRoot ?? project.root, 'build-info.ts');
81
+ if (!tree.exists(SCRIPT_PATH))
82
+ tree.write(SCRIPT_PATH, SCRIPT);
83
+ if (!tree.exists(outputPath))
84
+ tree.write(outputPath, PLACEHOLDER);
85
+ const gitignore = tree.read('.gitignore', 'utf-8') ?? '';
86
+ // Regenerated on every build, and its SHA changes with every commit - committing it would put a
87
+ // one-line diff in every commit that says nothing about the change.
88
+ if (!gitignore.includes(outputPath))
89
+ tree.write('.gitignore', `${gitignore.trimEnd()}\n${outputPath}\n`);
90
+ project.targets ??= {};
91
+ project.targets['build-info'] = {
92
+ executor: 'nx:run-commands',
93
+ options: {
94
+ commands: [`node ${SCRIPT_PATH} ${outputPath} ${joinPathFragments(project.root, 'package.json')}`],
95
+ },
96
+ };
97
+ for (const target of ['build', 'serve']) {
98
+ const config = project.targets[target];
99
+ if (!config)
100
+ continue;
101
+ config.dependsOn ??= [];
102
+ if (!config.dependsOn.includes('build-info'))
103
+ config.dependsOn.push('build-info');
104
+ }
105
+ updateProjectConfiguration(tree, options.project, project);
106
+ if (!wireProvider(tree, project.root, outputPath)) {
107
+ logger.warn(`No provideQueryDevtools() call found in ${project.root} - pass \`about: APP_BUILD_INFO\` to it yourself.`);
108
+ }
109
+ if (!options.skipFormat)
110
+ await formatFiles(tree);
111
+ };
112
+ export default devtoolsAboutGenerator;
113
+ //# sourceMappingURL=generator.js.map
@@ -0,0 +1,25 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "$id": "devtools-about",
4
+ "title": "Query devtools About generator",
5
+ "description": "Generates the application's build info (version, commit SHA, branch, build time) and wires it into provideQueryDevtools({ about })",
6
+ "type": "object",
7
+ "properties": {
8
+ "project": {
9
+ "type": "string",
10
+ "description": "The application to wire up",
11
+ "$default": { "$source": "argv", "index": 0 },
12
+ "x-prompt": "Which application should report its build info?"
13
+ },
14
+ "outputPath": {
15
+ "type": "string",
16
+ "description": "Where to write the generated build-info file. Defaults to `<sourceRoot>/build-info.ts`."
17
+ },
18
+ "skipFormat": {
19
+ "type": "boolean",
20
+ "description": "Skip formatting files after generation",
21
+ "default": false
22
+ }
23
+ },
24
+ "required": ["project"]
25
+ }
@@ -10,6 +10,11 @@
10
10
  "schema": "./migrate-provider-shape/schema.json",
11
11
  "description": "Rewrite destructured provider tuples into the tree-shakeable definition shape"
12
12
  },
13
+ "migrate-surface-interaction-swatch": {
14
+ "factory": "./migrate-surface-interaction-swatch/migration",
15
+ "schema": "./migrate-surface-interaction-swatch/schema.json",
16
+ "description": "Rewrite surface theme interactionColor maps into the { color, onColor, inkColor } swatch shape"
17
+ },
13
18
  "tailwind-4-color-theme": {
14
19
  "factory": "./tailwind-4-color-theme/generator",
15
20
  "schema": "./tailwind-4-color-theme/schema.json",
@@ -19,6 +24,11 @@
19
24
  "factory": "./tailwind-4-surface-theme/generator",
20
25
  "schema": "./tailwind-4-surface-theme/schema.json",
21
26
  "description": "Generate surface theming for Tailwind CSS v4"
27
+ },
28
+ "devtools-about": {
29
+ "factory": "./devtools-about/generator",
30
+ "schema": "./devtools-about/schema.json",
31
+ "description": "Generate the app's build info and wire it into provideQueryDevtools({ about })"
22
32
  }
23
33
  }
24
34
  }
@@ -0,0 +1,43 @@
1
+ import { Project, SyntaxKind } from 'ts-morph';
2
+ const INTERACTION_KEYS = ['default', 'hover', 'focus', 'active', 'disabled'];
3
+ const createProject = () => new Project({
4
+ useInMemoryFileSystem: true,
5
+ compilerOptions: {
6
+ target: 99,
7
+ module: 99,
8
+ },
9
+ });
10
+ const isFlatInteractionColor = (initializer) => initializer
11
+ .getProperties()
12
+ .some((property) => property.isKind(SyntaxKind.PropertyAssignment) && INTERACTION_KEYS.includes(property.getName()));
13
+ const findFlatInteractionColor = (sourceFile) => {
14
+ for (const property of sourceFile.getDescendantsOfKind(SyntaxKind.PropertyAssignment)) {
15
+ if (property.getName() !== 'interactionColor')
16
+ continue;
17
+ const initializer = property.getInitializer();
18
+ if (initializer?.isKind(SyntaxKind.ObjectLiteralExpression) && isFlatInteractionColor(initializer)) {
19
+ return initializer;
20
+ }
21
+ }
22
+ return null;
23
+ };
24
+ /**
25
+ * Rewrites `interactionColor: { default, hover, … }` into `interactionColor: { color: { default, … } }`,
26
+ * the swatch shape that also carries the optional `onColor` and `inkColor` maps.
27
+ */
28
+ export const migrateInteractionSwatchInFile = (filePath, content) => {
29
+ if (!content.includes('interactionColor')) {
30
+ return { changed: false, content };
31
+ }
32
+ const sourceFile = createProject().createSourceFile(filePath, content);
33
+ let changed = false;
34
+ // Re-queried after every rewrite: manipulating the AST forgets the nodes a single up-front
35
+ // collection would have handed us.
36
+ for (let initializer = findFlatInteractionColor(sourceFile); initializer;) {
37
+ initializer.replaceWithText(`{ color: ${initializer.getText()} }`);
38
+ changed = true;
39
+ initializer = findFlatInteractionColor(sourceFile);
40
+ }
41
+ return { changed, content: changed ? sourceFile.getFullText() : content };
42
+ };
43
+ //# sourceMappingURL=interaction-swatch.js.map
@@ -0,0 +1,29 @@
1
+ import { formatFiles } from '@nx/devkit';
2
+ import { createMigrationScope } from '../migrate-provider-shape/migration-scope';
3
+ import { migrateInteractionSwatchInFile } from './interaction-swatch';
4
+ export default async function migrateSurfaceInteractionSwatch(tree, schema) {
5
+ console.log('\n🔄 Migrating surface interactionColor to the swatch shape...');
6
+ const scope = createMigrationScope(tree, schema);
7
+ console.log(` Scope: ${scope.describe()}`);
8
+ let filesChanged = 0;
9
+ scope.visit(tree, (filePath) => {
10
+ if (!filePath.endsWith('.ts') || filePath.endsWith('.d.ts'))
11
+ return;
12
+ const before = tree.read(filePath, 'utf-8');
13
+ if (!before)
14
+ return;
15
+ const result = migrateInteractionSwatchInFile(filePath, before);
16
+ if (!result.changed)
17
+ return;
18
+ tree.write(filePath, result.content);
19
+ filesChanged++;
20
+ console.log(` ✓ ${filePath}`);
21
+ });
22
+ console.log(filesChanged === 0
23
+ ? ' Nothing to migrate.\n'
24
+ : `\n✅ Migrated ${filesChanged} file(s). Regenerate your surface CSS with \`nx g @ethlete/core:tailwind-4-surface-theme\`.\n`);
25
+ if (!schema.skipFormat) {
26
+ await formatFiles(tree);
27
+ }
28
+ }
29
+ //# sourceMappingURL=migration.js.map
@@ -0,0 +1,26 @@
1
+ {
2
+ "$schema": "http://json-schema.org/schema",
3
+ "$id": "migrate-surface-interaction-swatch",
4
+ "title": "Migrate surface interactionColor to the swatch shape",
5
+ "type": "object",
6
+ "properties": {
7
+ "projects": {
8
+ "type": "array",
9
+ "items": { "type": "string" },
10
+ "description": "Nx projects to migrate. Defaults to every project in the workspace.",
11
+ "alias": "project"
12
+ },
13
+ "include": {
14
+ "type": "array",
15
+ "items": { "type": "string" },
16
+ "description": "Path prefixes to migrate, relative to the workspace root (e.g. 'apps/web'). Unioned with `projects`. Defaults to the whole workspace.",
17
+ "alias": "includePath"
18
+ },
19
+ "skipFormat": {
20
+ "type": "boolean",
21
+ "description": "Skip formatting files after migration",
22
+ "default": false
23
+ }
24
+ },
25
+ "required": []
26
+ }
@@ -158,9 +158,52 @@ const COLOR_PROPS = ['background', 'color', 'colorMuted', 'colorSubtle', 'border
158
158
  const isColorProp = (name) => COLOR_PROPS.some((p) => p === name);
159
159
  const INTERACTION_KEYS = ['default', 'hover', 'focus', 'active', 'disabled'];
160
160
  const isInteractionKey = (name) => INTERACTION_KEYS.some((key) => key === name);
161
+ const SWATCH_KEYS = ['color', 'onColor', 'inkColor'];
162
+ const isSwatchKey = (name) => SWATCH_KEYS.some((key) => key === name);
163
+ function parseInteractionColorMap(obj) {
164
+ const map = {};
165
+ for (const prop of obj.getProperties()) {
166
+ if (!prop.isKind(SyntaxKind.PropertyAssignment))
167
+ continue;
168
+ const name = prop.getName();
169
+ const initializer = prop.getInitializer();
170
+ if (initializer?.isKind(SyntaxKind.StringLiteral) && isInteractionKey(name)) {
171
+ map[name] = initializer.getLiteralValue();
172
+ }
173
+ }
174
+ return map;
175
+ }
176
+ function parseInteractionColor(obj, themeName) {
177
+ const swatch = {};
178
+ for (const prop of obj.getProperties()) {
179
+ if (!prop.isKind(SyntaxKind.PropertyAssignment))
180
+ continue;
181
+ const name = prop.getName();
182
+ const initializer = prop.getInitializer();
183
+ if (isInteractionKey(name)) {
184
+ throw new Error(`Surface theme "${themeName}" uses the old flat \`interactionColor\` shape. It now takes a swatch: ` +
185
+ `{ color: { default, hover, focus, active, disabled }, onColor?, inkColor? }. ` +
186
+ `Run \`nx g @ethlete/core:migrate-surface-interaction-swatch\` to convert your themes.`);
187
+ }
188
+ if (!initializer?.isKind(SyntaxKind.ObjectLiteralExpression) || !isSwatchKey(name))
189
+ continue;
190
+ const map = parseInteractionColorMap(initializer);
191
+ if (name === 'color') {
192
+ if (map.default && map.hover && map.focus && map.active && map.disabled) {
193
+ swatch.color = map;
194
+ }
195
+ continue;
196
+ }
197
+ if (map.default) {
198
+ swatch[name] = map;
199
+ }
200
+ }
201
+ return swatch.color ? swatch : undefined;
202
+ }
161
203
  function parseSurfaceThemeObject(obj) {
162
204
  const properties = obj.getProperties();
163
205
  const theme = {};
206
+ let interactionColorObj = null;
164
207
  for (const prop of properties) {
165
208
  if (!prop.isKind(SyntaxKind.PropertyAssignment)) {
166
209
  continue;
@@ -177,24 +220,7 @@ function parseSurfaceThemeObject(obj) {
177
220
  continue;
178
221
  }
179
222
  if (propName === 'interactionColor' && initializer.isKind(SyntaxKind.ObjectLiteralExpression)) {
180
- const interactionProps = initializer.getProperties();
181
- const interactionColor = {};
182
- for (const iProp of interactionProps) {
183
- if (!iProp.isKind(SyntaxKind.PropertyAssignment))
184
- continue;
185
- const iPropName = iProp.getName();
186
- const iInit = iProp.getInitializer();
187
- if (iInit?.isKind(SyntaxKind.StringLiteral) && isInteractionKey(iPropName)) {
188
- interactionColor[iPropName] = iInit.getLiteralValue();
189
- }
190
- }
191
- if (interactionColor.default &&
192
- interactionColor.hover &&
193
- interactionColor.focus &&
194
- interactionColor.active &&
195
- interactionColor.disabled) {
196
- theme.interactionColor = interactionColor;
197
- }
223
+ interactionColorObj = initializer;
198
224
  continue;
199
225
  }
200
226
  if (propName === 'elevation') {
@@ -227,6 +253,9 @@ function parseSurfaceThemeObject(obj) {
227
253
  !theme.border) {
228
254
  throw new Error('SurfaceTheme must have name, type, elevation, background, color, colorMuted, colorSubtle, and border properties');
229
255
  }
256
+ if (interactionColorObj) {
257
+ theme.interactionColor = parseInteractionColor(interactionColorObj, theme.name);
258
+ }
230
259
  return theme;
231
260
  }
232
261
  // Makes the named theme the sole default of its surface type, overriding any isDefault
@@ -322,11 +351,14 @@ function generateSurfaceThemeCss(themes, utilityPrefix, runtimePrefix, schema) {
322
351
  tailwindVars.push(` --color-${utilityPrefix}-surface-${name}-subtle: rgb(${theme.colorSubtle});`);
323
352
  tailwindVars.push(` --color-${utilityPrefix}-surface-${name}-border: rgb(${theme.border});`);
324
353
  if (theme.interactionColor) {
325
- tailwindVars.push(` --color-${utilityPrefix}-surface-${name}-interaction: rgb(${theme.interactionColor.default});`);
326
- tailwindVars.push(` --color-${utilityPrefix}-surface-${name}-interaction-hover: rgb(${theme.interactionColor.hover});`);
327
- tailwindVars.push(` --color-${utilityPrefix}-surface-${name}-interaction-focus: rgb(${theme.interactionColor.focus});`);
328
- tailwindVars.push(` --color-${utilityPrefix}-surface-${name}-interaction-active: rgb(${theme.interactionColor.active});`);
329
- tailwindVars.push(` --color-${utilityPrefix}-surface-${name}-interaction-disabled: rgb(${theme.interactionColor.disabled});`);
354
+ const { color, onColor, inkColor } = theme.interactionColor;
355
+ pushStaticInteractionColors(tailwindVars, `--color-${utilityPrefix}-surface-${name}-interaction`, color);
356
+ if (onColor) {
357
+ pushStaticInteractionColors(tailwindVars, `--color-${utilityPrefix}-surface-${name}-on-interaction`, onColor);
358
+ }
359
+ if (inkColor) {
360
+ pushStaticInteractionColors(tailwindVars, `--color-${utilityPrefix}-surface-${name}-interaction-ink`, inkColor);
361
+ }
330
362
  }
331
363
  tailwindVars.push('');
332
364
  }
@@ -420,11 +452,39 @@ ${tailwindVars.join('\n')}
420
452
  ${themeVars.join('\n')}
421
453
  }
422
454
 
423
- ${aliasBlock}`;
455
+ ${aliasBlock}
456
+ ${buildSurfaceColorScope(runtimePrefix)}`;
457
+ }
458
+ // The reserved `surface` color scope. It bridges the two theming systems - color variables out of
459
+ // surface variables - so `[etProvideColor]="'surface'"` renders a component in the neutral of
460
+ // whatever surface it sits on. It lives here rather than in the SDK's own CSS because a stylesheet
461
+ // shipped with `ProvideColorDirective` would land in every bundle that themes anything, and only
462
+ // apps that use the scope should pay for it.
463
+ function buildSurfaceColorScope(runtimePrefix) {
464
+ const tint = `var(--${runtimePrefix}-surface-interaction, var(--${runtimePrefix}-surface-color))`;
465
+ const on = `var(--${runtimePrefix}-surface-on-interaction, var(--${runtimePrefix}-surface-background))`;
466
+ const ink = `var(--${runtimePrefix}-surface-interaction-ink, var(--${runtimePrefix}-surface-color))`;
467
+ const state = (name, source, fallback) => INTERACTION_STATE_SUFFIXES.map((suffix) => ` --${runtimePrefix}-color-${name}${suffix}: ${suffix ? `var(--${runtimePrefix}-surface-${source}${suffix}, ${fallback})` : fallback};`).join('\n');
468
+ return `/* The reserved \`surface\` color: resolves the color tokens from the ambient surface's neutral swatch. */
469
+ @layer base {
470
+ .${runtimePrefix}-color--surface {
471
+ ${state('primary', 'interaction', tint)}
472
+
473
+ ${state('on-primary', 'on-interaction', on)}
474
+
475
+ ${state('primary-ink', 'interaction-ink', ink)}
476
+ }
477
+ }
478
+ `;
424
479
  }
425
480
  // Dynamic surface color declarations (unindented). Emitted in `@theme` (so Tailwind generates
426
481
  // the utilities) and re-declared on every surface selector (so the utilities resolve per scope).
427
482
  function buildDynamicSurfaceColors(utilityPrefix, runtimePrefix) {
483
+ // `on-interaction` and `interaction-ink` are optional per theme, so each falls back to the
484
+ // surface's own background/color - the same defaults the `.<runtimePrefix>-color--surface`
485
+ // bridge applies.
486
+ const onFallback = `var(--${runtimePrefix}-surface-background)`;
487
+ const inkFallback = `var(--${runtimePrefix}-surface-color)`;
428
488
  return [
429
489
  `--color-${utilityPrefix}-surface-bg: rgb(var(--${runtimePrefix}-surface-background));`,
430
490
  `--color-${utilityPrefix}-surface: rgb(var(--${runtimePrefix}-surface-color));`,
@@ -436,8 +496,35 @@ function buildDynamicSurfaceColors(utilityPrefix, runtimePrefix) {
436
496
  `--color-${utilityPrefix}-surface-interaction-focus: rgb(var(--${runtimePrefix}-surface-interaction-focus));`,
437
497
  `--color-${utilityPrefix}-surface-interaction-active: rgb(var(--${runtimePrefix}-surface-interaction-active));`,
438
498
  `--color-${utilityPrefix}-surface-interaction-disabled: rgb(var(--${runtimePrefix}-surface-interaction-disabled));`,
499
+ ...INTERACTION_STATE_SUFFIXES.map((suffix) => `--color-${utilityPrefix}-surface-on-interaction${suffix}: rgb(var(--${runtimePrefix}-surface-on-interaction${suffix}, ${onFallback}));`),
500
+ ...INTERACTION_STATE_SUFFIXES.map((suffix) => `--color-${utilityPrefix}-surface-interaction-ink${suffix}: rgb(var(--${runtimePrefix}-surface-interaction-ink${suffix}, ${inkFallback}));`),
439
501
  ];
440
502
  }
503
+ const INTERACTION_STATE_SUFFIXES = ['', '-hover', '-focus', '-active', '-disabled'];
504
+ // Fills the optional states from the ones that are set, matching how the color theme generator
505
+ // resolves a swatch: hover falls back to default, focus to hover, active and disabled to default.
506
+ function resolveInteractionStates(map) {
507
+ const hover = map.hover || map.default;
508
+ return {
509
+ '': map.default,
510
+ '-hover': hover,
511
+ '-focus': map.focus || hover,
512
+ '-active': map.active || map.default,
513
+ '-disabled': map.disabled || map.default,
514
+ };
515
+ }
516
+ function pushStaticInteractionColors(vars, name, map) {
517
+ const states = resolveInteractionStates(map);
518
+ for (const suffix of INTERACTION_STATE_SUFFIXES) {
519
+ vars.push(` ${name}${suffix}: rgb(${states[suffix]});`);
520
+ }
521
+ }
522
+ function pushRuntimeInteractionVars(vars, name, map, indent) {
523
+ const states = resolveInteractionStates(map);
524
+ for (const suffix of INTERACTION_STATE_SUFFIXES) {
525
+ vars.push(`${indent}${name}${suffix}: ${states[suffix]};`);
526
+ }
527
+ }
441
528
  function pushSurfaceVars(vars, runtimePrefix, theme, indent) {
442
529
  vars.push(`${indent}--${runtimePrefix}-surface-background: ${theme.background};`);
443
530
  vars.push(`${indent}--${runtimePrefix}-surface-color: ${theme.color};`);
@@ -447,11 +534,14 @@ function pushSurfaceVars(vars, runtimePrefix, theme, indent) {
447
534
  vars.push(`${indent}--${runtimePrefix}-surface-type: ${theme.type};`);
448
535
  vars.push(`${indent}--${runtimePrefix}-surface-elevation: ${theme.elevation};`);
449
536
  if (theme.interactionColor) {
450
- vars.push(`${indent}--${runtimePrefix}-surface-interaction: ${theme.interactionColor.default};`);
451
- vars.push(`${indent}--${runtimePrefix}-surface-interaction-hover: ${theme.interactionColor.hover};`);
452
- vars.push(`${indent}--${runtimePrefix}-surface-interaction-focus: ${theme.interactionColor.focus};`);
453
- vars.push(`${indent}--${runtimePrefix}-surface-interaction-active: ${theme.interactionColor.active};`);
454
- vars.push(`${indent}--${runtimePrefix}-surface-interaction-disabled: ${theme.interactionColor.disabled};`);
537
+ const { color, onColor, inkColor } = theme.interactionColor;
538
+ pushRuntimeInteractionVars(vars, `--${runtimePrefix}-surface-interaction`, color, indent);
539
+ if (onColor) {
540
+ pushRuntimeInteractionVars(vars, `--${runtimePrefix}-surface-on-interaction`, onColor, indent);
541
+ }
542
+ if (inkColor) {
543
+ pushRuntimeInteractionVars(vars, `--${runtimePrefix}-surface-interaction-ink`, inkColor, indent);
544
+ }
455
545
  }
456
546
  }
457
547
  function findMainStylesFile(tree) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ethlete/core",
3
- "version": "5.0.0-next.44",
3
+ "version": "5.0.0-next.46",
4
4
  "license": "MIT",
5
5
  "sideEffects": false,
6
6
  "peerDependencies": {