@ankhorage/surface 0.2.3 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/dist/examples/DocsExamples.d.ts.map +1 -1
- package/dist/examples/DocsExamples.js +0 -2
- package/dist/examples/DocsExamples.js.map +1 -1
- package/dist/internal/resolvers/resolveControlSize.d.ts +2 -2
- package/dist/internal/resolvers/resolveControlSize.d.ts.map +1 -1
- package/dist/internal/resolvers/resolveControlSize.js.map +1 -1
- package/dist/internal/resolvers/resolveIconSize.d.ts +2 -2
- package/dist/internal/resolvers/resolveIconSize.d.ts.map +1 -1
- package/dist/internal/resolvers/resolveIconSize.js.map +1 -1
- package/dist/internal/resolvers/resolveInteractiveColors.d.ts +3 -3
- package/dist/internal/resolvers/resolveInteractiveColors.d.ts.map +1 -1
- package/dist/internal/resolvers/resolveInteractiveColors.js.map +1 -1
- package/dist/internal/resolvers/resolveSelectionControlColors.d.ts +2 -2
- package/dist/internal/resolvers/resolveSelectionControlColors.d.ts.map +1 -1
- package/dist/internal/resolvers/resolveSelectionControlColors.js.map +1 -1
- package/dist/internal/resolvers/resolveTextColor.d.ts +3 -3
- package/dist/internal/resolvers/resolveTextColor.d.ts.map +1 -1
- package/dist/internal/resolvers/resolveTextColor.js.map +1 -1
- package/dist/internal/resolvers/resolveTextStyles.d.ts +3 -3
- package/dist/internal/resolvers/resolveTextStyles.d.ts.map +1 -1
- package/dist/internal/resolvers/resolveTextStyles.js.map +1 -1
- package/dist/internal/resolvers/resolveTone.d.ts +2 -2
- package/dist/internal/resolvers/resolveTone.d.ts.map +1 -1
- package/dist/internal/resolvers/resolveTone.js.map +1 -1
- package/dist/layout/Container.d.ts +2 -2
- package/dist/layout/Container.d.ts.map +1 -1
- package/dist/layout/Container.js.map +1 -1
- package/dist/layout/helpers.d.ts +9 -9
- package/dist/layout/helpers.d.ts.map +1 -1
- package/dist/layout/helpers.js.map +1 -1
- package/dist/primitives/heading/resolveHeadingStyle.d.ts +2 -2
- package/dist/primitives/heading/resolveHeadingStyle.d.ts.map +1 -1
- package/dist/primitives/heading/resolveHeadingStyle.js.map +1 -1
- package/dist/primitives/icon/Icon.d.ts +3 -3
- package/dist/primitives/icon/Icon.d.ts.map +1 -1
- package/dist/primitives/icon/Icon.js.map +1 -1
- package/dist/theme/ThemeContext.d.ts +3 -3
- package/dist/theme/ThemeContext.d.ts.map +1 -1
- package/dist/theme/ThemeContext.js.map +1 -1
- package/dist/theme/colorEngine.d.ts +10 -11
- package/dist/theme/colorEngine.d.ts.map +1 -1
- package/dist/theme/colorEngine.js +102 -412
- package/dist/theme/colorEngine.js.map +1 -1
- package/dist/theme/createTheme.d.ts +3 -3
- package/dist/theme/createTheme.d.ts.map +1 -1
- package/dist/theme/createTheme.js +2 -4
- package/dist/theme/createTheme.js.map +1 -1
- package/dist/theme/types.d.ts +5 -17
- package/dist/theme/types.d.ts.map +1 -1
- package/dist/theme/types.js.map +1 -1
- package/package.json +4 -4
- package/src/examples/DocsExamples.tsx +0 -2
- package/src/internal/resolvers/resolveControlSize.ts +5 -2
- package/src/internal/resolvers/resolveIconSize.ts +2 -2
- package/src/internal/resolvers/resolveInteractiveColors.ts +3 -3
- package/src/internal/resolvers/resolveSelectionControlColors.ts +2 -2
- package/src/internal/resolvers/resolveTextColor.ts +3 -3
- package/src/internal/resolvers/resolveTextStyles.ts +6 -6
- package/src/internal/resolvers/resolveTone.ts +2 -2
- package/src/layout/Container.tsx +2 -2
- package/src/layout/helpers.test.ts +2 -2
- package/src/layout/helpers.ts +12 -9
- package/src/primitives/heading/resolveHeadingStyle.ts +2 -2
- package/src/primitives/icon/Icon.tsx +3 -3
- package/src/theme/ThemeContext.tsx +2 -2
- package/src/theme/colorEngine.test.ts +158 -154
- package/src/theme/colorEngine.ts +128 -477
- package/src/theme/createTheme.ts +6 -8
- package/src/theme/types.ts +15 -18
- package/src/utils/deepMerge.test.ts +0 -4
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
import type { SemanticColorToken } from '@ankhorage/color-theory';
|
|
2
|
+
import {
|
|
3
|
+
DARK_SEMANTIC_COLOR_REFERENCES,
|
|
4
|
+
LIGHT_SEMANTIC_COLOR_REFERENCES,
|
|
5
|
+
} from '@ankhorage/color-theory';
|
|
6
|
+
import type { ThemeConfig } from '@ankhorage/contracts';
|
|
1
7
|
import { describe, expect, it } from 'bun:test';
|
|
2
|
-
import { oklch } from 'culori';
|
|
3
8
|
|
|
4
|
-
import { generatePalette } from './colorEngine';
|
|
5
|
-
import type {
|
|
9
|
+
import { generatePalette, resolveSemanticColors } from './colorEngine';
|
|
10
|
+
import type { ThemeSemantics } from './types';
|
|
6
11
|
|
|
7
12
|
const mockConfig: ThemeConfig = {
|
|
8
13
|
id: 'test',
|
|
@@ -10,53 +15,19 @@ const mockConfig: ThemeConfig = {
|
|
|
10
15
|
light: {
|
|
11
16
|
primaryColor: '#3B82F6',
|
|
12
17
|
harmony: 'triadic',
|
|
13
|
-
colorTone: 'neutral',
|
|
14
18
|
},
|
|
15
19
|
dark: {
|
|
16
20
|
primaryColor: '#3B82F6',
|
|
17
21
|
harmony: 'triadic',
|
|
18
|
-
colorTone: 'neutral',
|
|
19
22
|
},
|
|
20
23
|
};
|
|
21
24
|
|
|
22
|
-
function configForColorTone(colorTone: ColorTone): ThemeConfig {
|
|
23
|
-
return {
|
|
24
|
-
...mockConfig,
|
|
25
|
-
light: {
|
|
26
|
-
...mockConfig.light,
|
|
27
|
-
colorTone,
|
|
28
|
-
},
|
|
29
|
-
dark: {
|
|
30
|
-
...mockConfig.dark,
|
|
31
|
-
colorTone,
|
|
32
|
-
},
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function lightness(hex: string): number {
|
|
37
|
-
const color = oklch(hex);
|
|
38
|
-
if (!color) {
|
|
39
|
-
throw new Error(`Expected valid OKLCH color for ${hex}.`);
|
|
40
|
-
}
|
|
41
|
-
return color.l;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function chroma(hex: string): number {
|
|
45
|
-
const color = oklch(hex);
|
|
46
|
-
if (!color) {
|
|
47
|
-
throw new Error(`Expected valid OKLCH color for ${hex}.`);
|
|
48
|
-
}
|
|
49
|
-
return color.c;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
25
|
function expectRequiredSemanticRoles(semantics: ThemeSemantics) {
|
|
53
26
|
expect(semantics.neutral.bg).toBeDefined();
|
|
54
27
|
expect(semantics.neutral.surface).toBeDefined();
|
|
55
28
|
expect(semantics.neutral.text).toBeDefined();
|
|
56
29
|
expect(semantics.brand.base).toBeDefined();
|
|
57
30
|
expect(semantics.secondary.base).toBeDefined();
|
|
58
|
-
expect(semantics.accent.base).toBeDefined();
|
|
59
|
-
expect(semantics.highlight.base).toBeDefined();
|
|
60
31
|
expect(semantics.danger.base).toBeDefined();
|
|
61
32
|
expect(semantics.success.base).toBeDefined();
|
|
62
33
|
expect(semantics.warning.base).toBeDefined();
|
|
@@ -69,159 +40,192 @@ function expectRequiredSemanticRoles(semantics: ThemeSemantics) {
|
|
|
69
40
|
}
|
|
70
41
|
|
|
71
42
|
describe('colorEngine', () => {
|
|
72
|
-
it('
|
|
73
|
-
const { colors,
|
|
43
|
+
it('generates a valid palette for light mode', () => {
|
|
44
|
+
const { colors, swatches, semantics } = generatePalette(mockConfig, 'light');
|
|
74
45
|
|
|
75
|
-
// Primary should be defined
|
|
76
46
|
expect(colors.primary).toBeDefined();
|
|
47
|
+
expect(swatches.primary).toBeDefined();
|
|
48
|
+
expect(swatches.neutral).toBeDefined();
|
|
49
|
+
expect(Object.keys(swatches.primary)).toHaveLength(11);
|
|
50
|
+
expect(Object.keys(swatches.neutral)).toHaveLength(11);
|
|
77
51
|
|
|
78
|
-
//
|
|
79
|
-
const neutralBg = oklch(semantics.neutral.bg);
|
|
80
|
-
expect(neutralBg?.c).toBeLessThanOrEqual(0.021); // Small epsilon for float
|
|
81
|
-
|
|
82
|
-
// Verify presence of new tokens
|
|
83
|
-
expect(semantics.neutral.bgSubtle).toBeDefined();
|
|
84
|
-
expect(semantics.brand.onSolidText).toBeDefined();
|
|
85
|
-
expect(semantics.brand.softBg).toBeDefined();
|
|
52
|
+
// Surface semantic aliases
|
|
86
53
|
expect(semantics.surface.default).toBe(semantics.neutral.surface);
|
|
87
54
|
expect(semantics.content.muted).toBe(semantics.neutral.textMuted);
|
|
88
55
|
expect(semantics.border.focus).toBe(semantics.brand.outline);
|
|
89
56
|
expect(semantics.action.primary.base).toBe(semantics.brand.base);
|
|
90
57
|
expect(semantics.action.danger.base).toBe(semantics.danger.base);
|
|
91
58
|
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
expect(
|
|
97
|
-
|
|
98
|
-
expect(Object.keys(primaryScale)).toHaveLength(11);
|
|
99
|
-
expect(neutralScale[50]).toBeDefined();
|
|
100
|
-
expect(neutralScale[950]).toBeDefined();
|
|
59
|
+
// Surface runtime semantic aliases derived from ordinal swatches
|
|
60
|
+
expect(semantics.accent.base).toBeDefined();
|
|
61
|
+
expect(semantics.highlight.base).toBeDefined();
|
|
62
|
+
// accent/highlight are Surface semantic aliases (tertiary/quaternary fallback to primary when absent)
|
|
63
|
+
expect(semantics.accent.base).toBe(colors.accent);
|
|
64
|
+
expect(semantics.highlight.base).toBe(colors.highlight);
|
|
101
65
|
});
|
|
102
66
|
|
|
103
|
-
it('
|
|
104
|
-
const { colors } = generatePalette(mockConfig, '
|
|
105
|
-
|
|
106
|
-
const p = oklch(colors.primary);
|
|
107
|
-
const s = oklch(colors.secondary);
|
|
108
|
-
const a = oklch(colors.accent);
|
|
109
|
-
|
|
110
|
-
if (p && s && a) {
|
|
111
|
-
const h1 = p.h ?? 0;
|
|
112
|
-
const h2 = s.h ?? 0;
|
|
113
|
-
const h3 = a.h ?? 0;
|
|
114
|
-
|
|
115
|
-
// Check distance (allowing for small float rounding and perceptual shift)
|
|
116
|
-
const diff1 = Math.abs((h2 - h1 + 360) % 360);
|
|
117
|
-
const diff2 = Math.abs((h3 - h1 + 360) % 360);
|
|
67
|
+
it('generates a valid palette for dark mode', () => {
|
|
68
|
+
const { colors, semantics } = generatePalette(mockConfig, 'dark');
|
|
118
69
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
expect(diff2).toBeGreaterThan(235);
|
|
122
|
-
expect(diff2).toBeLessThan(245);
|
|
123
|
-
}
|
|
70
|
+
expect(colors.background).toBe(semantics.neutral.bg);
|
|
71
|
+
expect(semantics.content.inverse).toBe(semantics.brand.onSolidText);
|
|
124
72
|
});
|
|
125
73
|
|
|
126
|
-
it('
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
light: { ...mockConfig.light, harmony: 'monochromatic' as const },
|
|
130
|
-
};
|
|
131
|
-
const { colors } = generatePalette(config, 'light');
|
|
74
|
+
it('uses mode-aware role semantics for dark mode soft states', () => {
|
|
75
|
+
const light = generatePalette(mockConfig, 'light');
|
|
76
|
+
const dark = generatePalette(mockConfig, 'dark');
|
|
132
77
|
|
|
133
|
-
|
|
134
|
-
|
|
78
|
+
expect(light.semantics.brand.softBg).toBe(light.swatches.primary[100]);
|
|
79
|
+
expect(light.semantics.brand.softHover).toBe(light.swatches.primary[200]);
|
|
80
|
+
expect(light.semantics.brand.softActive).toBe(light.swatches.primary[300]);
|
|
81
|
+
expect(dark.semantics.brand.softBg).toBe(dark.swatches.primary[900]);
|
|
82
|
+
expect(dark.semantics.brand.softHover).toBe(dark.swatches.primary[800]);
|
|
83
|
+
expect(dark.semantics.brand.softActive).toBe(dark.swatches.primary[700]);
|
|
84
|
+
});
|
|
135
85
|
|
|
136
|
-
|
|
86
|
+
it('preserves the primary color at swatch step 500 in light mode', () => {
|
|
87
|
+
const { swatches } = generatePalette(mockConfig, 'light');
|
|
88
|
+
expect(swatches.primary[500]).toBe(mockConfig.light.primaryColor);
|
|
137
89
|
});
|
|
138
90
|
|
|
139
|
-
it('
|
|
140
|
-
const {
|
|
91
|
+
it('preserves the primary color at swatch step 500 in dark mode', () => {
|
|
92
|
+
const { swatches } = generatePalette(mockConfig, 'dark');
|
|
93
|
+
expect(swatches.primary[500]).toBe(mockConfig.dark.primaryColor);
|
|
94
|
+
});
|
|
141
95
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
expect(
|
|
96
|
+
it('provides a neutral swatch with neutralKeyColor at step 500', () => {
|
|
97
|
+
const { swatches } = generatePalette(mockConfig, 'light');
|
|
98
|
+
// neutral swatch must have a step 500 entry
|
|
99
|
+
expect(swatches.neutral[500]).toBeDefined();
|
|
146
100
|
});
|
|
147
101
|
|
|
148
|
-
it('
|
|
102
|
+
it('throws deterministically on invalid primary color', () => {
|
|
149
103
|
const config = {
|
|
150
104
|
...mockConfig,
|
|
151
|
-
light: { ...mockConfig.light, primaryColor: '
|
|
105
|
+
light: { ...mockConfig.light, primaryColor: 'not-a-hex-color' },
|
|
152
106
|
};
|
|
153
|
-
|
|
154
|
-
// Should not throw
|
|
155
|
-
const { colors } = generatePalette(config, 'light');
|
|
156
|
-
expect(colors.primary).toBeDefined();
|
|
157
|
-
// Default fallback blue #3B82F6 in OKLCH
|
|
158
|
-
const p = oklch(colors.primary);
|
|
159
|
-
const fallbackBlue = oklch('#3B82F6');
|
|
160
|
-
expect(fallbackBlue).toBeDefined();
|
|
161
|
-
expect(p?.h).toBeCloseTo(fallbackBlue?.h ?? 0, 0);
|
|
107
|
+
expect(() => generatePalette(config, 'light')).toThrow();
|
|
162
108
|
});
|
|
163
109
|
|
|
164
|
-
it('
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
expect(
|
|
170
|
-
expect(lightness(obsidian.semantics.neutral.bg)).toBeLessThan(0.2);
|
|
171
|
-
expect(lightness(obsidian.semantics.surface.default)).toBeLessThan(0.2);
|
|
110
|
+
it('throws deterministically on invalid primary color in dark mode', () => {
|
|
111
|
+
const config = {
|
|
112
|
+
...mockConfig,
|
|
113
|
+
dark: { ...mockConfig.dark, primaryColor: 'rgb(0,0,0)' },
|
|
114
|
+
};
|
|
115
|
+
expect(() => generatePalette(config, 'dark')).toThrow();
|
|
172
116
|
});
|
|
173
117
|
|
|
174
|
-
it('
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
118
|
+
it('emits required semantic roles for all harmonies', () => {
|
|
119
|
+
const harmonies = [
|
|
120
|
+
'monochromatic',
|
|
121
|
+
'analogous',
|
|
122
|
+
'complementary',
|
|
123
|
+
'triadic',
|
|
124
|
+
'tetradic',
|
|
125
|
+
'splitComplementary',
|
|
126
|
+
] as const;
|
|
127
|
+
|
|
128
|
+
for (const harmony of harmonies) {
|
|
129
|
+
const config = {
|
|
130
|
+
...mockConfig,
|
|
131
|
+
light: { ...mockConfig.light, harmony },
|
|
132
|
+
dark: { ...mockConfig.dark, harmony },
|
|
133
|
+
};
|
|
134
|
+
const light = generatePalette(config, 'light');
|
|
135
|
+
const dark = generatePalette(config, 'dark');
|
|
136
|
+
expectRequiredSemanticRoles(light.semantics);
|
|
137
|
+
expectRequiredSemanticRoles(dark.semantics);
|
|
138
|
+
}
|
|
184
139
|
});
|
|
185
140
|
|
|
186
|
-
it('
|
|
187
|
-
const {
|
|
188
|
-
|
|
189
|
-
expect(
|
|
190
|
-
expect(
|
|
191
|
-
expect(
|
|
141
|
+
it('generates ordinal chromatic role swatches (no accent/highlight as swatch keys)', () => {
|
|
142
|
+
const { swatches } = generatePalette(mockConfig, 'light');
|
|
143
|
+
const keys = Object.keys(swatches);
|
|
144
|
+
expect(keys).not.toContain('accent');
|
|
145
|
+
expect(keys).not.toContain('highlight');
|
|
146
|
+
expect(keys).not.toContain('surfaceTint');
|
|
147
|
+
expect(keys).not.toContain('base');
|
|
148
|
+
// ordinal roles are present
|
|
149
|
+
expect(keys).toContain('primary');
|
|
150
|
+
expect(keys).toContain('neutral');
|
|
192
151
|
});
|
|
193
152
|
|
|
194
|
-
it('
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
);
|
|
153
|
+
it('semantic resolver maps all SemanticColorToken entries for light mode', () => {
|
|
154
|
+
const { swatches } = generatePalette(mockConfig, 'light');
|
|
155
|
+
// Build a minimal GeneratedThemeModeColors for resolver testing
|
|
156
|
+
const generated = {
|
|
157
|
+
harmonyRoleColors: {} as never,
|
|
158
|
+
swatches,
|
|
159
|
+
neutral: { neutralKeyColor: swatches.neutral[500], diagnostics: {} as never },
|
|
160
|
+
};
|
|
161
|
+
const resolved = resolveSemanticColors(generated, LIGHT_SEMANTIC_COLOR_REFERENCES);
|
|
162
|
+
const tokens: SemanticColorToken[] = [
|
|
163
|
+
'background',
|
|
164
|
+
'surface',
|
|
165
|
+
'surfaceRaised',
|
|
166
|
+
'border',
|
|
167
|
+
'divider',
|
|
168
|
+
'text',
|
|
169
|
+
'textMuted',
|
|
170
|
+
'disabledBg',
|
|
171
|
+
'disabledText',
|
|
172
|
+
'brand',
|
|
173
|
+
'brandEmphasis',
|
|
174
|
+
'action',
|
|
175
|
+
'actionEmphasis',
|
|
176
|
+
];
|
|
177
|
+
for (const token of tokens) {
|
|
178
|
+
expect(resolved[token]).toBeDefined();
|
|
179
|
+
expect(typeof resolved[token]).toBe('string');
|
|
180
|
+
}
|
|
203
181
|
});
|
|
204
182
|
|
|
205
|
-
it('
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
'
|
|
215
|
-
'
|
|
216
|
-
'
|
|
183
|
+
it('semantic resolver maps all SemanticColorToken entries for dark mode', () => {
|
|
184
|
+
const { swatches } = generatePalette(mockConfig, 'dark');
|
|
185
|
+
const generated = {
|
|
186
|
+
harmonyRoleColors: {} as never,
|
|
187
|
+
swatches,
|
|
188
|
+
neutral: { neutralKeyColor: swatches.neutral[500], diagnostics: {} as never },
|
|
189
|
+
};
|
|
190
|
+
const resolved = resolveSemanticColors(generated, DARK_SEMANTIC_COLOR_REFERENCES);
|
|
191
|
+
const tokens: SemanticColorToken[] = [
|
|
192
|
+
'background',
|
|
193
|
+
'surface',
|
|
194
|
+
'surfaceRaised',
|
|
195
|
+
'border',
|
|
196
|
+
'divider',
|
|
197
|
+
'text',
|
|
198
|
+
'textMuted',
|
|
199
|
+
'disabledBg',
|
|
200
|
+
'disabledText',
|
|
201
|
+
'brand',
|
|
202
|
+
'brandEmphasis',
|
|
203
|
+
'action',
|
|
204
|
+
'actionEmphasis',
|
|
217
205
|
];
|
|
206
|
+
for (const token of tokens) {
|
|
207
|
+
expect(resolved[token]).toBeDefined();
|
|
208
|
+
}
|
|
209
|
+
});
|
|
218
210
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
211
|
+
it('neutral semantics power backgrounds, borders, text in light mode', () => {
|
|
212
|
+
const { semantics } = generatePalette(mockConfig, 'light');
|
|
213
|
+
expect(semantics.neutral.bg).toBeDefined();
|
|
214
|
+
expect(semantics.neutral.bgSubtle).toBeDefined();
|
|
215
|
+
expect(semantics.neutral.surface).toBeDefined();
|
|
216
|
+
expect(semantics.neutral.surfaceHover).toBeDefined();
|
|
217
|
+
expect(semantics.neutral.border).toBeDefined();
|
|
218
|
+
expect(semantics.neutral.borderStrong).toBeDefined();
|
|
219
|
+
expect(semantics.neutral.divider).toBeDefined();
|
|
220
|
+
expect(semantics.neutral.text).toBeDefined();
|
|
221
|
+
expect(semantics.neutral.textMuted).toBeDefined();
|
|
222
|
+
expect(semantics.neutral.textSubtle).toBeDefined();
|
|
223
|
+
});
|
|
222
224
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
225
|
+
it('readable foregrounds are generated for brand base in both modes', () => {
|
|
226
|
+
const light = generatePalette(mockConfig, 'light');
|
|
227
|
+
const dark = generatePalette(mockConfig, 'dark');
|
|
228
|
+
expect(light.semantics.brand.onSolidText).toMatch(/^#/);
|
|
229
|
+
expect(dark.semantics.brand.onSolidText).toMatch(/^#/);
|
|
226
230
|
});
|
|
227
231
|
});
|