@happyvertical/smrt-ui 0.30.0 → 0.31.1
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/dist/actions/__tests__/ripple.test.js +55 -1
- package/dist/actions/ripple.d.ts +9 -1
- package/dist/actions/ripple.d.ts.map +1 -1
- package/dist/actions/ripple.js +56 -7
- package/dist/components/display/ConfidenceBadge.svelte +14 -10
- package/dist/components/display/ConfidenceBadge.svelte.d.ts.map +1 -1
- package/dist/components/display/StatusBadge.svelte +81 -75
- package/dist/components/display/StatusBadge.svelte.d.ts.map +1 -1
- package/dist/components/display/__tests__/ConfidenceBadge.test.js +13 -0
- package/dist/components/display/__tests__/StatusBadge.test.js +17 -2
- package/dist/components/feedback/ConfirmDialog.svelte +78 -2
- package/dist/components/feedback/ConfirmDialog.svelte.d.ts.map +1 -1
- package/dist/components/feedback/LoadingOverlay.svelte +6 -1
- package/dist/components/feedback/LoadingOverlay.svelte.d.ts.map +1 -1
- package/dist/components/feedback/__tests__/ConfirmDialog.test.js +55 -3
- package/dist/components/feedback/__tests__/LoadingOverlay.test.js +14 -0
- package/dist/components/roles/RoleSelector.svelte +106 -13
- package/dist/components/roles/RoleSelector.svelte.d.ts +10 -0
- package/dist/components/roles/RoleSelector.svelte.d.ts.map +1 -1
- package/dist/components/roles/__tests__/RoleSelector.test.js +134 -0
- package/dist/components/ui/Button.svelte +12 -2
- package/dist/components/ui/Pagination.svelte +3 -1
- package/dist/components/ui/Pagination.svelte.d.ts.map +1 -1
- package/dist/components/ui/__tests__/Pagination.test.js +14 -0
- package/dist/themes/__tests__/create-theme.test.js +79 -0
- package/dist/themes/__tests__/css-generator.test.js +55 -0
- package/dist/themes/create-theme.d.ts.map +1 -1
- package/dist/themes/create-theme.js +29 -22
- package/dist/themes/css-generator.d.ts.map +1 -1
- package/dist/themes/css-generator.js +4 -1
- package/dist/themes/studio/index.d.ts.map +1 -1
- package/dist/themes/studio/index.js +19 -1
- package/dist/themes/types.d.ts +8 -1
- package/dist/themes/types.d.ts.map +1 -1
- package/dist/utils/theme/__tests__/color.test.js +17 -0
- package/dist/utils/theme/color.d.ts +11 -0
- package/dist/utils/theme/color.d.ts.map +1 -1
- package/dist/utils/theme/color.js +39 -4
- package/package.json +2 -2
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression tests for createTheme color handling (#1586, epic #1354).
|
|
3
|
+
*
|
|
4
|
+
* The dark-color derivation and primary-container derivation used to assume the
|
|
5
|
+
* primary was 6-digit hex. For `rgb()`, 3-digit hex, or a named color they
|
|
6
|
+
* emitted invalid CSS — `rgb(0,122,255)20`, `#f6320`, `#NaNNaNNaN`. Inputs are
|
|
7
|
+
* now normalized to RGB first (or rejected with a clear error), so the generated
|
|
8
|
+
* palette is always valid CSS.
|
|
9
|
+
*/
|
|
10
|
+
import { describe, expect, it } from 'vitest';
|
|
11
|
+
import { createTheme } from '../create-theme';
|
|
12
|
+
const HEX6 = /^#[0-9a-fA-F]{6}$/;
|
|
13
|
+
const HEX_ALPHA = /^#[0-9a-fA-F]{8}$/;
|
|
14
|
+
/** Every color value in a palette must be syntactically valid CSS. */
|
|
15
|
+
function expectAllColorsValid(colors) {
|
|
16
|
+
for (const [key, value] of Object.entries(colors)) {
|
|
17
|
+
expect(typeof value).toBe('string');
|
|
18
|
+
// The regression: derivations used to emit '#NaNNaNNaN' / 'rgb(...)20'.
|
|
19
|
+
expect(value, `${key}=${value}`).not.toContain('NaN');
|
|
20
|
+
// Hex values must be well-formed: exactly 3, 4, 6, or 8 digits (all valid
|
|
21
|
+
// CSS). A 5- or 7-digit hex (e.g. the '#f6320' the bug produced) must fail.
|
|
22
|
+
// rgb()/rgba() and other formats pass through unchanged.
|
|
23
|
+
if (value.startsWith('#')) {
|
|
24
|
+
expect(value, `${key}=${value}`).toMatch(/^#([0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
describe('createTheme color handling (#1586)', () => {
|
|
29
|
+
it('derives a valid primary container from a 6-digit hex primary', () => {
|
|
30
|
+
const theme = createTheme({
|
|
31
|
+
id: 'brand6',
|
|
32
|
+
name: 'Brand 6',
|
|
33
|
+
light: { primary: '#0b57d0', background: '#ffffff' },
|
|
34
|
+
});
|
|
35
|
+
// 8-digit hex alpha form: primary + "20".
|
|
36
|
+
expect(theme.light.primaryContainer).toMatch(HEX_ALPHA);
|
|
37
|
+
expect(theme.light.primaryContainer.toLowerCase()).toBe('#0b57d020');
|
|
38
|
+
expectAllColorsValid(theme.light);
|
|
39
|
+
expectAllColorsValid(theme.dark);
|
|
40
|
+
});
|
|
41
|
+
it('normalizes an rgb() primary instead of emitting invalid CSS', () => {
|
|
42
|
+
const theme = createTheme({
|
|
43
|
+
id: 'brandRgb',
|
|
44
|
+
name: 'Brand RGB',
|
|
45
|
+
light: { primary: 'rgb(0, 122, 255)', background: '#ffffff' },
|
|
46
|
+
});
|
|
47
|
+
// Was 'rgb(0, 122, 255)20' (invalid); now a normalized 8-digit hex.
|
|
48
|
+
expect(theme.light.primaryContainer).not.toContain('rgb(');
|
|
49
|
+
expect(theme.light.primaryContainer).toMatch(HEX_ALPHA);
|
|
50
|
+
expect(theme.light.primaryContainer.toLowerCase()).toBe('#007aff20');
|
|
51
|
+
// Auto-generated dark colors used to be '#NaNNaNNaN' for an rgb() input;
|
|
52
|
+
// every value is now valid CSS (hex values are well-formed, rgb() values
|
|
53
|
+
// pass through unchanged).
|
|
54
|
+
expectAllColorsValid(theme.light);
|
|
55
|
+
expectAllColorsValid(theme.dark);
|
|
56
|
+
// The brightness-derived dark colors normalize to proper hex.
|
|
57
|
+
expect(theme.dark.onPrimary).toMatch(HEX6);
|
|
58
|
+
expect(theme.dark.secondary).toMatch(HEX6);
|
|
59
|
+
});
|
|
60
|
+
it('expands a 3-digit hex primary correctly', () => {
|
|
61
|
+
const theme = createTheme({
|
|
62
|
+
id: 'brand3',
|
|
63
|
+
name: 'Brand 3',
|
|
64
|
+
light: { primary: '#07f', background: '#fff' },
|
|
65
|
+
});
|
|
66
|
+
// '#07f' -> '#0077ff', container '#0077ff20'.
|
|
67
|
+
expect(theme.light.primaryContainer.toLowerCase()).toBe('#0077ff20');
|
|
68
|
+
expectAllColorsValid(theme.light);
|
|
69
|
+
expectAllColorsValid(theme.dark);
|
|
70
|
+
expect(theme.dark.onPrimary).toMatch(HEX6);
|
|
71
|
+
});
|
|
72
|
+
it('throws a clear error for an unsupported named color', () => {
|
|
73
|
+
expect(() => createTheme({
|
|
74
|
+
id: 'brandNamed',
|
|
75
|
+
name: 'Brand Named',
|
|
76
|
+
light: { primary: 'tomato', background: '#ffffff' },
|
|
77
|
+
})).toThrow(/unsupported color "tomato"/);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -1,9 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Unit tests for the theme CSS generator (coverage uplift, S6 gate).
|
|
3
3
|
*/
|
|
4
|
+
import { readFileSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
4
6
|
import { describe, expect, it } from 'vitest';
|
|
5
7
|
import { generateAllThemesCSS, generateThemeCSS, generateThemeVariables, variablesToStyleString, } from '../css-generator';
|
|
6
8
|
import { getTheme } from '../registry';
|
|
9
|
+
// Raw CSS text of the static studio preset, used to prove the static-CSS
|
|
10
|
+
// delivery path and the JS generator emit identical elevation values (#1586).
|
|
11
|
+
// Resolved from process.cwd() (NOT import.meta.url): the smrt-vitest plugin runs
|
|
12
|
+
// each package's suite with cwd === the package root, so this is stable here,
|
|
13
|
+
// whereas `new URL('…', import.meta.url)` fails to resolve under the vite-node
|
|
14
|
+
// loader this suite runs in.
|
|
15
|
+
const studioCss = readFileSync(join(process.cwd(), 'src/themes/styles/studio.css'), 'utf8');
|
|
16
|
+
/**
|
|
17
|
+
* Pull the `--smrt-elevation-0..5` values out of a specific
|
|
18
|
+
* `[data-theme="<preset>"][data-color-scheme="<scheme>"]` block of a static CSS
|
|
19
|
+
* preset file.
|
|
20
|
+
*/
|
|
21
|
+
function staticElevation(css, preset, scheme) {
|
|
22
|
+
const selector = `[data-theme="${preset}"][data-color-scheme="${scheme}"]`;
|
|
23
|
+
const start = css.indexOf(selector);
|
|
24
|
+
if (start === -1)
|
|
25
|
+
throw new Error(`block not found: ${selector}`);
|
|
26
|
+
const open = css.indexOf('{', start);
|
|
27
|
+
const close = css.indexOf('}', open);
|
|
28
|
+
const body = css.slice(open + 1, close);
|
|
29
|
+
const out = {};
|
|
30
|
+
for (const line of body.split('\n')) {
|
|
31
|
+
const m = line.match(/(--smrt-elevation-\d):\s*(.+);/);
|
|
32
|
+
if (m)
|
|
33
|
+
out[m[1]] = m[2].trim();
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
7
37
|
describe('theme CSS generator', () => {
|
|
8
38
|
const theme = getTheme('material');
|
|
9
39
|
it('generateThemeVariables returns --smrt-* custom properties', () => {
|
|
@@ -29,4 +59,29 @@ describe('theme CSS generator', () => {
|
|
|
29
59
|
const css = await generateAllThemesCSS();
|
|
30
60
|
expect(css.length).toBeGreaterThan(0);
|
|
31
61
|
});
|
|
62
|
+
describe('studio per-scheme elevation (#1586)', () => {
|
|
63
|
+
const studio = getTheme('studio');
|
|
64
|
+
it('emits distinct dark-scheme elevation, not the light values', () => {
|
|
65
|
+
const light = generateThemeVariables(studio, false);
|
|
66
|
+
const dark = generateThemeVariables(studio, true);
|
|
67
|
+
// Level 1 is a white inset hairline in dark vs a tinted inset in light.
|
|
68
|
+
expect(dark['--smrt-elevation-1']).toContain('rgba(255, 255, 255');
|
|
69
|
+
expect(light['--smrt-elevation-1']).toContain('rgba(60, 64, 67');
|
|
70
|
+
expect(dark['--smrt-elevation-1']).not.toBe(light['--smrt-elevation-1']);
|
|
71
|
+
// Deeper black shadows above level 1.
|
|
72
|
+
expect(dark['--smrt-elevation-5']).toContain('rgba(0, 0, 0, 0.25)');
|
|
73
|
+
});
|
|
74
|
+
it('matches the static studio.css for both schemes (drift guard)', () => {
|
|
75
|
+
// The static-CSS delivery path and the JS generator must agree on VALUES,
|
|
76
|
+
// not just token names — this is what let the studio dark shadows diverge.
|
|
77
|
+
for (const scheme of ['light', 'dark']) {
|
|
78
|
+
const generated = generateThemeVariables(studio, scheme === 'dark');
|
|
79
|
+
const fromCss = staticElevation(studioCss, 'studio', scheme);
|
|
80
|
+
for (let level = 0; level <= 5; level += 1) {
|
|
81
|
+
const key = `--smrt-elevation-${level}`;
|
|
82
|
+
expect(fromCss[key], `${scheme} ${key}`).toBe(generated[key]);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
});
|
|
32
87
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-theme.d.ts","sourceRoot":"","sources":["../../src/themes/create-theme.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"create-theme.d.ts","sourceRoot":"","sources":["../../src/themes/create-theme.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAcH,OAAO,KAAK,EACV,YAAY,EACZ,WAAW,EACX,cAAc,EACd,YAAY,EACZ,KAAK,EACL,WAAW,EACX,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,wCAAwC;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,YAAY,CAAC,CAAC;IAC5E,iFAAiF;IACjF,IAAI,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;IAC7B,wDAAwD;IACxD,UAAU,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IACtC,mCAAmC;IACnC,SAAS,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IACpC,sCAAsC;IACtC,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAC9B,+BAA+B;IAC/B,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,kBAAkB;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAyPD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,KAAK,CAsD9D;AAkBD;;;GAGG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAG1D;AAgBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAEhD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAIjE;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAEpE;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,MAAM,EACpB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,IAAI,GAAG,MAAM,GAAG,OAAO,CAAC,GAC1D,KAAK,CAWP"}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Helper functions for creating custom themes that match the SMRT theme system.
|
|
5
5
|
*/
|
|
6
|
+
import { hexToRgb, rgbToHex } from '../utils/theme/color.js';
|
|
6
7
|
import { hasCustomTheme, registerCustomTheme, themes as registeredThemes, } from './registry.js';
|
|
7
8
|
import { borderRadiusScale, durationScale, materialEasing, spacingScale, } from './shared.js';
|
|
8
9
|
/**
|
|
@@ -54,34 +55,36 @@ const defaultColorStructure = {
|
|
|
54
55
|
scrim: 'rgba(0, 0, 0, 0.5)',
|
|
55
56
|
};
|
|
56
57
|
/**
|
|
57
|
-
*
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
* Helper function to convert RGB component to 2-digit hex
|
|
58
|
+
* Normalize any supported CSS color string to a canonical 6-digit hex.
|
|
59
|
+
*
|
|
60
|
+
* Accepts 6-digit hex, 3-digit shorthand hex, and `rgb()`/`rgba()` (via the
|
|
61
|
+
* shared `hexToRgb`). Throws on input that can't be parsed (e.g. a named color
|
|
62
|
+
* like `tomato`) so an unsupported value fails loudly instead of silently
|
|
63
|
+
* producing invalid CSS like `#NaNNaNNaN` or `rgb(...)20` (#1586).
|
|
64
64
|
*/
|
|
65
|
-
function
|
|
66
|
-
const
|
|
67
|
-
|
|
65
|
+
function normalizeHex(color) {
|
|
66
|
+
const { r, g, b } = hexToRgb(color);
|
|
67
|
+
if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) {
|
|
68
|
+
throw new Error(`createTheme: unsupported color "${color}". Use 6-digit hex (#rrggbb), ` +
|
|
69
|
+
'3-digit hex (#rgb), or rgb()/rgba() notation.');
|
|
70
|
+
}
|
|
71
|
+
return rgbToHex(r, g, b);
|
|
68
72
|
}
|
|
69
73
|
/**
|
|
70
74
|
* Generate dark mode colors from light mode colors
|
|
71
75
|
* Uses simple inversion logic - for production use, manually define dark colors
|
|
72
76
|
*/
|
|
73
77
|
function generateDarkColors(light) {
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
// Adjust color brightness by a multiplicative factor. Normalizes the input to
|
|
79
|
+
// RGB first (via the shared parser), so 3-digit hex and rgb()/rgba() inputs no
|
|
80
|
+
// longer NaN-poison the output into `#NaNNaNNaN` (#1586).
|
|
81
|
+
const adjustBrightness = (color, factor) => {
|
|
82
|
+
const { r, g, b } = hexToRgb(color);
|
|
83
|
+
if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) {
|
|
84
|
+
throw new Error(`createTheme: unsupported color "${color}". Use 6-digit hex (#rrggbb), ` +
|
|
85
|
+
'3-digit hex (#rgb), or rgb()/rgba() notation.');
|
|
80
86
|
}
|
|
81
|
-
|
|
82
|
-
const g = parseInt(fullHex.slice(3, 5), 16) * factor;
|
|
83
|
-
const b = parseInt(fullHex.slice(5, 7), 16) * factor;
|
|
84
|
-
return `#${toHexComponent(r)}${toHexComponent(g)}${toHexComponent(b)}`;
|
|
87
|
+
return rgbToHex(r * factor, g * factor, b * factor);
|
|
85
88
|
};
|
|
86
89
|
return {
|
|
87
90
|
...light,
|
|
@@ -266,8 +269,12 @@ export function createTheme(options) {
|
|
|
266
269
|
...defaultColorStructure,
|
|
267
270
|
...baseTheme?.light,
|
|
268
271
|
...lightPartial,
|
|
269
|
-
// Generate derived colors
|
|
270
|
-
|
|
272
|
+
// Generate derived colors. The container is the primary at ~12.5% alpha,
|
|
273
|
+
// expressed as 8-digit hex. Normalize the primary to canonical 6-hex first
|
|
274
|
+
// so a 3-digit hex or rgb()/rgba() primary can't yield invalid CSS like
|
|
275
|
+
// `rgb(0,122,255)20` or `#f6320` (#1586).
|
|
276
|
+
primaryContainer: lightPartial.primaryContainer ||
|
|
277
|
+
`${normalizeHex(lightPartial.primary)}20`,
|
|
271
278
|
onPrimaryContainer: lightPartial.onPrimaryContainer || lightPartial.primary,
|
|
272
279
|
inversePrimary: lightPartial.inversePrimary || lightPartial.primary,
|
|
273
280
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"css-generator.d.ts","sourceRoot":"","sources":["../../src/themes/css-generator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,OAAO,KAAK,EAQV,KAAK,EAEN,MAAM,YAAY,CAAC;AAEpB;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AA+LD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,OAAO,EACf,OAAO,GAAE,kBAAuB,GAC/B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"css-generator.d.ts","sourceRoot":"","sources":["../../src/themes/css-generator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,OAAO,KAAK,EAQV,KAAK,EAEN,MAAM,YAAY,CAAC;AAEpB;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AA+LD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,OAAO,EACf,OAAO,GAAE,kBAAuB,GAC/B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA0CxB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC,MAAM,CAIR;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,OAAO,EACf,OAAO,GAAE,kBAAuB,GAC/B,MAAM,CAaR;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC,CAS5D"}
|
|
@@ -157,6 +157,9 @@ function generateStaticTokens(prefix) {
|
|
|
157
157
|
export function generateThemeVariables(theme, isDark, options = {}) {
|
|
158
158
|
const { prefix = '--smrt' } = options;
|
|
159
159
|
const colors = isDark ? theme.dark : theme.light;
|
|
160
|
+
// Per-scheme elevation: dark uses `darkElevation` when the preset defines it,
|
|
161
|
+
// so the JS generator and the static dark CSS stay in lockstep (#1586).
|
|
162
|
+
const elevation = isDark && theme.darkElevation ? theme.darkElevation : theme.elevation;
|
|
160
163
|
return {
|
|
161
164
|
// Theme identification
|
|
162
165
|
[`${prefix}-theme-id`]: theme.id,
|
|
@@ -172,7 +175,7 @@ export function generateThemeVariables(theme, isDark, options = {}) {
|
|
|
172
175
|
// Border radius
|
|
173
176
|
...generateBorderRadiusVariables(theme.borderRadius, prefix),
|
|
174
177
|
// Elevation
|
|
175
|
-
...generateElevationVariables(
|
|
178
|
+
...generateElevationVariables(elevation, prefix),
|
|
176
179
|
// Duration
|
|
177
180
|
...generateDurationVariables(theme.duration, prefix),
|
|
178
181
|
// Easing
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/themes/studio/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,OAAO,KAAK,EAGV,KAAK,EAEN,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/themes/studio/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,OAAO,KAAK,EAGV,KAAK,EAEN,MAAM,aAAa,CAAC;AAiSrB;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,KAazB,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
|
@@ -240,7 +240,7 @@ const typography = {
|
|
|
240
240
|
},
|
|
241
241
|
};
|
|
242
242
|
/**
|
|
243
|
-
* Studio elevation - Minimal, subtle shadows
|
|
243
|
+
* Studio elevation (light) - Minimal, subtle shadows
|
|
244
244
|
* Flat design philosophy with only slight depth indication
|
|
245
245
|
*/
|
|
246
246
|
const elevation = {
|
|
@@ -251,6 +251,23 @@ const elevation = {
|
|
|
251
251
|
4: '0 4px 8px 0 rgba(60, 64, 67, 0.1), 0 2px 4px 0 rgba(60, 64, 67, 0.06)',
|
|
252
252
|
5: '0 8px 16px 0 rgba(60, 64, 67, 0.12), 0 4px 8px 0 rgba(60, 64, 67, 0.06)',
|
|
253
253
|
};
|
|
254
|
+
/**
|
|
255
|
+
* Studio elevation (dark) - Tuned for a near-black surface.
|
|
256
|
+
*
|
|
257
|
+
* The flat light shadows (tinted with the light surface color, very low alpha)
|
|
258
|
+
* read as invisible on a dark surface, so dark mode uses a white inset hairline
|
|
259
|
+
* at level 1 and deeper black shadows above it. Defining this here — rather than
|
|
260
|
+
* only in the static `styles/studio.css` — keeps the JS ThemeProvider output and
|
|
261
|
+
* the static dark CSS in lockstep (#1586).
|
|
262
|
+
*/
|
|
263
|
+
const darkElevation = {
|
|
264
|
+
0: 'none',
|
|
265
|
+
1: 'inset 0 0 0 1px rgba(255, 255, 255, 0.08)',
|
|
266
|
+
2: '0 1px 2px 0 rgba(0, 0, 0, 0.2), 0 1px 3px 0 rgba(0, 0, 0, 0.1)',
|
|
267
|
+
3: '0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 1px 2px 0 rgba(0, 0, 0, 0.1)',
|
|
268
|
+
4: '0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 2px 4px 0 rgba(0, 0, 0, 0.1)',
|
|
269
|
+
5: '0 8px 16px 0 rgba(0, 0, 0, 0.25), 0 4px 8px 0 rgba(0, 0, 0, 0.1)',
|
|
270
|
+
};
|
|
254
271
|
/**
|
|
255
272
|
* Studio theme definition
|
|
256
273
|
*/
|
|
@@ -261,6 +278,7 @@ export const studioTheme = {
|
|
|
261
278
|
dark: darkColors,
|
|
262
279
|
typography,
|
|
263
280
|
elevation,
|
|
281
|
+
darkElevation,
|
|
264
282
|
spacing: spacingScale,
|
|
265
283
|
borderRadius: borderRadiusScale,
|
|
266
284
|
duration: durationScale,
|
package/dist/themes/types.d.ts
CHANGED
|
@@ -191,8 +191,15 @@ export interface Theme {
|
|
|
191
191
|
dark: ColorPalette;
|
|
192
192
|
/** Typography scale */
|
|
193
193
|
typography: TypographyScale;
|
|
194
|
-
/** Elevation shadows */
|
|
194
|
+
/** Elevation shadows (used for both schemes unless `darkElevation` is set) */
|
|
195
195
|
elevation: ElevationScale;
|
|
196
|
+
/**
|
|
197
|
+
* Optional dark-scheme elevation overrides. When present, the CSS generator
|
|
198
|
+
* emits these for the dark scheme instead of `elevation`. Lets a preset tune
|
|
199
|
+
* shadows for a dark surface (e.g. studio's punchier dark shadows) so the JS
|
|
200
|
+
* generator and the static dark CSS can't diverge (#1586).
|
|
201
|
+
*/
|
|
202
|
+
darkElevation?: ElevationScale;
|
|
196
203
|
/** Spacing scale */
|
|
197
204
|
spacing: SpacingScale;
|
|
198
205
|
/** Border radius scale */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/themes/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/themes/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,6 +6,23 @@ describe('Theme Color Utilities', () => {
|
|
|
6
6
|
expect(hexToRgb('#ffffff')).toEqual({ r: 255, g: 255, b: 255 });
|
|
7
7
|
expect(hexToRgb('#FF5733')).toEqual({ r: 255, g: 87, b: 51 });
|
|
8
8
|
});
|
|
9
|
+
it('expands 3-digit shorthand hex (#abc -> #aabbcc) (#1586)', () => {
|
|
10
|
+
// Regression: shorthand hex used to NaN-poison the b channel (only the first
|
|
11
|
+
// 2 chars were read), corrupting any palette generated from a 3-digit seed.
|
|
12
|
+
expect(hexToRgb('#abc')).toEqual({ r: 170, g: 187, b: 204 });
|
|
13
|
+
expect(hexToRgb('#000')).toEqual({ r: 0, g: 0, b: 0 });
|
|
14
|
+
expect(hexToRgb('#fff')).toEqual({ r: 255, g: 255, b: 255 });
|
|
15
|
+
});
|
|
16
|
+
it('parses functional rgb()/rgba() notation (#1586)', () => {
|
|
17
|
+
expect(hexToRgb('rgb(0, 122, 255)')).toEqual({ r: 0, g: 122, b: 255 });
|
|
18
|
+
expect(hexToRgb('rgb(0 122 255)')).toEqual({ r: 0, g: 122, b: 255 });
|
|
19
|
+
expect(hexToRgb('rgba(0, 122, 255, 0.5)')).toEqual({
|
|
20
|
+
r: 0,
|
|
21
|
+
g: 122,
|
|
22
|
+
b: 255,
|
|
23
|
+
});
|
|
24
|
+
expect(hexToRgb('rgb(100%, 0%, 50%)')).toEqual({ r: 255, g: 0, b: 128 });
|
|
25
|
+
});
|
|
9
26
|
it('should convert rgb to hex correctly', () => {
|
|
10
27
|
expect(rgbToHex(0, 0, 0)).toBe('#000000');
|
|
11
28
|
expect(rgbToHex(255, 255, 255)).toBe('#ffffff');
|
|
@@ -46,6 +46,17 @@ export interface Theme {
|
|
|
46
46
|
surfaceContainerHigh: string;
|
|
47
47
|
surfaceContainerHighest: string;
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Parse a CSS color string into an {@link Rgb}.
|
|
51
|
+
*
|
|
52
|
+
* Accepts 6-digit hex (`#rrggbb`), 3-digit shorthand hex (`#rgb`, expanded by
|
|
53
|
+
* doubling each nibble), and functional `rgb()` / `rgba()` notation (commas or
|
|
54
|
+
* spaces, integer or percentage channels). Named colors and other formats are
|
|
55
|
+
* not supported and yield `{ r: NaN, g: NaN, b: NaN }`; callers that accept
|
|
56
|
+
* arbitrary input should validate before relying on the result.
|
|
57
|
+
*
|
|
58
|
+
* The leading `#` is optional for hex input.
|
|
59
|
+
*/
|
|
49
60
|
export declare function hexToRgb(hex: string): Rgb;
|
|
50
61
|
export declare function rgbToHex(r: number, g: number, b: number): string;
|
|
51
62
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"color.d.ts","sourceRoot":"","sources":["../../../src/utils/theme/color.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,GAAG;IAClB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,GAAG;IAClB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAGD,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,uBAAuB,EAAE,MAAM,CAAC;CACjC;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,
|
|
1
|
+
{"version":3,"file":"color.d.ts","sourceRoot":"","sources":["../../../src/utils/theme/color.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,GAAG;IAClB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,GAAG;IAClB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAGD,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,uBAAuB,EAAE,MAAM,CAAC;CACjC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAkCzC;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAMhE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAM7C;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,GAAG,MAAM,CAMzD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,EAAE,EAAE,MAAM,GAAG,GAAG,EAChB,EAAE,EAAE,MAAM,GAAG,GAAG,EAChB,SAAS,UAAQ,GAChB,OAAO,CAKT;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,MAAM,GAAG,GAAG,EAChB,EAAE,EAAE,MAAM,GAAG,GAAG,EAChB,SAAS,UAAQ,GAChB,OAAO,CAKT;AAmED,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE,OAAO,GAAG,MAAgB,GAC/B,KAAK,CA2HP"}
|
|
@@ -1,8 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a CSS color string into an {@link Rgb}.
|
|
3
|
+
*
|
|
4
|
+
* Accepts 6-digit hex (`#rrggbb`), 3-digit shorthand hex (`#rgb`, expanded by
|
|
5
|
+
* doubling each nibble), and functional `rgb()` / `rgba()` notation (commas or
|
|
6
|
+
* spaces, integer or percentage channels). Named colors and other formats are
|
|
7
|
+
* not supported and yield `{ r: NaN, g: NaN, b: NaN }`; callers that accept
|
|
8
|
+
* arbitrary input should validate before relying on the result.
|
|
9
|
+
*
|
|
10
|
+
* The leading `#` is optional for hex input.
|
|
11
|
+
*/
|
|
1
12
|
export function hexToRgb(hex) {
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
13
|
+
const input = hex.trim();
|
|
14
|
+
// Functional rgb()/rgba() notation, e.g. rgb(0, 122, 255) or rgb(0 122 255 / .5).
|
|
15
|
+
const rgbMatch = input.match(/^rgba?\(([^)]+)\)$/i);
|
|
16
|
+
if (rgbMatch) {
|
|
17
|
+
const channels = rgbMatch[1]
|
|
18
|
+
.split(/[\s,/]+/)
|
|
19
|
+
.filter(Boolean)
|
|
20
|
+
.slice(0, 3);
|
|
21
|
+
const toChannel = (part) => {
|
|
22
|
+
if (part.endsWith('%')) {
|
|
23
|
+
return Math.round((Number.parseFloat(part) / 100) * 255);
|
|
24
|
+
}
|
|
25
|
+
return Number.parseInt(part, 10);
|
|
26
|
+
};
|
|
27
|
+
const [r, g, b] = channels.map(toChannel);
|
|
28
|
+
return { r, g, b };
|
|
29
|
+
}
|
|
30
|
+
const cleanHex = input.replace('#', '');
|
|
31
|
+
// 3-digit shorthand hex: #abc -> #aabbcc.
|
|
32
|
+
if (cleanHex.length === 3) {
|
|
33
|
+
const r = Number.parseInt(cleanHex[0] + cleanHex[0], 16);
|
|
34
|
+
const g = Number.parseInt(cleanHex[1] + cleanHex[1], 16);
|
|
35
|
+
const b = Number.parseInt(cleanHex[2] + cleanHex[2], 16);
|
|
36
|
+
return { r, g, b };
|
|
37
|
+
}
|
|
38
|
+
const r = Number.parseInt(cleanHex.substring(0, 2), 16);
|
|
39
|
+
const g = Number.parseInt(cleanHex.substring(2, 4), 16);
|
|
40
|
+
const b = Number.parseInt(cleanHex.substring(4, 6), 16);
|
|
6
41
|
return { r, g, b };
|
|
7
42
|
}
|
|
8
43
|
export function rgbToHex(r, g, b) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/smrt-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.31.1",
|
|
4
4
|
"description": "Domain-agnostic Svelte 5 UI runtime for SMRT: primitives, i18n client, theme system, and module UI registry",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
},
|
|
109
109
|
"dependencies": {
|
|
110
110
|
"esm-env": "^1.2.2",
|
|
111
|
-
"@happyvertical/smrt-types": "0.
|
|
111
|
+
"@happyvertical/smrt-types": "0.31.1"
|
|
112
112
|
},
|
|
113
113
|
"peerDependencies": {
|
|
114
114
|
"svelte": "^5.18.2"
|