@ethlete/core 5.0.0-next.7 → 5.0.0-next.9
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 +14 -0
- package/fesm2022/ethlete-core.mjs +1017 -219
- 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 +53 -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 +775 -532
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import { formatFiles, logger, visitNotIgnoredFiles } from '@nx/devkit';
|
|
2
|
+
import { Project, SyntaxKind } from 'ts-morph';
|
|
3
|
+
export default async function generate(tree, schema) {
|
|
4
|
+
logger.log('\n🔄 Starting Tailwind 4 surface theme generator...\n');
|
|
5
|
+
const themesPath = schema.themesPath || 'src/surface-themes.ts';
|
|
6
|
+
const outputPath = schema.outputPath || 'src/styles/generated-tailwind-surface-themes.css';
|
|
7
|
+
const prefix = schema.prefix || 'et';
|
|
8
|
+
if (!tree.exists(themesPath)) {
|
|
9
|
+
logger.error(`❌ Surface themes file not found at: ${themesPath}`);
|
|
10
|
+
logger.log(`\nPlease specify the correct path using --themesPath option.`);
|
|
11
|
+
logger.log(`Example: nx g @ethlete/core:tailwind-4-surface-theme --themesPath=src/app/surface-themes.ts\n`);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
logger.log(`📁 Reading surface themes from: ${themesPath}`);
|
|
15
|
+
const themesContent = tree.read(themesPath, 'utf-8');
|
|
16
|
+
if (!themesContent) {
|
|
17
|
+
logger.error('❌ Failed to read surface themes file');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
let themes;
|
|
21
|
+
try {
|
|
22
|
+
themes = extractSurfaceThemesFromContent(themesContent, themesPath);
|
|
23
|
+
logger.log(`✅ Found ${themes.length} surface theme(s)`);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
logger.error('❌ Failed to parse surface themes file');
|
|
27
|
+
logger.error(` ${error instanceof Error ? error.message : String(error)}`);
|
|
28
|
+
logger.log('\nThe surface themes file must export themes as:');
|
|
29
|
+
logger.log(' export const SURFACE_THEMES = [...] satisfies SurfaceTheme[];\n');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
validateSurfaceThemeConfiguration(themes);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
logger.error('❌ Surface theme configuration error');
|
|
37
|
+
logger.error(` ${error instanceof Error ? error.message : String(error)}`);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
logger.log('\n🎨 Generating Tailwind surface theme CSS...');
|
|
41
|
+
const css = generateSurfaceThemeCss(themes, prefix, schema);
|
|
42
|
+
tree.write(outputPath, css);
|
|
43
|
+
logger.log(`✅ Generated Tailwind surface themes at: ${outputPath}`);
|
|
44
|
+
const mainStylesFiles = findMainStylesFile(tree);
|
|
45
|
+
if (mainStylesFiles.length > 0) {
|
|
46
|
+
logger.log('\n📝 Found potential main styles files:');
|
|
47
|
+
mainStylesFiles.forEach((file) => logger.log(` - ${file}`));
|
|
48
|
+
logger.log('\n⚠️ Please manually import the generated themes:');
|
|
49
|
+
logger.log(` @import './${outputPath.replace('src/styles/', '')}';`);
|
|
50
|
+
}
|
|
51
|
+
if (!schema.skipFormat) {
|
|
52
|
+
await formatFiles(tree);
|
|
53
|
+
}
|
|
54
|
+
logger.log('\n✅ Generation completed successfully!\n');
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region Parsing
|
|
58
|
+
function extractSurfaceThemesFromContent(content, filePath) {
|
|
59
|
+
const project = new Project({
|
|
60
|
+
useInMemoryFileSystem: true,
|
|
61
|
+
compilerOptions: {
|
|
62
|
+
target: 99,
|
|
63
|
+
module: 99,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
const sourceFile = project.createSourceFile(filePath, content);
|
|
67
|
+
const themes = [];
|
|
68
|
+
const exportedDeclarations = sourceFile.getVariableDeclarations().filter((decl) => {
|
|
69
|
+
const statement = decl.getVariableStatement();
|
|
70
|
+
return statement?.isExported();
|
|
71
|
+
});
|
|
72
|
+
const themesArray = exportedDeclarations.find((decl) => {
|
|
73
|
+
const name = decl.getName();
|
|
74
|
+
return name === 'SURFACE_THEMES' || name === 'surfaceThemes';
|
|
75
|
+
});
|
|
76
|
+
if (!themesArray) {
|
|
77
|
+
throw new Error('Could not find SURFACE_THEMES or surfaceThemes export');
|
|
78
|
+
}
|
|
79
|
+
let initializer = themesArray.getInitializer();
|
|
80
|
+
if (!initializer) {
|
|
81
|
+
throw new Error('SURFACE_THEMES export has no initializer');
|
|
82
|
+
}
|
|
83
|
+
if (initializer.isKind(SyntaxKind.SatisfiesExpression)) {
|
|
84
|
+
initializer = initializer.getExpression();
|
|
85
|
+
}
|
|
86
|
+
if (initializer.isKind(SyntaxKind.AsExpression)) {
|
|
87
|
+
initializer = initializer.getExpression();
|
|
88
|
+
}
|
|
89
|
+
if (!initializer.isKind(SyntaxKind.ArrayLiteralExpression)) {
|
|
90
|
+
throw new Error('SURFACE_THEMES export must be an array literal');
|
|
91
|
+
}
|
|
92
|
+
const elements = initializer.getElements();
|
|
93
|
+
for (const element of elements) {
|
|
94
|
+
if (element.isKind(SyntaxKind.Identifier)) {
|
|
95
|
+
const name = element.getText();
|
|
96
|
+
const themeDecl = exportedDeclarations.find((decl) => decl.getName() === name);
|
|
97
|
+
if (!themeDecl) {
|
|
98
|
+
logger.warn(`⚠️ Could not find declaration for surface theme: ${name}`);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
let themeObj = themeDecl.getInitializer();
|
|
102
|
+
if (!themeObj) {
|
|
103
|
+
logger.warn(`⚠️ Surface theme ${name} has no initializer`);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (themeObj.isKind(SyntaxKind.AsExpression)) {
|
|
107
|
+
themeObj = themeObj.getExpression();
|
|
108
|
+
}
|
|
109
|
+
if (!themeObj.isKind(SyntaxKind.ObjectLiteralExpression)) {
|
|
110
|
+
logger.warn(`⚠️ Surface theme ${name} is not an object literal`);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
const theme = parseSurfaceThemeObject(themeObj);
|
|
115
|
+
themes.push(theme);
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
logger.warn(`⚠️ Failed to parse surface theme ${name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (themes.length === 0) {
|
|
123
|
+
throw new Error('No valid surface themes found in SURFACE_THEMES array');
|
|
124
|
+
}
|
|
125
|
+
return themes;
|
|
126
|
+
}
|
|
127
|
+
function parseSurfaceThemeObject(obj) {
|
|
128
|
+
const properties = obj.getProperties();
|
|
129
|
+
const theme = {};
|
|
130
|
+
const STRING_PROPS = ['name', 'background', 'color', 'colorMuted', 'colorSubtle', 'border'];
|
|
131
|
+
for (const prop of properties) {
|
|
132
|
+
if (!prop.isKind(SyntaxKind.PropertyAssignment)) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const propName = prop.getName();
|
|
136
|
+
const initializer = prop.getInitializer();
|
|
137
|
+
if (!initializer) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (propName === 'isDefault') {
|
|
141
|
+
if (initializer.isKind(SyntaxKind.TrueKeyword)) {
|
|
142
|
+
theme.isDefault = true;
|
|
143
|
+
}
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (propName === 'interactionColor' && initializer.isKind(SyntaxKind.ObjectLiteralExpression)) {
|
|
147
|
+
const interactionProps = initializer.getProperties();
|
|
148
|
+
const interactionColor = {};
|
|
149
|
+
for (const iProp of interactionProps) {
|
|
150
|
+
if (!iProp.isKind(SyntaxKind.PropertyAssignment))
|
|
151
|
+
continue;
|
|
152
|
+
const iPropName = iProp.getName();
|
|
153
|
+
const iInit = iProp.getInitializer();
|
|
154
|
+
if (iInit?.isKind(SyntaxKind.StringLiteral)) {
|
|
155
|
+
interactionColor[iPropName] = iInit.getLiteralValue();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (interactionColor.default &&
|
|
159
|
+
interactionColor.hover &&
|
|
160
|
+
interactionColor.focus &&
|
|
161
|
+
interactionColor.active &&
|
|
162
|
+
interactionColor.disabled) {
|
|
163
|
+
theme.interactionColor = interactionColor;
|
|
164
|
+
}
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (propName === 'elevation') {
|
|
168
|
+
if (initializer.isKind(SyntaxKind.NumericLiteral)) {
|
|
169
|
+
theme.elevation = Number(initializer.getLiteralValue());
|
|
170
|
+
}
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (propName === 'type' && initializer.isKind(SyntaxKind.StringLiteral)) {
|
|
174
|
+
theme.type = initializer.getLiteralValue();
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (STRING_PROPS.includes(propName) && initializer.isKind(SyntaxKind.StringLiteral)) {
|
|
178
|
+
theme[propName] = initializer.getLiteralValue();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (!theme.name ||
|
|
182
|
+
!theme.type ||
|
|
183
|
+
theme.elevation === undefined ||
|
|
184
|
+
!theme.background ||
|
|
185
|
+
!theme.color ||
|
|
186
|
+
!theme.colorMuted ||
|
|
187
|
+
!theme.colorSubtle ||
|
|
188
|
+
!theme.border) {
|
|
189
|
+
throw new Error('SurfaceTheme must have name, type, elevation, background, color, colorMuted, colorSubtle, and border properties');
|
|
190
|
+
}
|
|
191
|
+
return theme;
|
|
192
|
+
}
|
|
193
|
+
function validateSurfaceThemeConfiguration(themes) {
|
|
194
|
+
const defaultThemes = themes.filter((t) => t.isDefault);
|
|
195
|
+
if (defaultThemes.length === 0) {
|
|
196
|
+
throw new Error('No default surface theme found. Each type (light/dark) must have exactly one theme with isDefault: true');
|
|
197
|
+
}
|
|
198
|
+
const types = new Set(themes.map((t) => t.type));
|
|
199
|
+
for (const type of types) {
|
|
200
|
+
const defaultsForType = defaultThemes.filter((t) => t.type === type);
|
|
201
|
+
if (defaultsForType.length === 0) {
|
|
202
|
+
throw new Error(`No default surface theme found for type '${type}'. Add isDefault: true to one '${type}' theme`);
|
|
203
|
+
}
|
|
204
|
+
if (defaultsForType.length > 1) {
|
|
205
|
+
throw new Error(`Multiple default surface themes found for type '${type}': ${defaultsForType.map((t) => t.name).join(', ')}. Only one theme per type can have isDefault: true`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
//#endregion
|
|
210
|
+
//#region CSS Generation
|
|
211
|
+
function createCssSurfaceName(name) {
|
|
212
|
+
return name
|
|
213
|
+
.toLowerCase()
|
|
214
|
+
.replace(/\s+/g, '-')
|
|
215
|
+
.replace(/[^a-z0-9-]/g, '');
|
|
216
|
+
}
|
|
217
|
+
function generateSurfaceThemeCss(themes, prefix, schema) {
|
|
218
|
+
const tailwindVars = [];
|
|
219
|
+
const themeVars = [];
|
|
220
|
+
const themesPath = schema.themesPath || 'src/surface-themes.ts';
|
|
221
|
+
const outputPath = schema.outputPath || 'src/styles/generated-tailwind-surface-themes.css';
|
|
222
|
+
const header = `/*
|
|
223
|
+
* Auto-generated Tailwind 4 surface theme colors from @ethlete/core
|
|
224
|
+
* DO NOT EDIT THIS FILE MANUALLY
|
|
225
|
+
*
|
|
226
|
+
* Generated from your surface theme definitions
|
|
227
|
+
* This file can be regenerated by running:
|
|
228
|
+
* nx g @ethlete/core:tailwind-4-surface-theme --themesPath=${themesPath}${schema.outputPath ? ` --outputPath=${outputPath}` : ''}${schema.prefix && schema.prefix !== 'et' ? ` --prefix=${schema.prefix}` : ''}
|
|
229
|
+
*/
|
|
230
|
+
|
|
231
|
+
`;
|
|
232
|
+
const defaultThemes = themes.filter((t) => t.isDefault);
|
|
233
|
+
const regularThemes = themes.filter((t) => !t.isDefault);
|
|
234
|
+
// Static Tailwind @theme block — one color set per surface theme
|
|
235
|
+
for (const theme of themes) {
|
|
236
|
+
const name = createCssSurfaceName(theme.name);
|
|
237
|
+
tailwindVars.push(` /* ${theme.name} surface */`);
|
|
238
|
+
tailwindVars.push(` --color-${prefix}-surface-${name}-bg: rgb(${theme.background});`);
|
|
239
|
+
tailwindVars.push(` --color-${prefix}-surface-${name}: rgb(${theme.color});`);
|
|
240
|
+
tailwindVars.push(` --color-${prefix}-surface-${name}-muted: rgb(${theme.colorMuted});`);
|
|
241
|
+
tailwindVars.push(` --color-${prefix}-surface-${name}-subtle: rgb(${theme.colorSubtle});`);
|
|
242
|
+
tailwindVars.push(` --color-${prefix}-surface-${name}-border: rgb(${theme.border});`);
|
|
243
|
+
if (theme.interactionColor) {
|
|
244
|
+
tailwindVars.push(` --color-${prefix}-surface-${name}-interaction: rgb(${theme.interactionColor.default});`);
|
|
245
|
+
tailwindVars.push(` --color-${prefix}-surface-${name}-interaction-hover: rgb(${theme.interactionColor.hover});`);
|
|
246
|
+
tailwindVars.push(` --color-${prefix}-surface-${name}-interaction-focus: rgb(${theme.interactionColor.focus});`);
|
|
247
|
+
tailwindVars.push(` --color-${prefix}-surface-${name}-interaction-active: rgb(${theme.interactionColor.active});`);
|
|
248
|
+
tailwindVars.push(` --color-${prefix}-surface-${name}-interaction-disabled: rgb(${theme.interactionColor.disabled});`);
|
|
249
|
+
}
|
|
250
|
+
tailwindVars.push('');
|
|
251
|
+
}
|
|
252
|
+
// Dynamic surface colors (reference runtime CSS variables)
|
|
253
|
+
tailwindVars.push(' /* Dynamic surface colors (references runtime CSS variables) */');
|
|
254
|
+
tailwindVars.push(` --color-${prefix}-surface-bg: rgb(var(--${prefix}-surface-background));`);
|
|
255
|
+
tailwindVars.push(` --color-${prefix}-surface: rgb(var(--${prefix}-surface-color));`);
|
|
256
|
+
tailwindVars.push(` --color-${prefix}-surface-muted: rgb(var(--${prefix}-surface-color-muted));`);
|
|
257
|
+
tailwindVars.push(` --color-${prefix}-surface-subtle: rgb(var(--${prefix}-surface-color-subtle));`);
|
|
258
|
+
tailwindVars.push(` --color-${prefix}-surface-border: rgb(var(--${prefix}-surface-border));`);
|
|
259
|
+
// Dynamic interaction colors (reference runtime CSS variables)
|
|
260
|
+
tailwindVars.push(` --color-${prefix}-surface-interaction: rgb(var(--${prefix}-surface-interaction));`);
|
|
261
|
+
tailwindVars.push(` --color-${prefix}-surface-interaction-hover: rgb(var(--${prefix}-surface-interaction-hover));`);
|
|
262
|
+
tailwindVars.push(` --color-${prefix}-surface-interaction-focus: rgb(var(--${prefix}-surface-interaction-focus));`);
|
|
263
|
+
tailwindVars.push(` --color-${prefix}-surface-interaction-active: rgb(var(--${prefix}-surface-interaction-active));`);
|
|
264
|
+
tailwindVars.push(` --color-${prefix}-surface-interaction-disabled: rgb(var(--${prefix}-surface-interaction-disabled));`);
|
|
265
|
+
// Runtime CSS — theme selector classes
|
|
266
|
+
for (const theme of themes) {
|
|
267
|
+
const name = createCssSurfaceName(theme.name);
|
|
268
|
+
const isDefault = theme.isDefault;
|
|
269
|
+
if (isDefault) {
|
|
270
|
+
const selectors = [`.${prefix}-surface--default-${theme.type}`, `.${prefix}-surface--${name}`];
|
|
271
|
+
themeVars.push(`${selectors.join(', ')} {`);
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
themeVars.push(`.${prefix}-surface--${name} {`);
|
|
275
|
+
}
|
|
276
|
+
pushSurfaceVars(themeVars, prefix, theme, ' ');
|
|
277
|
+
themeVars.push('}\n');
|
|
278
|
+
}
|
|
279
|
+
// Media query defaults — apply surface vars to :root based on prefers-color-scheme
|
|
280
|
+
const defaultLight = themes.find((t) => t.isDefault && t.type === 'light');
|
|
281
|
+
const defaultDark = themes.find((t) => t.isDefault && t.type === 'dark');
|
|
282
|
+
if (defaultLight) {
|
|
283
|
+
themeVars.push(`@media (prefers-color-scheme: light) {`);
|
|
284
|
+
themeVars.push(` :root {`);
|
|
285
|
+
pushSurfaceVars(themeVars, prefix, defaultLight, ' ');
|
|
286
|
+
themeVars.push(` }`);
|
|
287
|
+
themeVars.push(`}\n`);
|
|
288
|
+
}
|
|
289
|
+
if (defaultDark) {
|
|
290
|
+
themeVars.push(`@media (prefers-color-scheme: dark) {`);
|
|
291
|
+
themeVars.push(` :root {`);
|
|
292
|
+
pushSurfaceVars(themeVars, prefix, defaultDark, ' ');
|
|
293
|
+
themeVars.push(` }`);
|
|
294
|
+
themeVars.push(`}\n`);
|
|
295
|
+
}
|
|
296
|
+
// Convenience var aliases — available on any element with a surface class or :root (via media queries)
|
|
297
|
+
const aliasBlock = `/* Convenience aliases (rgb + solid variants) */
|
|
298
|
+
@layer base {
|
|
299
|
+
:root, :where([class*="${prefix}-surface--"]) {
|
|
300
|
+
--${prefix}-surface-background-rgb: var(--${prefix}-surface-background);
|
|
301
|
+
--${prefix}-surface-background-solid: rgb(var(--${prefix}-surface-background-rgb));
|
|
302
|
+
|
|
303
|
+
--${prefix}-surface-color-rgb: var(--${prefix}-surface-color);
|
|
304
|
+
--${prefix}-surface-color-solid: rgb(var(--${prefix}-surface-color-rgb));
|
|
305
|
+
|
|
306
|
+
--${prefix}-surface-color-muted-rgb: var(--${prefix}-surface-color-muted);
|
|
307
|
+
--${prefix}-surface-color-muted-solid: rgb(var(--${prefix}-surface-color-muted-rgb));
|
|
308
|
+
|
|
309
|
+
--${prefix}-surface-color-subtle-rgb: var(--${prefix}-surface-color-subtle);
|
|
310
|
+
--${prefix}-surface-color-subtle-solid: rgb(var(--${prefix}-surface-color-subtle-rgb));
|
|
311
|
+
|
|
312
|
+
--${prefix}-surface-border-rgb: var(--${prefix}-surface-border);
|
|
313
|
+
--${prefix}-surface-border-solid: rgb(var(--${prefix}-surface-border-rgb));
|
|
314
|
+
|
|
315
|
+
--${prefix}-surface-interaction-rgb: var(--${prefix}-surface-interaction);
|
|
316
|
+
--${prefix}-surface-interaction-solid: rgb(var(--${prefix}-surface-interaction-rgb));
|
|
317
|
+
|
|
318
|
+
--${prefix}-surface-interaction-hover-rgb: var(--${prefix}-surface-interaction-hover);
|
|
319
|
+
--${prefix}-surface-interaction-hover-solid: rgb(var(--${prefix}-surface-interaction-hover-rgb));
|
|
320
|
+
|
|
321
|
+
--${prefix}-surface-interaction-focus-rgb: var(--${prefix}-surface-interaction-focus);
|
|
322
|
+
--${prefix}-surface-interaction-focus-solid: rgb(var(--${prefix}-surface-interaction-focus-rgb));
|
|
323
|
+
|
|
324
|
+
--${prefix}-surface-interaction-active-rgb: var(--${prefix}-surface-interaction-active);
|
|
325
|
+
--${prefix}-surface-interaction-active-solid: rgb(var(--${prefix}-surface-interaction-active-rgb));
|
|
326
|
+
|
|
327
|
+
--${prefix}-surface-interaction-disabled-rgb: var(--${prefix}-surface-interaction-disabled);
|
|
328
|
+
--${prefix}-surface-interaction-disabled-solid: rgb(var(--${prefix}-surface-interaction-disabled-rgb));
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
`;
|
|
332
|
+
return `${header}@theme {
|
|
333
|
+
${tailwindVars.join('\n')}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
@layer base {
|
|
337
|
+
${themeVars.join('\n')}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
${aliasBlock}`;
|
|
341
|
+
}
|
|
342
|
+
function pushSurfaceVars(vars, prefix, theme, indent) {
|
|
343
|
+
vars.push(`${indent}--${prefix}-surface-background: ${theme.background};`);
|
|
344
|
+
vars.push(`${indent}--${prefix}-surface-color: ${theme.color};`);
|
|
345
|
+
vars.push(`${indent}--${prefix}-surface-color-muted: ${theme.colorMuted};`);
|
|
346
|
+
vars.push(`${indent}--${prefix}-surface-color-subtle: ${theme.colorSubtle};`);
|
|
347
|
+
vars.push(`${indent}--${prefix}-surface-border: ${theme.border};`);
|
|
348
|
+
vars.push(`${indent}--${prefix}-surface-type: ${theme.type};`);
|
|
349
|
+
vars.push(`${indent}--${prefix}-surface-elevation: ${theme.elevation};`);
|
|
350
|
+
if (theme.interactionColor) {
|
|
351
|
+
vars.push(`${indent}--${prefix}-surface-interaction: ${theme.interactionColor.default};`);
|
|
352
|
+
vars.push(`${indent}--${prefix}-surface-interaction-hover: ${theme.interactionColor.hover};`);
|
|
353
|
+
vars.push(`${indent}--${prefix}-surface-interaction-focus: ${theme.interactionColor.focus};`);
|
|
354
|
+
vars.push(`${indent}--${prefix}-surface-interaction-active: ${theme.interactionColor.active};`);
|
|
355
|
+
vars.push(`${indent}--${prefix}-surface-interaction-disabled: ${theme.interactionColor.disabled};`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
function findMainStylesFile(tree) {
|
|
359
|
+
const potentialFiles = [];
|
|
360
|
+
visitNotIgnoredFiles(tree, '', (path) => {
|
|
361
|
+
if (path.match(/styles\.(css|scss)$/) && !path.includes('node_modules') && !path.includes('dist')) {
|
|
362
|
+
potentialFiles.push(path);
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
return potentialFiles;
|
|
366
|
+
}
|
|
367
|
+
//#endregion
|
|
368
|
+
//# sourceMappingURL=generator.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/schema",
|
|
3
|
+
"$id": "tailwind-4-surface-theme",
|
|
4
|
+
"title": "Tailwind 4 Surface Theming Generator",
|
|
5
|
+
"description": "Generates Tailwind 4 compatible CSS from your Ethlete surface theme definitions",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"themesPath": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "Path to your surface themes file (relative to workspace root)",
|
|
11
|
+
"default": "src/surface-themes.ts",
|
|
12
|
+
"x-prompt": "Where is your surface themes file located?"
|
|
13
|
+
},
|
|
14
|
+
"outputPath": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "Where to output the generated Tailwind surface themes CSS",
|
|
17
|
+
"default": "src/styles/generated-tailwind-surface-themes.css",
|
|
18
|
+
"x-prompt": "Where should the generated CSS be saved?"
|
|
19
|
+
},
|
|
20
|
+
"prefix": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"description": "Prefix for surface variable names (e.g., 'et' creates --color-et-surface-*)",
|
|
23
|
+
"default": "et"
|
|
24
|
+
},
|
|
25
|
+
"skipFormat": {
|
|
26
|
+
"type": "boolean",
|
|
27
|
+
"description": "Skip formatting files after generation",
|
|
28
|
+
"default": false
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"required": []
|
|
32
|
+
}
|
package/package.json
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ethlete/core",
|
|
3
|
-
"version": "5.0.0-next.
|
|
3
|
+
"version": "5.0.0-next.9",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"peerDependencies": {
|
|
7
|
-
"@analogjs/vite-plugin-angular": "2.
|
|
8
|
-
"@angular/cdk": "21.2.
|
|
9
|
-
"@angular/common": "21.2.
|
|
10
|
-
"@angular/core": "21.2.
|
|
11
|
-
"@angular/forms": "21.2.
|
|
12
|
-
"@angular/platform-browser": "21.2.
|
|
13
|
-
"@angular/router": "21.2.
|
|
7
|
+
"@analogjs/vite-plugin-angular": "2.5.1",
|
|
8
|
+
"@angular/cdk": "21.2.11",
|
|
9
|
+
"@angular/common": "21.2.13",
|
|
10
|
+
"@angular/core": "21.2.13",
|
|
11
|
+
"@angular/forms": "21.2.13",
|
|
12
|
+
"@angular/platform-browser": "21.2.13",
|
|
13
|
+
"@angular/router": "21.2.13",
|
|
14
14
|
"@ethlete/types": "^2.0.0-next.0",
|
|
15
15
|
"@floating-ui/dom": "1.7.6",
|
|
16
|
-
"@nx/devkit": "22.
|
|
17
|
-
"@nx/vite": "22.
|
|
16
|
+
"@nx/devkit": "22.7.2",
|
|
17
|
+
"@nx/vite": "22.7.2",
|
|
18
18
|
"rxjs": "7.8.2",
|
|
19
19
|
"ts-morph": "21.0.1",
|
|
20
20
|
"typescript": "5.9.3",
|
|
21
|
-
"vite": "
|
|
21
|
+
"vite": "8.0.13"
|
|
22
22
|
},
|
|
23
23
|
"generators": "./generators/generators.json",
|
|
24
24
|
"module": "fesm2022/ethlete-core.mjs",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"default": "./fesm2022/ethlete-core.mjs"
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
|
+
"type": "module",
|
|
35
36
|
"dependencies": {
|
|
36
37
|
"tslib": "^2.3.0"
|
|
37
38
|
}
|