@ethlete/core 5.0.0-next.1 → 5.0.0-next.10
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/CHANGELOG.md +62 -0
- package/fesm2022/ethlete-core.mjs +2249 -844
- package/fesm2022/ethlete-core.mjs.map +1 -1
- package/generators/generators.json +9 -4
- package/generators/migrate-to-v5/color-naming.js +173 -0
- package/generators/migrate-to-v5/migration.js +6 -0
- package/generators/migrate-to-v5/schema.json +5 -0
- package/generators/{tailwind-4-theme → tailwind-4-color-theme}/generator.js +77 -54
- package/generators/{tailwind-4-theme → tailwind-4-color-theme}/schema.json +1 -1
- package/generators/tailwind-4-surface-theme/generator.js +368 -0
- package/generators/tailwind-4-surface-theme/schema.json +32 -0
- package/package.json +12 -11
- package/types/ethlete-core.d.ts +873 -415
|
@@ -5,10 +5,15 @@
|
|
|
5
5
|
"schema": "./migrate-to-v5/schema.json",
|
|
6
6
|
"description": "Migrate to v5"
|
|
7
7
|
},
|
|
8
|
-
"tailwind-4-theme": {
|
|
9
|
-
"factory": "./tailwind-4-theme/generator",
|
|
10
|
-
"schema": "./tailwind-4-theme/schema.json",
|
|
11
|
-
"description": "Generate theming for Tailwind CSS v4"
|
|
8
|
+
"tailwind-4-color-theme": {
|
|
9
|
+
"factory": "./tailwind-4-color-theme/generator",
|
|
10
|
+
"schema": "./tailwind-4-color-theme/schema.json",
|
|
11
|
+
"description": "Generate color theming for Tailwind CSS v4"
|
|
12
|
+
},
|
|
13
|
+
"tailwind-4-surface-theme": {
|
|
14
|
+
"factory": "./tailwind-4-surface-theme/generator",
|
|
15
|
+
"schema": "./tailwind-4-surface-theme/schema.json",
|
|
16
|
+
"description": "Generate surface theming for Tailwind CSS v4"
|
|
12
17
|
}
|
|
13
18
|
}
|
|
14
19
|
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { logger } from '@nx/devkit';
|
|
2
|
+
/**
|
|
3
|
+
* Symbol renames in TypeScript files (imports + usage).
|
|
4
|
+
*/
|
|
5
|
+
const TS_SYMBOL_RENAMES = {
|
|
6
|
+
ProvideThemeDirective: 'ProvideColorDirective',
|
|
7
|
+
THEME_PROVIDER: 'COLOR_PROVIDER',
|
|
8
|
+
ColorThemedDirective: 'ColoredDirective',
|
|
9
|
+
ColorThemedStylesComponent: 'ColoredStylesComponent',
|
|
10
|
+
SurfaceThemedDirective: 'SurfacedDirective',
|
|
11
|
+
SurfaceThemedStylesComponent: 'SurfacedStylesComponent',
|
|
12
|
+
injectThemesPrefix: 'injectColorThemesPrefix',
|
|
13
|
+
createCssThemeName: 'createCssColorThemeName',
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* String replacements applied to HTML templates and inline templates.
|
|
17
|
+
*/
|
|
18
|
+
const TEMPLATE_REPLACEMENTS = [
|
|
19
|
+
['[etProvideTheme]', '[etProvideColor]'],
|
|
20
|
+
['etProvideTheme', 'etProvideColor'],
|
|
21
|
+
['[etProvideAltTheme]', '[etProvideAltColor]'],
|
|
22
|
+
['etProvideAltTheme', 'etProvideAltColor'],
|
|
23
|
+
['[etColorThemed]', '[etColored]'],
|
|
24
|
+
['etColorThemed', 'etColored'],
|
|
25
|
+
['[etSurfaceThemed]', '[etSurfaced]'],
|
|
26
|
+
['etSurfaceThemed', 'etSurfaced'],
|
|
27
|
+
['[theme]=', '[color]='],
|
|
28
|
+
['[altTheme]=', '[altColor]='],
|
|
29
|
+
];
|
|
30
|
+
/**
|
|
31
|
+
* CSS class name replacements.
|
|
32
|
+
*/
|
|
33
|
+
const CSS_REPLACEMENTS = [
|
|
34
|
+
['.et-color-themed', '.et-colored'],
|
|
35
|
+
['.et-surface-themed', '.et-surfaced'],
|
|
36
|
+
['.et-theme--', '.et-color--'],
|
|
37
|
+
['.et-theme-alt--', '.et-color-alt--'],
|
|
38
|
+
];
|
|
39
|
+
/**
|
|
40
|
+
* Host directive input alias replacements in TypeScript files.
|
|
41
|
+
*/
|
|
42
|
+
const HOST_DIRECTIVE_REPLACEMENTS = [
|
|
43
|
+
["'etProvideTheme:theme'", "'etProvideColor:color'"],
|
|
44
|
+
["'etProvideAltTheme:altTheme'", "'etProvideAltColor:altColor'"],
|
|
45
|
+
["'etProvideTheme:color'", "'etProvideColor:color'"],
|
|
46
|
+
["'etProvideAltTheme:altColor'", "'etProvideAltColor:altColor'"],
|
|
47
|
+
['"etProvideTheme:theme"', '"etProvideColor:color"'],
|
|
48
|
+
['"etProvideAltTheme:altTheme"', '"etProvideAltColor:altColor"'],
|
|
49
|
+
['"etProvideTheme"', '"etProvideColor"'],
|
|
50
|
+
['"etProvideAltTheme"', '"etProvideAltColor"'],
|
|
51
|
+
];
|
|
52
|
+
export default async function migrateColorNaming(tree) {
|
|
53
|
+
logger.log('\n🔄 Migrating theme → color naming...\n');
|
|
54
|
+
const tsFiles = [];
|
|
55
|
+
const htmlFiles = [];
|
|
56
|
+
const cssFiles = [];
|
|
57
|
+
findFiles('.', tree, tsFiles, htmlFiles, cssFiles);
|
|
58
|
+
let filesModified = 0;
|
|
59
|
+
for (const filePath of tsFiles) {
|
|
60
|
+
const wasModified = migrateTypeScriptFile(tree, filePath);
|
|
61
|
+
if (wasModified) {
|
|
62
|
+
filesModified++;
|
|
63
|
+
logger.log(` ✓ ${filePath}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
for (const filePath of htmlFiles) {
|
|
67
|
+
const wasModified = migrateTemplateFile(tree, filePath);
|
|
68
|
+
if (wasModified) {
|
|
69
|
+
filesModified++;
|
|
70
|
+
logger.log(` ✓ ${filePath}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
for (const filePath of cssFiles) {
|
|
74
|
+
const wasModified = migrateCssFile(tree, filePath);
|
|
75
|
+
if (wasModified) {
|
|
76
|
+
filesModified++;
|
|
77
|
+
logger.log(` ✓ ${filePath}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (filesModified > 0) {
|
|
81
|
+
logger.log(`\n✅ Successfully migrated color naming in ${filesModified} file(s)\n`);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
logger.log('\nℹ️ No files needed migration\n');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function findFiles(dir, tree, tsFiles, htmlFiles, cssFiles) {
|
|
88
|
+
const children = tree.children(dir);
|
|
89
|
+
for (const child of children) {
|
|
90
|
+
const path = dir === '.' ? child : `${dir}/${child}`;
|
|
91
|
+
if (tree.isFile(path)) {
|
|
92
|
+
if (path.includes('node_modules'))
|
|
93
|
+
continue;
|
|
94
|
+
if (path.endsWith('.ts') && !path.endsWith('.spec.ts')) {
|
|
95
|
+
tsFiles.push(path);
|
|
96
|
+
}
|
|
97
|
+
else if (path.endsWith('.html')) {
|
|
98
|
+
htmlFiles.push(path);
|
|
99
|
+
}
|
|
100
|
+
else if (path.endsWith('.css') || path.endsWith('.scss')) {
|
|
101
|
+
cssFiles.push(path);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
if (child === 'node_modules')
|
|
106
|
+
continue;
|
|
107
|
+
findFiles(path, tree, tsFiles, htmlFiles, cssFiles);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function migrateTypeScriptFile(tree, filePath) {
|
|
112
|
+
let content = tree.read(filePath, 'utf-8');
|
|
113
|
+
if (!content)
|
|
114
|
+
return false;
|
|
115
|
+
const original = content;
|
|
116
|
+
// Rename symbols
|
|
117
|
+
for (const [oldName, newName] of Object.entries(TS_SYMBOL_RENAMES)) {
|
|
118
|
+
content = replaceWholeWord(content, oldName, newName);
|
|
119
|
+
}
|
|
120
|
+
// Rename host directive input aliases
|
|
121
|
+
for (const [oldStr, newStr] of HOST_DIRECTIVE_REPLACEMENTS) {
|
|
122
|
+
content = content.replaceAll(oldStr, newStr);
|
|
123
|
+
}
|
|
124
|
+
// Handle inline templates in TypeScript files
|
|
125
|
+
for (const [oldStr, newStr] of TEMPLATE_REPLACEMENTS) {
|
|
126
|
+
content = content.replaceAll(oldStr, newStr);
|
|
127
|
+
}
|
|
128
|
+
// Handle CSS in TypeScript files
|
|
129
|
+
for (const [oldStr, newStr] of CSS_REPLACEMENTS) {
|
|
130
|
+
content = content.replaceAll(oldStr, newStr);
|
|
131
|
+
}
|
|
132
|
+
if (content !== original) {
|
|
133
|
+
tree.write(filePath, content);
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
function migrateTemplateFile(tree, filePath) {
|
|
139
|
+
let content = tree.read(filePath, 'utf-8');
|
|
140
|
+
if (!content)
|
|
141
|
+
return false;
|
|
142
|
+
const original = content;
|
|
143
|
+
for (const [oldStr, newStr] of TEMPLATE_REPLACEMENTS) {
|
|
144
|
+
content = content.replaceAll(oldStr, newStr);
|
|
145
|
+
}
|
|
146
|
+
if (content !== original) {
|
|
147
|
+
tree.write(filePath, content);
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
function migrateCssFile(tree, filePath) {
|
|
153
|
+
let content = tree.read(filePath, 'utf-8');
|
|
154
|
+
if (!content)
|
|
155
|
+
return false;
|
|
156
|
+
const original = content;
|
|
157
|
+
for (const [oldStr, newStr] of CSS_REPLACEMENTS) {
|
|
158
|
+
content = content.replaceAll(oldStr, newStr);
|
|
159
|
+
}
|
|
160
|
+
if (content !== original) {
|
|
161
|
+
tree.write(filePath, content);
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
function replaceWholeWord(content, oldWord, newWord) {
|
|
167
|
+
const regex = new RegExp(`\\b${escapeRegex(oldWord)}\\b`, 'g');
|
|
168
|
+
return content.replace(regex, newWord);
|
|
169
|
+
}
|
|
170
|
+
function escapeRegex(str) {
|
|
171
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=color-naming.js.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { formatFiles } from '@nx/devkit';
|
|
2
|
+
import migrateColorNaming from './color-naming.js';
|
|
2
3
|
import migrateCreateProvider from './create-provider.js';
|
|
3
4
|
import migrateRouterStateService from './router-state-service.js';
|
|
4
5
|
import migrateViewportService from './viewport-service.js';
|
|
@@ -18,6 +19,11 @@ export default async function migrate(tree, schema) {
|
|
|
18
19
|
if (shouldMigrateRouterStateService) {
|
|
19
20
|
await migrateRouterStateService(tree);
|
|
20
21
|
}
|
|
22
|
+
const shouldMigrateColorNaming = schema.migrateColorNaming !== false;
|
|
23
|
+
if (shouldMigrateColorNaming) {
|
|
24
|
+
console.log(' • Migrating theme → color naming...');
|
|
25
|
+
await migrateColorNaming(tree);
|
|
26
|
+
}
|
|
21
27
|
if (!schema.skipFormat) {
|
|
22
28
|
console.log(' • Formatting files...');
|
|
23
29
|
await formatFiles(tree);
|
|
@@ -23,6 +23,11 @@
|
|
|
23
23
|
"type": "boolean",
|
|
24
24
|
"description": "Migrate router state service to v5",
|
|
25
25
|
"default": true
|
|
26
|
+
},
|
|
27
|
+
"migrateColorNaming": {
|
|
28
|
+
"type": "boolean",
|
|
29
|
+
"description": "Migrate theme naming to color naming (ProvideThemeDirective -> ProvideColorDirective, etc.)",
|
|
30
|
+
"default": true
|
|
26
31
|
}
|
|
27
32
|
},
|
|
28
33
|
"required": []
|
|
@@ -9,7 +9,7 @@ export default async function generate(tree, schema) {
|
|
|
9
9
|
if (!tree.exists(themesPath)) {
|
|
10
10
|
logger.error(`❌ Themes file not found at: ${themesPath}`);
|
|
11
11
|
logger.log(`\nPlease specify the correct path using --themesPath option.`);
|
|
12
|
-
logger.log(`Example: nx g @ethlete/core:tailwind-4-theme --themesPath=src/app/themes.ts\n`);
|
|
12
|
+
logger.log(`Example: nx g @ethlete/core:tailwind-4-color-theme --themesPath=src/app/themes.ts\n`);
|
|
13
13
|
return;
|
|
14
14
|
}
|
|
15
15
|
logger.log(`📁 Reading themes from: ${themesPath}`);
|
|
@@ -167,9 +167,9 @@ function parseThemeObject(obj, sourceFile) {
|
|
|
167
167
|
theme.isDefault = true;
|
|
168
168
|
}
|
|
169
169
|
break;
|
|
170
|
-
case '
|
|
171
|
-
if (initializer.isKind(SyntaxKind.
|
|
172
|
-
theme.
|
|
170
|
+
case 'type':
|
|
171
|
+
if (initializer.isKind(SyntaxKind.StringLiteral)) {
|
|
172
|
+
theme.type = initializer.getLiteralValue();
|
|
173
173
|
}
|
|
174
174
|
break;
|
|
175
175
|
case 'primary':
|
|
@@ -188,8 +188,6 @@ function parseThemeObject(obj, sourceFile) {
|
|
|
188
188
|
}
|
|
189
189
|
function validateThemeConfiguration(themes) {
|
|
190
190
|
const defaultThemes = themes.filter((t) => t.isDefault);
|
|
191
|
-
const defaultAltThemes = themes.filter((t) => t.isDefaultAlt);
|
|
192
|
-
const bothDefaultAndAlt = themes.filter((t) => t.isDefault && t.isDefaultAlt);
|
|
193
191
|
// Error: No default theme
|
|
194
192
|
if (defaultThemes.length === 0) {
|
|
195
193
|
throw new Error('No default theme found. At least one theme must have isDefault: true');
|
|
@@ -198,13 +196,18 @@ function validateThemeConfiguration(themes) {
|
|
|
198
196
|
if (defaultThemes.length > 1) {
|
|
199
197
|
throw new Error(`Multiple default themes found: ${defaultThemes.map((t) => t.name).join(', ')}. Only one theme can have isDefault: true`);
|
|
200
198
|
}
|
|
201
|
-
// Error:
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
199
|
+
// Error: Duplicate theme types
|
|
200
|
+
const typedThemes = themes.filter((t) => t.type);
|
|
201
|
+
const typeMap = new Map();
|
|
202
|
+
for (const theme of typedThemes) {
|
|
203
|
+
const existing = typeMap.get(theme.type) || [];
|
|
204
|
+
existing.push(theme.name);
|
|
205
|
+
typeMap.set(theme.type, existing);
|
|
206
|
+
}
|
|
207
|
+
for (const [type, names] of typeMap) {
|
|
208
|
+
if (names.length > 1) {
|
|
209
|
+
throw new Error(`Multiple themes with type "${type}" found: ${names.join(', ')}. Only one theme can have a given type`);
|
|
210
|
+
}
|
|
208
211
|
}
|
|
209
212
|
}
|
|
210
213
|
function parseThemeSwatch(obj, sourceFile) {
|
|
@@ -219,7 +222,7 @@ function parseThemeSwatch(obj, sourceFile) {
|
|
|
219
222
|
if (!initializer) {
|
|
220
223
|
continue;
|
|
221
224
|
}
|
|
222
|
-
if (propName === 'color' || propName === 'onColor') {
|
|
225
|
+
if (propName === 'color' || propName === 'onColor' || propName === 'inkColor') {
|
|
223
226
|
const colorMap = parseColorMap(initializer, sourceFile);
|
|
224
227
|
if (colorMap) {
|
|
225
228
|
swatch[propName] = colorMap;
|
|
@@ -306,14 +309,13 @@ function generateTailwindThemeCss(themes, prefix, schema) {
|
|
|
306
309
|
*
|
|
307
310
|
* Generated from your theme definitions
|
|
308
311
|
* This file can be regenerated by running:
|
|
309
|
-
* nx g @ethlete/core:tailwind-4-theme --themesPath=${themesPath}${schema.outputPath ? ` --outputPath=${outputPath}` : ''}${schema.prefix && schema.prefix !== 'et' ? ` --prefix=${schema.prefix}` : ''}
|
|
312
|
+
* nx g @ethlete/core:tailwind-4-color-theme --themesPath=${themesPath}${schema.outputPath ? ` --outputPath=${outputPath}` : ''}${schema.prefix && schema.prefix !== 'et' ? ` --prefix=${schema.prefix}` : ''}
|
|
310
313
|
*/
|
|
311
314
|
|
|
312
315
|
`;
|
|
313
316
|
// Validation is now done separately before this function is called
|
|
314
317
|
const defaultThemes = themes.filter((t) => t.isDefault);
|
|
315
|
-
const
|
|
316
|
-
const regularThemes = themes.filter((t) => !t.isDefault && !t.isDefaultAlt);
|
|
318
|
+
const regularThemes = themes.filter((t) => !t.isDefault);
|
|
317
319
|
// Generate static Tailwind @theme block for each theme
|
|
318
320
|
for (const theme of themes) {
|
|
319
321
|
const name = createCssThemeName(theme.name);
|
|
@@ -325,6 +327,9 @@ function generateTailwindThemeCss(themes, prefix, schema) {
|
|
|
325
327
|
// On colors for Tailwind utilities
|
|
326
328
|
addTailwindColorVariants(tailwindVars, `${prefix}-on-${name}`, theme.primary.onColor);
|
|
327
329
|
tailwindVars.push('');
|
|
330
|
+
// Ink colors for standalone foreground usage
|
|
331
|
+
addTailwindColorVariants(tailwindVars, `${prefix}-${name}-ink`, theme.primary.inkColor || theme.primary.color);
|
|
332
|
+
tailwindVars.push('');
|
|
328
333
|
// Secondary colors if present
|
|
329
334
|
if (theme.secondary) {
|
|
330
335
|
addTailwindColorVariants(tailwindVars, `${prefix}-${name}-secondary`, theme.secondary.color);
|
|
@@ -341,68 +346,67 @@ function generateTailwindThemeCss(themes, prefix, schema) {
|
|
|
341
346
|
}
|
|
342
347
|
}
|
|
343
348
|
tailwindVars.push('');
|
|
344
|
-
//
|
|
349
|
+
// Main theme dynamic colors
|
|
345
350
|
const mainThemes = [...defaultThemes, ...regularThemes];
|
|
346
351
|
const hasSecondary = mainThemes.some((t) => t.secondary);
|
|
347
352
|
const hasTertiary = mainThemes.some((t) => t.tertiary);
|
|
348
|
-
const hasAltSecondary = defaultAltThemes.some((t) => t.secondary);
|
|
349
|
-
const hasAltTertiary = defaultAltThemes.some((t) => t.tertiary);
|
|
350
|
-
// Main theme dynamic colors
|
|
351
353
|
tailwindVars.push(' /* Dynamic theme colors (references runtime CSS variables) */');
|
|
352
354
|
addDynamicThemeColors(tailwindVars, prefix, 'theme', 'primary', true);
|
|
355
|
+
addDynamicInkColors(tailwindVars, prefix, 'theme-ink', 'primary-ink');
|
|
353
356
|
if (hasSecondary) {
|
|
354
357
|
addDynamicThemeColors(tailwindVars, prefix, 'theme-secondary', 'secondary', false);
|
|
355
358
|
}
|
|
356
359
|
if (hasTertiary) {
|
|
357
360
|
addDynamicThemeColors(tailwindVars, prefix, 'theme-tertiary', 'tertiary', false);
|
|
358
361
|
}
|
|
359
|
-
//
|
|
360
|
-
tailwindVars.push(' /* Alt theme dynamic colors */');
|
|
361
|
-
addDynamicThemeColors(tailwindVars, prefix, 'alt-theme', 'alt-primary', true);
|
|
362
|
-
if (hasAltSecondary) {
|
|
363
|
-
addDynamicThemeColors(tailwindVars, prefix, 'alt-theme-secondary', 'alt-secondary', false);
|
|
364
|
-
}
|
|
365
|
-
if (hasAltTertiary) {
|
|
366
|
-
addDynamicThemeColors(tailwindVars, prefix, 'alt-theme-tertiary', 'alt-tertiary', false);
|
|
367
|
-
}
|
|
368
|
-
// Generate runtime CSS for ALL themes (both main and alt variants)
|
|
362
|
+
// Generate runtime CSS for all themes
|
|
369
363
|
themes.forEach((theme) => {
|
|
370
364
|
const name = createCssThemeName(theme.name);
|
|
371
|
-
// Determine if this is the default
|
|
365
|
+
// Determine if this is the default theme
|
|
372
366
|
const isDefault = theme.isDefault;
|
|
373
|
-
|
|
374
|
-
// Generate main theme variant (.et-theme--{name})
|
|
367
|
+
// Generate color variant (.et-color--{name})
|
|
375
368
|
if (isDefault) {
|
|
376
|
-
// Default
|
|
377
|
-
const selectors = [':root', `.${prefix}-
|
|
378
|
-
themeVars.push(`${selectors.join(', ')} {`);
|
|
379
|
-
}
|
|
380
|
-
else if (!isDefaultAlt) {
|
|
381
|
-
// Regular themes just get their own class
|
|
382
|
-
themeVars.push(`.${prefix}-theme--${name} {`);
|
|
383
|
-
}
|
|
384
|
-
if (isDefault || !isDefaultAlt) {
|
|
385
|
-
addThemeColorVariants(themeVars, prefix, '', theme);
|
|
386
|
-
themeVars.push('}\n');
|
|
387
|
-
}
|
|
388
|
-
// Generate alt theme variant (.et-theme-alt--{name})
|
|
389
|
-
if (isDefaultAlt) {
|
|
390
|
-
// Default alt theme gets :root and .et-theme-alt--default selectors
|
|
391
|
-
const selectors = [':root', `.${prefix}-theme-alt--default`, `.${prefix}-theme-alt--${name}`];
|
|
369
|
+
// Default color gets :root and .et-color--default selectors
|
|
370
|
+
const selectors = [':root', `.${prefix}-color--default`, `.${prefix}-color--${name}`];
|
|
392
371
|
themeVars.push(`${selectors.join(', ')} {`);
|
|
393
372
|
}
|
|
394
373
|
else {
|
|
395
|
-
//
|
|
396
|
-
themeVars.push(`.${prefix}-
|
|
374
|
+
// Regular colors just get their own class
|
|
375
|
+
themeVars.push(`.${prefix}-color--${name} {`);
|
|
397
376
|
}
|
|
398
|
-
addThemeColorVariants(themeVars, prefix, '
|
|
377
|
+
addThemeColorVariants(themeVars, prefix, '', theme);
|
|
399
378
|
themeVars.push('}\n');
|
|
400
379
|
});
|
|
380
|
+
// Convenience var aliases — available on any element with a color class (or root for default)
|
|
381
|
+
const aliasBlock = `/* Convenience aliases (rgb + solid + opacity variants) */
|
|
382
|
+
@layer base {
|
|
383
|
+
:root, :where([class*="${prefix}-color--"]) {
|
|
384
|
+
--${prefix}-theme-color-primary-rgb: var(--${prefix}-color-primary);
|
|
385
|
+
--${prefix}-theme-color-primary-opacity: 1;
|
|
386
|
+
--${prefix}-theme-color-primary-solid: rgb(var(--${prefix}-theme-color-primary-rgb));
|
|
387
|
+
--${prefix}-theme-color-primary: rgb(var(--${prefix}-theme-color-primary-rgb) / var(--${prefix}-theme-color-primary-opacity));
|
|
388
|
+
|
|
389
|
+
--${prefix}-theme-color-on-primary-rgb: var(--${prefix}-color-on-primary);
|
|
390
|
+
--${prefix}-theme-color-on-primary-opacity: 1;
|
|
391
|
+
--${prefix}-theme-color-on-primary-solid: rgb(var(--${prefix}-theme-color-on-primary-rgb) / var(--${prefix}-theme-color-on-primary-opacity));
|
|
392
|
+
--${prefix}-theme-color-on-primary: rgb(var(--${prefix}-theme-color-on-primary-rgb) / var(--${prefix}-theme-color-on-primary-opacity));
|
|
393
|
+
|
|
394
|
+
--${prefix}-theme-color-ink-rgb: var(--${prefix}-color-primary-ink, var(--${prefix}-color-primary));
|
|
395
|
+
--${prefix}-theme-color-ink-opacity: 1;
|
|
396
|
+
--${prefix}-theme-color-ink-solid: rgb(var(--${prefix}-theme-color-ink-rgb) / var(--${prefix}-theme-color-ink-opacity));
|
|
397
|
+
--${prefix}-theme-color-ink: rgb(var(--${prefix}-theme-color-ink-rgb) / var(--${prefix}-theme-color-ink-opacity));
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
`;
|
|
401
401
|
return `${header}@theme {
|
|
402
402
|
${tailwindVars.join('\n')}
|
|
403
403
|
}
|
|
404
404
|
|
|
405
|
-
|
|
405
|
+
@layer base {
|
|
406
|
+
${themeVars.join('\n')}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
${aliasBlock}`;
|
|
406
410
|
}
|
|
407
411
|
function addDynamicThemeColors(vars, prefix, tailwindName, cssVarName, addSpacingBefore) {
|
|
408
412
|
if (addSpacingBefore && vars.length > 0 && vars[vars.length - 1] !== '') {
|
|
@@ -423,6 +427,14 @@ function addDynamicThemeColors(vars, prefix, tailwindName, cssVarName, addSpacin
|
|
|
423
427
|
vars.push(` --color-${prefix}-on-${tailwindName}-disabled: rgb(var(--${prefix}-color-on-${cssVarName}-disabled));`);
|
|
424
428
|
vars.push('');
|
|
425
429
|
}
|
|
430
|
+
function addDynamicInkColors(vars, prefix, tailwindName, cssVarName) {
|
|
431
|
+
vars.push(` --color-${prefix}-${tailwindName}: rgb(var(--${prefix}-color-${cssVarName}));`);
|
|
432
|
+
vars.push(` --color-${prefix}-${tailwindName}-hover: rgb(var(--${prefix}-color-${cssVarName}-hover));`);
|
|
433
|
+
vars.push(` --color-${prefix}-${tailwindName}-focus: rgb(var(--${prefix}-color-${cssVarName}-focus));`);
|
|
434
|
+
vars.push(` --color-${prefix}-${tailwindName}-active: rgb(var(--${prefix}-color-${cssVarName}-active));`);
|
|
435
|
+
vars.push(` --color-${prefix}-${tailwindName}-disabled: rgb(var(--${prefix}-color-${cssVarName}-disabled));`);
|
|
436
|
+
vars.push('');
|
|
437
|
+
}
|
|
426
438
|
function addTailwindColorVariants(vars, colorName, colorSet) {
|
|
427
439
|
// Tailwind 4 requires --color-* prefix and rgb() wrapper
|
|
428
440
|
// Always generate all variants with fallbacks
|
|
@@ -465,6 +477,17 @@ function addThemeColorVariants(vars, prefix, altPrefix, theme) {
|
|
|
465
477
|
vars.push(` --${prefix}-color-${altPrefix}on-${level}-focus: ${onFocusColor};`);
|
|
466
478
|
vars.push(` --${prefix}-color-${altPrefix}on-${level}-active: ${onActiveColor};`);
|
|
467
479
|
vars.push(` --${prefix}-color-${altPrefix}on-${level}-disabled: ${onDisabledColor};`);
|
|
480
|
+
const inkDefaultColor = swatch.inkColor?.default || defaultColor;
|
|
481
|
+
const inkHoverColor = swatch.inkColor?.hover || inkDefaultColor;
|
|
482
|
+
const inkFocusColor = swatch.inkColor?.focus || inkHoverColor;
|
|
483
|
+
const inkActiveColor = swatch.inkColor?.active || inkDefaultColor;
|
|
484
|
+
const inkDisabledColor = swatch.inkColor?.disabled || inkDefaultColor;
|
|
485
|
+
vars.push('');
|
|
486
|
+
vars.push(` --${prefix}-color-${altPrefix}${level}-ink: ${inkDefaultColor};`);
|
|
487
|
+
vars.push(` --${prefix}-color-${altPrefix}${level}-ink-hover: ${inkHoverColor};`);
|
|
488
|
+
vars.push(` --${prefix}-color-${altPrefix}${level}-ink-focus: ${inkFocusColor};`);
|
|
489
|
+
vars.push(` --${prefix}-color-${altPrefix}${level}-ink-active: ${inkActiveColor};`);
|
|
490
|
+
vars.push(` --${prefix}-color-${altPrefix}${level}-ink-disabled: ${inkDisabledColor};`);
|
|
468
491
|
if (theme.secondary || theme.tertiary) {
|
|
469
492
|
vars.push('');
|
|
470
493
|
}
|