@ankhorage/zora 0.16.2 → 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 +62 -0
- package/README.md +11 -13
- package/dist/components/heading/resolveHeadingRecipe.d.ts +2 -2
- package/dist/components/heading/resolveHeadingRecipe.d.ts.map +1 -1
- package/dist/components/heading/resolveHeadingRecipe.js.map +1 -1
- package/dist/components/text/resolveTextRecipe.d.ts +2 -2
- package/dist/components/text/resolveTextRecipe.d.ts.map +1 -1
- package/dist/components/text/resolveTextRecipe.js.map +1 -1
- package/dist/patterns/theme-composer/ThemeComposer.d.ts.map +1 -1
- package/dist/patterns/theme-composer/ThemeComposer.js +10 -86
- package/dist/patterns/theme-composer/ThemeComposer.js.map +1 -1
- package/dist/patterns/theme-composer/index.d.ts +1 -1
- package/dist/patterns/theme-composer/index.d.ts.map +1 -1
- package/dist/patterns/theme-composer/index.js.map +1 -1
- package/dist/patterns/theme-composer/types.d.ts +1 -13
- package/dist/patterns/theme-composer/types.d.ts.map +1 -1
- package/dist/patterns/theme-composer/types.js.map +1 -1
- package/dist/theme/createZoraThemeConfig.d.ts +1 -1
- package/dist/theme/createZoraThemeConfig.d.ts.map +1 -1
- package/dist/theme/createZoraThemeConfig.js +5 -6
- package/dist/theme/createZoraThemeConfig.js.map +1 -1
- package/dist/theme/index.d.ts +1 -1
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/types.d.ts +16 -11
- package/dist/theme/types.d.ts.map +1 -1
- package/dist/theme/types.js +1 -20
- package/dist/theme/types.js.map +1 -1
- package/dist/theme/useZoraTheme.d.ts +1 -1
- package/dist/theme/zoraDefaultTheme.js +1 -1
- package/dist/theme/zoraDefaultTheme.js.map +1 -1
- package/package.json +4 -4
- package/src/components/heading/resolveHeadingRecipe.test.ts +30 -5
- package/src/components/heading/resolveHeadingRecipe.ts +6 -6
- package/src/components/text/resolveTextRecipe.test.ts +30 -5
- package/src/components/text/resolveTextRecipe.ts +6 -6
- package/src/patterns/theme-composer/ThemeComposer.test.ts +9 -141
- package/src/patterns/theme-composer/ThemeComposer.tsx +10 -131
- package/src/patterns/theme-composer/index.ts +1 -6
- package/src/patterns/theme-composer/types.ts +1 -15
- package/src/showcaseCoverage.test.ts +14 -0
- package/src/theme/createZoraThemeConfig.test.ts +51 -26
- package/src/theme/createZoraThemeConfig.ts +7 -7
- package/src/theme/index.ts +1 -3
- package/src/theme/types.ts +22 -34
- package/src/theme/zoraDefaultTheme.ts +1 -1
- package/dist/internal/color/colorToneRecipes.d.ts +0 -23
- package/dist/internal/color/colorToneRecipes.d.ts.map +0 -1
- package/dist/internal/color/colorToneRecipes.js +0 -139
- package/dist/internal/color/colorToneRecipes.js.map +0 -1
- package/dist/internal/color/harmony.d.ts +0 -12
- package/dist/internal/color/harmony.d.ts.map +0 -1
- package/dist/internal/color/harmony.js +0 -69
- package/dist/internal/color/harmony.js.map +0 -1
- package/dist/internal/color/hue.d.ts +0 -3
- package/dist/internal/color/hue.d.ts.map +0 -1
- package/dist/internal/color/hue.js +0 -7
- package/dist/internal/color/hue.js.map +0 -1
- package/dist/internal/color/index.d.ts +0 -10
- package/dist/internal/color/index.d.ts.map +0 -1
- package/dist/internal/color/index.js +0 -10
- package/dist/internal/color/index.js.map +0 -1
- package/dist/internal/color/oklch.d.ts +0 -6
- package/dist/internal/color/oklch.d.ts.map +0 -1
- package/dist/internal/color/oklch.js +0 -50
- package/dist/internal/color/oklch.js.map +0 -1
- package/dist/internal/color/primary.d.ts +0 -3
- package/dist/internal/color/primary.d.ts.map +0 -1
- package/dist/internal/color/primary.js +0 -44
- package/dist/internal/color/primary.js.map +0 -1
- package/dist/internal/color/roleHues.d.ts +0 -15
- package/dist/internal/color/roleHues.d.ts.map +0 -1
- package/dist/internal/color/roleHues.js +0 -103
- package/dist/internal/color/roleHues.js.map +0 -1
- package/dist/internal/color/roleScales.d.ts +0 -20
- package/dist/internal/color/roleScales.d.ts.map +0 -1
- package/dist/internal/color/roleScales.js +0 -79
- package/dist/internal/color/roleScales.js.map +0 -1
- package/dist/internal/color/scales.d.ts +0 -19
- package/dist/internal/color/scales.d.ts.map +0 -1
- package/dist/internal/color/scales.js +0 -135
- package/dist/internal/color/scales.js.map +0 -1
- package/dist/internal/color/semanticTokens.d.ts +0 -28
- package/dist/internal/color/semanticTokens.d.ts.map +0 -1
- package/dist/internal/color/semanticTokens.js +0 -84
- package/dist/internal/color/semanticTokens.js.map +0 -1
- package/dist/internal/color/types.d.ts +0 -10
- package/dist/internal/color/types.d.ts.map +0 -1
- package/dist/internal/color/types.js +0 -4
- package/dist/internal/color/types.js.map +0 -1
- package/dist/patterns/theme-composer/recommendations.d.ts +0 -14
- package/dist/patterns/theme-composer/recommendations.d.ts.map +0 -1
- package/dist/patterns/theme-composer/recommendations.js +0 -58
- package/dist/patterns/theme-composer/recommendations.js.map +0 -1
- package/src/internal/color/colorToneRecipes.test.ts +0 -89
- package/src/internal/color/colorToneRecipes.ts +0 -167
- package/src/internal/color/harmony.test.ts +0 -145
- package/src/internal/color/harmony.ts +0 -96
- package/src/internal/color/hue.test.ts +0 -28
- package/src/internal/color/hue.ts +0 -7
- package/src/internal/color/index.ts +0 -44
- package/src/internal/color/oklch.ts +0 -65
- package/src/internal/color/primary.test.ts +0 -105
- package/src/internal/color/primary.ts +0 -64
- package/src/internal/color/roleHues.test.ts +0 -197
- package/src/internal/color/roleHues.ts +0 -142
- package/src/internal/color/roleScales.test.ts +0 -220
- package/src/internal/color/roleScales.ts +0 -127
- package/src/internal/color/scales.test.ts +0 -151
- package/src/internal/color/scales.ts +0 -194
- package/src/internal/color/semanticTokens.test.ts +0 -170
- package/src/internal/color/semanticTokens.ts +0 -114
- package/src/internal/color/types.ts +0 -15
- package/src/patterns/theme-composer/recommendations.ts +0 -85
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { COLOR_HARMONIES, parseHexColorOrThrow } from '@ankhorage/color-theory';
|
|
1
2
|
import { Box, Stack } from '@ankhorage/surface';
|
|
2
3
|
import React from 'react';
|
|
3
4
|
|
|
@@ -9,63 +10,27 @@ import { Input } from '../../components/input';
|
|
|
9
10
|
import { Select } from '../../components/select';
|
|
10
11
|
import { Tabs } from '../../components/tabs';
|
|
11
12
|
import { Text } from '../../components/text';
|
|
12
|
-
import {
|
|
13
|
-
ZORA_COLOR_HARMONIES,
|
|
14
|
-
ZORA_COLOR_TONES,
|
|
15
|
-
type ZoraColorTone,
|
|
16
|
-
type ZoraTheme,
|
|
17
|
-
type ZoraThemeMode,
|
|
18
|
-
} from '../../theme/types';
|
|
13
|
+
import type { ZoraThemeMode } from '../../theme/types';
|
|
19
14
|
import { useZoraTheme } from '../../theme/useZoraTheme';
|
|
20
15
|
import { withZoraThemeScope } from '../../theme/withZoraThemeScope';
|
|
21
|
-
import {
|
|
22
|
-
createThemeFromThemeComposerRecommendation,
|
|
23
|
-
findThemeComposerRecommendation,
|
|
24
|
-
formatThemeComposerLabel,
|
|
25
|
-
} from './recommendations';
|
|
26
16
|
import type { ThemeComposerProps } from './types';
|
|
27
17
|
|
|
28
|
-
const HEX_RE = /^#[0-9A-Fa-f]{6}$/;
|
|
29
|
-
|
|
30
18
|
function isValidHex(value: string): boolean {
|
|
31
|
-
|
|
19
|
+
try {
|
|
20
|
+
parseHexColorOrThrow(value);
|
|
21
|
+
return true;
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
32
25
|
}
|
|
33
26
|
|
|
34
|
-
const HARMONY_OPTIONS =
|
|
35
|
-
|
|
36
|
-
const TONE_OPTIONS = ZORA_COLOR_TONES.map((t) => ({ value: t, label: t }));
|
|
27
|
+
const HARMONY_OPTIONS = COLOR_HARMONIES.map((h) => ({ value: h, label: h }));
|
|
37
28
|
|
|
38
29
|
const MODE_TABS = [
|
|
39
30
|
{ value: 'light' as ZoraThemeMode, label: 'Light' },
|
|
40
31
|
{ value: 'dark' as ZoraThemeMode, label: 'Dark' },
|
|
41
32
|
];
|
|
42
33
|
|
|
43
|
-
const COLOR_TONE_BACKGROUND_TONE: Record<ZoraColorTone, string> = {
|
|
44
|
-
neutral: 'Neutral',
|
|
45
|
-
pastel: 'Pastel',
|
|
46
|
-
earth: 'Earth',
|
|
47
|
-
mineral: 'Mineral',
|
|
48
|
-
muted: 'Muted',
|
|
49
|
-
jewel: 'Neutral',
|
|
50
|
-
fluorescent: 'Obsidian',
|
|
51
|
-
obsidian: 'Obsidian',
|
|
52
|
-
vaporwave: 'Pastel',
|
|
53
|
-
monochromeAccent: 'Neutral',
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const COLOR_TONE_FOREGROUND_TONE: Record<ZoraColorTone, string> = {
|
|
57
|
-
neutral: 'Jewel',
|
|
58
|
-
pastel: 'Jewel',
|
|
59
|
-
earth: 'Mineral',
|
|
60
|
-
mineral: 'Jewel',
|
|
61
|
-
muted: 'Jewel',
|
|
62
|
-
jewel: 'Jewel',
|
|
63
|
-
fluorescent: 'Fluorescent',
|
|
64
|
-
obsidian: 'Fluorescent',
|
|
65
|
-
vaporwave: 'Fluorescent',
|
|
66
|
-
monochromeAccent: 'Jewel',
|
|
67
|
-
};
|
|
68
|
-
|
|
69
34
|
function ThemeComposerInner({
|
|
70
35
|
themeId: _themeId,
|
|
71
36
|
value,
|
|
@@ -73,9 +38,6 @@ function ThemeComposerInner({
|
|
|
73
38
|
mode,
|
|
74
39
|
onModeChange,
|
|
75
40
|
onSubmit,
|
|
76
|
-
appCategory,
|
|
77
|
-
appMood,
|
|
78
|
-
recommendations,
|
|
79
41
|
testID,
|
|
80
42
|
}: ThemeComposerProps) {
|
|
81
43
|
const { theme } = useZoraTheme();
|
|
@@ -96,70 +58,16 @@ function ThemeComposerInner({
|
|
|
96
58
|
|
|
97
59
|
if (isValidHex(normalized)) {
|
|
98
60
|
setHexError(undefined);
|
|
99
|
-
onChange({ ...value, primaryColor: normalized
|
|
61
|
+
onChange({ ...value, primaryColor: normalized });
|
|
100
62
|
} else {
|
|
101
63
|
setHexError('Enter a valid 6-digit hex color (e.g. #0f766e).');
|
|
102
64
|
}
|
|
103
65
|
}
|
|
104
66
|
|
|
105
67
|
const activeMode = mode ?? 'light';
|
|
106
|
-
const recommendation = findThemeComposerRecommendation({
|
|
107
|
-
appCategory,
|
|
108
|
-
appMood,
|
|
109
|
-
recommendations,
|
|
110
|
-
});
|
|
111
68
|
|
|
112
69
|
return (
|
|
113
70
|
<Stack gap="l" testID={testID}>
|
|
114
|
-
{recommendation ? (
|
|
115
|
-
<Card
|
|
116
|
-
title="Recommended starting point"
|
|
117
|
-
description="Use this as an optional starting point. It is only applied when you choose it."
|
|
118
|
-
actions={
|
|
119
|
-
<Button
|
|
120
|
-
size="s"
|
|
121
|
-
emphasis="soft"
|
|
122
|
-
tone="primary"
|
|
123
|
-
onPress={() =>
|
|
124
|
-
onChange(
|
|
125
|
-
createThemeFromThemeComposerRecommendation({
|
|
126
|
-
value,
|
|
127
|
-
recommendation,
|
|
128
|
-
}),
|
|
129
|
-
)
|
|
130
|
-
}
|
|
131
|
-
testID={testID ? `${testID}-apply-recommendation` : undefined}
|
|
132
|
-
>
|
|
133
|
-
Apply recommendation
|
|
134
|
-
</Button>
|
|
135
|
-
}
|
|
136
|
-
>
|
|
137
|
-
<Stack gap="s">
|
|
138
|
-
<Stack direction="row" gap="s" wrap="wrap">
|
|
139
|
-
<Badge tone="primary" emphasis="soft">
|
|
140
|
-
{formatThemeComposerLabel(recommendation.appMood)} mood
|
|
141
|
-
</Badge>
|
|
142
|
-
<Badge tone="neutral" emphasis="soft">
|
|
143
|
-
{formatThemeComposerLabel(recommendation.suggestedColorTone)} color tone
|
|
144
|
-
</Badge>
|
|
145
|
-
<Badge tone="neutral" emphasis="soft">
|
|
146
|
-
{formatThemeComposerLabel(recommendation.suggestedHarmony)} harmony
|
|
147
|
-
</Badge>
|
|
148
|
-
{recommendation.suggestedPrimaryHueDegrees === undefined ? null : (
|
|
149
|
-
<Badge tone="neutral" emphasis="soft">
|
|
150
|
-
{recommendation.suggestedPrimaryHueDegrees}° hue
|
|
151
|
-
</Badge>
|
|
152
|
-
)}
|
|
153
|
-
</Stack>
|
|
154
|
-
<Text tone="muted" variant="bodySmall">
|
|
155
|
-
Suggested for {formatThemeComposerLabel(recommendation.appCategory)}. The color tone
|
|
156
|
-
controls palette character, harmony controls accent relationships, and hue sets the
|
|
157
|
-
starting primary color when available.
|
|
158
|
-
</Text>
|
|
159
|
-
</Stack>
|
|
160
|
-
</Card>
|
|
161
|
-
) : null}
|
|
162
|
-
|
|
163
71
|
{/* Section: Primary Color */}
|
|
164
72
|
<Card title="Primary color" description="Set the seed color for your theme palette.">
|
|
165
73
|
<Stack gap="m">
|
|
@@ -209,35 +117,6 @@ function ThemeComposerInner({
|
|
|
209
117
|
/>
|
|
210
118
|
</Card>
|
|
211
119
|
|
|
212
|
-
{/* Section: Color tone */}
|
|
213
|
-
<Card
|
|
214
|
-
title="Color tone"
|
|
215
|
-
description="Controls the vibrancy and saturation style of the palette."
|
|
216
|
-
>
|
|
217
|
-
<Stack gap="s">
|
|
218
|
-
<Select
|
|
219
|
-
value={value.colorTone}
|
|
220
|
-
options={TONE_OPTIONS}
|
|
221
|
-
onValueChange={(t) => onChange({ ...value, colorTone: t })}
|
|
222
|
-
testID={testID ? `${testID}-tone-select` : undefined}
|
|
223
|
-
/>
|
|
224
|
-
<Stack direction="row" gap="s" align="center">
|
|
225
|
-
<Text tone="muted" variant="caption">
|
|
226
|
-
Background:
|
|
227
|
-
</Text>
|
|
228
|
-
<Badge tone="neutral" emphasis="soft">
|
|
229
|
-
{COLOR_TONE_BACKGROUND_TONE[value.colorTone]}
|
|
230
|
-
</Badge>
|
|
231
|
-
<Text tone="muted" variant="caption">
|
|
232
|
-
Foreground:
|
|
233
|
-
</Text>
|
|
234
|
-
<Badge tone="neutral" emphasis="soft">
|
|
235
|
-
{COLOR_TONE_FOREGROUND_TONE[value.colorTone]}
|
|
236
|
-
</Badge>
|
|
237
|
-
</Stack>
|
|
238
|
-
</Stack>
|
|
239
|
-
</Card>
|
|
240
|
-
|
|
241
120
|
{/* Section: Mode */}
|
|
242
121
|
<Card title="Mode" description="Switch between light and dark presentation.">
|
|
243
122
|
<Tabs
|
|
@@ -1,24 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ZoraTheme, ZoraThemeMode } from '../../theme/types';
|
|
2
2
|
import type { ZoraBaseProps } from '../../theme/ZoraBaseProps';
|
|
3
3
|
|
|
4
|
-
export type ThemeComposerAppCategory = string;
|
|
5
|
-
export type ThemeComposerAppMood = string;
|
|
6
|
-
|
|
7
|
-
export interface ThemeComposerRecommendation {
|
|
8
|
-
appCategory: ThemeComposerAppCategory;
|
|
9
|
-
appMood: ThemeComposerAppMood;
|
|
10
|
-
suggestedColorTone: ZoraColorTone;
|
|
11
|
-
suggestedHarmony: ZoraColorHarmony;
|
|
12
|
-
suggestedPrimaryHueDegrees?: number;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
4
|
export interface ThemeComposerProps extends ZoraBaseProps {
|
|
16
5
|
value: ZoraTheme;
|
|
17
6
|
onChange: (theme: ZoraTheme) => void;
|
|
18
7
|
mode?: ZoraThemeMode;
|
|
19
8
|
onModeChange?: (mode: ZoraThemeMode) => void;
|
|
20
9
|
onSubmit?: (theme: ZoraTheme) => void;
|
|
21
|
-
appCategory?: ThemeComposerAppCategory;
|
|
22
|
-
appMood?: ThemeComposerAppMood;
|
|
23
|
-
recommendations?: readonly ThemeComposerRecommendation[];
|
|
24
10
|
}
|
|
@@ -5,6 +5,16 @@ import { describe, expect, test } from 'bun:test';
|
|
|
5
5
|
|
|
6
6
|
const SHOWCASE_ROOT = join(process.cwd(), 'examples', 'expo-showcase');
|
|
7
7
|
|
|
8
|
+
const IGNORED_DIRECTORY_NAMES = new Set([
|
|
9
|
+
'.expo',
|
|
10
|
+
'.git',
|
|
11
|
+
'.turbo',
|
|
12
|
+
'.vercel',
|
|
13
|
+
'coverage',
|
|
14
|
+
'dist',
|
|
15
|
+
'node_modules',
|
|
16
|
+
]);
|
|
17
|
+
|
|
8
18
|
const REQUIRED_SHOWCASE_COVERAGE = {
|
|
9
19
|
components: [
|
|
10
20
|
'Badge',
|
|
@@ -79,6 +89,10 @@ const REQUIRED_SHOWCASE_COVERAGE = {
|
|
|
79
89
|
|
|
80
90
|
function listFiles(root: string): string[] {
|
|
81
91
|
return readdirSync(root, { withFileTypes: true }).flatMap((entry) => {
|
|
92
|
+
if (entry.isDirectory() && IGNORED_DIRECTORY_NAMES.has(entry.name)) {
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
|
|
82
96
|
const absolutePath = join(root, entry.name);
|
|
83
97
|
if (entry.isDirectory()) {
|
|
84
98
|
return listFiles(absolutePath);
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { describe, expect, test } from 'bun:test';
|
|
2
2
|
|
|
3
|
-
import { parseHexToOklch } from '../internal/color';
|
|
4
3
|
import { createZoraThemeConfig } from './createZoraThemeConfig';
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
function isSixDigitHexColor(value: string): value is ZoraHexColor {
|
|
8
|
-
return /^#[0-9a-f]{6}$/.test(value);
|
|
9
|
-
}
|
|
4
|
+
import { zoraDefaultTheme } from './zoraDefaultTheme';
|
|
10
5
|
|
|
11
6
|
describe('createZoraThemeConfig', () => {
|
|
12
7
|
test('converts the default theme seed into a surface config', () => {
|
|
@@ -14,37 +9,67 @@ describe('createZoraThemeConfig', () => {
|
|
|
14
9
|
|
|
15
10
|
expect(themeConfig.id).toBe('zora');
|
|
16
11
|
expect(themeConfig.name).toBe('ZORA');
|
|
17
|
-
expect(
|
|
12
|
+
expect(themeConfig.light.primaryColor).toBe(zoraDefaultTheme.primaryColor);
|
|
18
13
|
expect(themeConfig.light.harmony).toBe('analogous');
|
|
19
|
-
expect(themeConfig.
|
|
20
|
-
expect(isSixDigitHexColor(themeConfig.dark.primaryColor)).toBe(true);
|
|
14
|
+
expect(themeConfig.dark.primaryColor).toBe(zoraDefaultTheme.primaryColor);
|
|
21
15
|
expect(themeConfig.dark.harmony).toBe('analogous');
|
|
22
|
-
expect(themeConfig.dark.colorTone).toBe('jewel');
|
|
23
|
-
|
|
24
|
-
expect(themeConfig.light.primaryColor).not.toBe(themeConfig.dark.primaryColor);
|
|
25
|
-
|
|
26
|
-
const lightOklch = parseHexToOklch(themeConfig.light.primaryColor);
|
|
27
|
-
const darkOklch = parseHexToOklch(themeConfig.dark.primaryColor);
|
|
28
|
-
expect(darkOklch.l).toBeGreaterThan(lightOklch.l);
|
|
29
16
|
});
|
|
30
17
|
|
|
31
|
-
test('
|
|
18
|
+
test('uses theme.name directly without fallback to id', () => {
|
|
32
19
|
const themeConfig = createZoraThemeConfig({
|
|
33
20
|
id: 'studio',
|
|
21
|
+
name: 'Studio',
|
|
22
|
+
appCategory: 'developer_tools',
|
|
34
23
|
primaryColor: '#0f766e',
|
|
35
24
|
harmony: 'analogous',
|
|
36
|
-
colorTone: 'jewel',
|
|
37
25
|
});
|
|
38
26
|
|
|
39
27
|
expect(themeConfig.id).toBe('studio');
|
|
40
|
-
expect(themeConfig.name).toBe('
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
28
|
+
expect(themeConfig.name).toBe('Studio');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('preserves the same primaryColor for both light and dark', () => {
|
|
32
|
+
const primary = '#2563eb';
|
|
33
|
+
const themeConfig = createZoraThemeConfig({
|
|
34
|
+
id: 'test',
|
|
35
|
+
name: 'Test',
|
|
36
|
+
appCategory: 'utilities_tools',
|
|
37
|
+
primaryColor: primary,
|
|
38
|
+
harmony: 'triadic',
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
expect(themeConfig.light.primaryColor).toBe(primary);
|
|
42
|
+
expect(themeConfig.dark.primaryColor).toBe(primary);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('validates public primaryColor strings during config conversion', () => {
|
|
46
|
+
expect(() =>
|
|
47
|
+
createZoraThemeConfig({
|
|
48
|
+
id: 'bad-theme',
|
|
49
|
+
name: 'Bad Theme',
|
|
50
|
+
appCategory: 'utilities_tools',
|
|
51
|
+
primaryColor: 'blue',
|
|
52
|
+
harmony: 'triadic',
|
|
53
|
+
}),
|
|
54
|
+
).toThrow();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('output has no colorTone field', () => {
|
|
58
|
+
const themeConfig = createZoraThemeConfig();
|
|
59
|
+
|
|
60
|
+
expect('colorTone' in themeConfig.light).toBe(false);
|
|
61
|
+
expect('colorTone' in themeConfig.dark).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('surfaceConfig.name equals theme.name', () => {
|
|
65
|
+
const themeConfig = createZoraThemeConfig({
|
|
66
|
+
id: 'my-theme',
|
|
67
|
+
name: 'My Theme',
|
|
68
|
+
appCategory: 'games',
|
|
69
|
+
primaryColor: '#7c3aed',
|
|
70
|
+
harmony: 'complementary',
|
|
71
|
+
});
|
|
47
72
|
|
|
48
|
-
expect(themeConfig.
|
|
73
|
+
expect(themeConfig.name).toBe('My Theme');
|
|
49
74
|
});
|
|
50
75
|
});
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { parseHexColorOrThrow } from '@ankhorage/color-theory';
|
|
2
|
+
import type { ThemeConfig } from '@ankhorage/contracts';
|
|
2
3
|
|
|
3
|
-
import { resolveModePrimaryColor } from '../internal/color';
|
|
4
4
|
import type { ZoraTheme } from './types';
|
|
5
5
|
import { zoraDefaultTheme } from './zoraDefaultTheme';
|
|
6
6
|
|
|
7
7
|
export function createZoraThemeConfig(theme: ZoraTheme = zoraDefaultTheme): ThemeConfig {
|
|
8
|
+
const primaryColor = parseHexColorOrThrow(theme.primaryColor);
|
|
9
|
+
|
|
8
10
|
return {
|
|
9
11
|
id: theme.id,
|
|
10
|
-
name: theme.name
|
|
12
|
+
name: theme.name,
|
|
11
13
|
light: {
|
|
12
|
-
primaryColor
|
|
14
|
+
primaryColor,
|
|
13
15
|
harmony: theme.harmony,
|
|
14
|
-
colorTone: theme.colorTone,
|
|
15
16
|
},
|
|
16
17
|
dark: {
|
|
17
|
-
primaryColor
|
|
18
|
+
primaryColor,
|
|
18
19
|
harmony: theme.harmony,
|
|
19
|
-
colorTone: theme.colorTone,
|
|
20
20
|
},
|
|
21
21
|
};
|
|
22
22
|
}
|
package/src/theme/index.ts
CHANGED
package/src/theme/types.ts
CHANGED
|
@@ -1,49 +1,37 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
ColorHarmony,
|
|
3
|
+
GeneratedThemeModeColors,
|
|
4
|
+
GeneratedThemeSwatches,
|
|
5
|
+
SemanticColorToken,
|
|
6
|
+
} from '@ankhorage/color-theory';
|
|
7
|
+
import type { AppCategory, ThemeConfig } from '@ankhorage/contracts';
|
|
8
|
+
import type { SurfaceTheme } from '@ankhorage/surface';
|
|
2
9
|
|
|
3
10
|
export type ZoraThemeId = string;
|
|
4
11
|
|
|
5
12
|
export type ZoraThemeMode = 'light' | 'dark';
|
|
6
13
|
|
|
7
|
-
export type ZoraHexColor = `#${string}`;
|
|
8
|
-
|
|
9
|
-
export const ZORA_COLOR_HARMONIES = [
|
|
10
|
-
'monochromatic',
|
|
11
|
-
'analogous',
|
|
12
|
-
'complementary',
|
|
13
|
-
'splitComplementary',
|
|
14
|
-
'triadic',
|
|
15
|
-
'tetradic',
|
|
16
|
-
] as const satisfies readonly ColorHarmony[];
|
|
17
|
-
|
|
18
|
-
export type ZoraColorHarmony = ColorHarmony;
|
|
19
|
-
|
|
20
|
-
export const ZORA_COLOR_TONES = [
|
|
21
|
-
'neutral',
|
|
22
|
-
'pastel',
|
|
23
|
-
'earth',
|
|
24
|
-
'mineral',
|
|
25
|
-
'muted',
|
|
26
|
-
'jewel',
|
|
27
|
-
'fluorescent',
|
|
28
|
-
'obsidian',
|
|
29
|
-
'vaporwave',
|
|
30
|
-
'monochromeAccent',
|
|
31
|
-
] as const satisfies readonly ColorTone[];
|
|
32
|
-
|
|
33
|
-
export type ZoraColorTone = ColorTone;
|
|
34
|
-
|
|
35
14
|
export interface ZoraTheme {
|
|
36
15
|
id: ZoraThemeId;
|
|
37
|
-
name
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
16
|
+
name: string;
|
|
17
|
+
appCategory: AppCategory;
|
|
18
|
+
primaryColor: string;
|
|
19
|
+
harmony: ColorHarmony;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ZoraComputedThemeMode {
|
|
23
|
+
mode: ZoraThemeMode;
|
|
24
|
+
surfaceTheme: SurfaceTheme;
|
|
25
|
+
generated: GeneratedThemeModeColors;
|
|
26
|
+
swatches: GeneratedThemeSwatches;
|
|
27
|
+
semanticColors?: Record<SemanticColorToken, string>;
|
|
41
28
|
}
|
|
42
29
|
|
|
43
30
|
export interface ZoraComputedTheme {
|
|
44
31
|
id: ZoraThemeId;
|
|
45
32
|
name: string;
|
|
46
|
-
mode: ZoraThemeMode;
|
|
47
33
|
source: ZoraTheme;
|
|
48
34
|
surfaceConfig: ThemeConfig;
|
|
35
|
+
light: ZoraComputedThemeMode;
|
|
36
|
+
dark: ZoraComputedThemeMode;
|
|
49
37
|
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { ZoraColorTone } from '../../theme/types';
|
|
2
|
-
import type { ZoraHueScaleRoleId } from './scales';
|
|
3
|
-
export interface ZoraColorToneLaneRecipe {
|
|
4
|
-
backgroundTone: ZoraColorTone;
|
|
5
|
-
foregroundTone: ZoraColorTone;
|
|
6
|
-
}
|
|
7
|
-
export interface ZoraColorToneRoleChromaFactors {
|
|
8
|
-
primary: number;
|
|
9
|
-
secondary: number;
|
|
10
|
-
accent: number;
|
|
11
|
-
highlight: number;
|
|
12
|
-
surfaceTint: number;
|
|
13
|
-
}
|
|
14
|
-
export interface ZoraColorToneRecipe {
|
|
15
|
-
colorTone: ZoraColorTone;
|
|
16
|
-
laneRecipe: ZoraColorToneLaneRecipe;
|
|
17
|
-
roleChromaFactors: ZoraColorToneRoleChromaFactors;
|
|
18
|
-
maxChroma: number;
|
|
19
|
-
minMidChroma: number;
|
|
20
|
-
}
|
|
21
|
-
export declare function getZoraColorToneRecipe(colorTone: ZoraColorTone): ZoraColorToneRecipe;
|
|
22
|
-
export declare function getZoraColorToneRoleChromaFactor(recipe: ZoraColorToneRecipe, role: ZoraHueScaleRoleId): number;
|
|
23
|
-
//# sourceMappingURL=colorToneRecipes.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"colorToneRecipes.d.ts","sourceRoot":"","sources":["../../../src/internal/color/colorToneRecipes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEnD,MAAM,WAAW,uBAAuB;IACtC,cAAc,EAAE,aAAa,CAAC;IAC9B,cAAc,EAAE,aAAa,CAAC;CAC/B;AAED,MAAM,WAAW,8BAA8B;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,aAAa,CAAC;IACzB,UAAU,EAAE,uBAAuB,CAAC;IACpC,iBAAiB,EAAE,8BAA8B,CAAC;IAClD,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAuID,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,aAAa,GAAG,mBAAmB,CAEpF;AAED,wBAAgB,gCAAgC,CAC9C,MAAM,EAAE,mBAAmB,EAC3B,IAAI,EAAE,kBAAkB,GACvB,MAAM,CAER"}
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
const ZORA_COLOR_TONE_RECIPES = {
|
|
2
|
-
neutral: {
|
|
3
|
-
colorTone: 'neutral',
|
|
4
|
-
laneRecipe: { backgroundTone: 'neutral', foregroundTone: 'jewel' },
|
|
5
|
-
roleChromaFactors: {
|
|
6
|
-
primary: 0.72,
|
|
7
|
-
secondary: 0.48,
|
|
8
|
-
accent: 0.56,
|
|
9
|
-
highlight: 0.6,
|
|
10
|
-
surfaceTint: 0.12,
|
|
11
|
-
},
|
|
12
|
-
maxChroma: 0.14,
|
|
13
|
-
minMidChroma: 0.025,
|
|
14
|
-
},
|
|
15
|
-
pastel: {
|
|
16
|
-
colorTone: 'pastel',
|
|
17
|
-
laneRecipe: { backgroundTone: 'pastel', foregroundTone: 'jewel' },
|
|
18
|
-
roleChromaFactors: {
|
|
19
|
-
primary: 0.58,
|
|
20
|
-
secondary: 0.48,
|
|
21
|
-
accent: 0.55,
|
|
22
|
-
highlight: 0.62,
|
|
23
|
-
surfaceTint: 0.2,
|
|
24
|
-
},
|
|
25
|
-
maxChroma: 0.12,
|
|
26
|
-
minMidChroma: 0.02,
|
|
27
|
-
},
|
|
28
|
-
earth: {
|
|
29
|
-
colorTone: 'earth',
|
|
30
|
-
laneRecipe: { backgroundTone: 'earth', foregroundTone: 'mineral' },
|
|
31
|
-
roleChromaFactors: {
|
|
32
|
-
primary: 0.64,
|
|
33
|
-
secondary: 0.52,
|
|
34
|
-
accent: 0.58,
|
|
35
|
-
highlight: 0.6,
|
|
36
|
-
surfaceTint: 0.16,
|
|
37
|
-
},
|
|
38
|
-
maxChroma: 0.13,
|
|
39
|
-
minMidChroma: 0.022,
|
|
40
|
-
},
|
|
41
|
-
mineral: {
|
|
42
|
-
colorTone: 'mineral',
|
|
43
|
-
laneRecipe: { backgroundTone: 'mineral', foregroundTone: 'jewel' },
|
|
44
|
-
roleChromaFactors: {
|
|
45
|
-
primary: 0.7,
|
|
46
|
-
secondary: 0.56,
|
|
47
|
-
accent: 0.64,
|
|
48
|
-
highlight: 0.68,
|
|
49
|
-
surfaceTint: 0.16,
|
|
50
|
-
},
|
|
51
|
-
maxChroma: 0.14,
|
|
52
|
-
minMidChroma: 0.025,
|
|
53
|
-
},
|
|
54
|
-
muted: {
|
|
55
|
-
colorTone: 'muted',
|
|
56
|
-
laneRecipe: { backgroundTone: 'muted', foregroundTone: 'jewel' },
|
|
57
|
-
roleChromaFactors: {
|
|
58
|
-
primary: 0.6,
|
|
59
|
-
secondary: 0.5,
|
|
60
|
-
accent: 0.56,
|
|
61
|
-
highlight: 0.6,
|
|
62
|
-
surfaceTint: 0.14,
|
|
63
|
-
},
|
|
64
|
-
maxChroma: 0.12,
|
|
65
|
-
minMidChroma: 0.02,
|
|
66
|
-
},
|
|
67
|
-
jewel: {
|
|
68
|
-
colorTone: 'jewel',
|
|
69
|
-
laneRecipe: { backgroundTone: 'neutral', foregroundTone: 'jewel' },
|
|
70
|
-
roleChromaFactors: {
|
|
71
|
-
primary: 1,
|
|
72
|
-
secondary: 0.72,
|
|
73
|
-
accent: 0.85,
|
|
74
|
-
highlight: 1,
|
|
75
|
-
surfaceTint: 0.18,
|
|
76
|
-
},
|
|
77
|
-
maxChroma: 0.2,
|
|
78
|
-
minMidChroma: 0.04,
|
|
79
|
-
},
|
|
80
|
-
fluorescent: {
|
|
81
|
-
colorTone: 'fluorescent',
|
|
82
|
-
laneRecipe: { backgroundTone: 'obsidian', foregroundTone: 'fluorescent' },
|
|
83
|
-
roleChromaFactors: {
|
|
84
|
-
primary: 1.12,
|
|
85
|
-
secondary: 0.82,
|
|
86
|
-
accent: 1.05,
|
|
87
|
-
highlight: 1.18,
|
|
88
|
-
surfaceTint: 0.22,
|
|
89
|
-
},
|
|
90
|
-
maxChroma: 0.24,
|
|
91
|
-
minMidChroma: 0.045,
|
|
92
|
-
},
|
|
93
|
-
obsidian: {
|
|
94
|
-
colorTone: 'obsidian',
|
|
95
|
-
laneRecipe: { backgroundTone: 'obsidian', foregroundTone: 'fluorescent' },
|
|
96
|
-
roleChromaFactors: {
|
|
97
|
-
primary: 1.08,
|
|
98
|
-
secondary: 0.78,
|
|
99
|
-
accent: 1,
|
|
100
|
-
highlight: 1.12,
|
|
101
|
-
surfaceTint: 0.2,
|
|
102
|
-
},
|
|
103
|
-
maxChroma: 0.22,
|
|
104
|
-
minMidChroma: 0.04,
|
|
105
|
-
},
|
|
106
|
-
vaporwave: {
|
|
107
|
-
colorTone: 'vaporwave',
|
|
108
|
-
laneRecipe: { backgroundTone: 'pastel', foregroundTone: 'fluorescent' },
|
|
109
|
-
roleChromaFactors: {
|
|
110
|
-
primary: 0.95,
|
|
111
|
-
secondary: 0.72,
|
|
112
|
-
accent: 1,
|
|
113
|
-
highlight: 1.08,
|
|
114
|
-
surfaceTint: 0.24,
|
|
115
|
-
},
|
|
116
|
-
maxChroma: 0.2,
|
|
117
|
-
minMidChroma: 0.035,
|
|
118
|
-
},
|
|
119
|
-
monochromeAccent: {
|
|
120
|
-
colorTone: 'monochromeAccent',
|
|
121
|
-
laneRecipe: { backgroundTone: 'neutral', foregroundTone: 'jewel' },
|
|
122
|
-
roleChromaFactors: {
|
|
123
|
-
primary: 0.88,
|
|
124
|
-
secondary: 0.36,
|
|
125
|
-
accent: 0.95,
|
|
126
|
-
highlight: 0.88,
|
|
127
|
-
surfaceTint: 0.08,
|
|
128
|
-
},
|
|
129
|
-
maxChroma: 0.18,
|
|
130
|
-
minMidChroma: 0.035,
|
|
131
|
-
},
|
|
132
|
-
};
|
|
133
|
-
export function getZoraColorToneRecipe(colorTone) {
|
|
134
|
-
return ZORA_COLOR_TONE_RECIPES[colorTone];
|
|
135
|
-
}
|
|
136
|
-
export function getZoraColorToneRoleChromaFactor(recipe, role) {
|
|
137
|
-
return recipe.roleChromaFactors[role];
|
|
138
|
-
}
|
|
139
|
-
//# sourceMappingURL=colorToneRecipes.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"colorToneRecipes.js","sourceRoot":"","sources":["../../../src/internal/color/colorToneRecipes.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ZoraColorTone } from '../../theme/types';\nimport type { ZoraHueScaleRoleId } from './scales';\n\nexport interface ZoraColorToneLaneRecipe {\n backgroundTone: ZoraColorTone;\n foregroundTone: ZoraColorTone;\n}\n\nexport interface ZoraColorToneRoleChromaFactors {\n primary: number;\n secondary: number;\n accent: number;\n highlight: number;\n surfaceTint: number;\n}\n\nexport interface ZoraColorToneRecipe {\n colorTone: ZoraColorTone;\n laneRecipe: ZoraColorToneLaneRecipe;\n roleChromaFactors: ZoraColorToneRoleChromaFactors;\n maxChroma: number;\n minMidChroma: number;\n}\n\nconst ZORA_COLOR_TONE_RECIPES = {\n neutral: {\n colorTone: 'neutral',\n laneRecipe: { backgroundTone: 'neutral', foregroundTone: 'jewel' },\n roleChromaFactors: {\n primary: 0.72,\n secondary: 0.48,\n accent: 0.56,\n highlight: 0.6,\n surfaceTint: 0.12,\n },\n maxChroma: 0.14,\n minMidChroma: 0.025,\n },\n pastel: {\n colorTone: 'pastel',\n laneRecipe: { backgroundTone: 'pastel', foregroundTone: 'jewel' },\n roleChromaFactors: {\n primary: 0.58,\n secondary: 0.48,\n accent: 0.55,\n highlight: 0.62,\n surfaceTint: 0.2,\n },\n maxChroma: 0.12,\n minMidChroma: 0.02,\n },\n earth: {\n colorTone: 'earth',\n laneRecipe: { backgroundTone: 'earth', foregroundTone: 'mineral' },\n roleChromaFactors: {\n primary: 0.64,\n secondary: 0.52,\n accent: 0.58,\n highlight: 0.6,\n surfaceTint: 0.16,\n },\n maxChroma: 0.13,\n minMidChroma: 0.022,\n },\n mineral: {\n colorTone: 'mineral',\n laneRecipe: { backgroundTone: 'mineral', foregroundTone: 'jewel' },\n roleChromaFactors: {\n primary: 0.7,\n secondary: 0.56,\n accent: 0.64,\n highlight: 0.68,\n surfaceTint: 0.16,\n },\n maxChroma: 0.14,\n minMidChroma: 0.025,\n },\n muted: {\n colorTone: 'muted',\n laneRecipe: { backgroundTone: 'muted', foregroundTone: 'jewel' },\n roleChromaFactors: {\n primary: 0.6,\n secondary: 0.5,\n accent: 0.56,\n highlight: 0.6,\n surfaceTint: 0.14,\n },\n maxChroma: 0.12,\n minMidChroma: 0.02,\n },\n jewel: {\n colorTone: 'jewel',\n laneRecipe: { backgroundTone: 'neutral', foregroundTone: 'jewel' },\n roleChromaFactors: {\n primary: 1,\n secondary: 0.72,\n accent: 0.85,\n highlight: 1,\n surfaceTint: 0.18,\n },\n maxChroma: 0.2,\n minMidChroma: 0.04,\n },\n fluorescent: {\n colorTone: 'fluorescent',\n laneRecipe: { backgroundTone: 'obsidian', foregroundTone: 'fluorescent' },\n roleChromaFactors: {\n primary: 1.12,\n secondary: 0.82,\n accent: 1.05,\n highlight: 1.18,\n surfaceTint: 0.22,\n },\n maxChroma: 0.24,\n minMidChroma: 0.045,\n },\n obsidian: {\n colorTone: 'obsidian',\n laneRecipe: { backgroundTone: 'obsidian', foregroundTone: 'fluorescent' },\n roleChromaFactors: {\n primary: 1.08,\n secondary: 0.78,\n accent: 1,\n highlight: 1.12,\n surfaceTint: 0.2,\n },\n maxChroma: 0.22,\n minMidChroma: 0.04,\n },\n vaporwave: {\n colorTone: 'vaporwave',\n laneRecipe: { backgroundTone: 'pastel', foregroundTone: 'fluorescent' },\n roleChromaFactors: {\n primary: 0.95,\n secondary: 0.72,\n accent: 1,\n highlight: 1.08,\n surfaceTint: 0.24,\n },\n maxChroma: 0.2,\n minMidChroma: 0.035,\n },\n monochromeAccent: {\n colorTone: 'monochromeAccent',\n laneRecipe: { backgroundTone: 'neutral', foregroundTone: 'jewel' },\n roleChromaFactors: {\n primary: 0.88,\n secondary: 0.36,\n accent: 0.95,\n highlight: 0.88,\n surfaceTint: 0.08,\n },\n maxChroma: 0.18,\n minMidChroma: 0.035,\n },\n} satisfies Record<ZoraColorTone, ZoraColorToneRecipe>;\n\nexport function getZoraColorToneRecipe(colorTone: ZoraColorTone): ZoraColorToneRecipe {\n return ZORA_COLOR_TONE_RECIPES[colorTone];\n}\n\nexport function getZoraColorToneRoleChromaFactor(\n recipe: ZoraColorToneRecipe,\n role: ZoraHueScaleRoleId,\n): number {\n return recipe.roleChromaFactors[role];\n}\n"]}
|