@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.
Files changed (114) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +11 -13
  3. package/dist/components/heading/resolveHeadingRecipe.d.ts +2 -2
  4. package/dist/components/heading/resolveHeadingRecipe.d.ts.map +1 -1
  5. package/dist/components/heading/resolveHeadingRecipe.js.map +1 -1
  6. package/dist/components/text/resolveTextRecipe.d.ts +2 -2
  7. package/dist/components/text/resolveTextRecipe.d.ts.map +1 -1
  8. package/dist/components/text/resolveTextRecipe.js.map +1 -1
  9. package/dist/patterns/theme-composer/ThemeComposer.d.ts.map +1 -1
  10. package/dist/patterns/theme-composer/ThemeComposer.js +10 -86
  11. package/dist/patterns/theme-composer/ThemeComposer.js.map +1 -1
  12. package/dist/patterns/theme-composer/index.d.ts +1 -1
  13. package/dist/patterns/theme-composer/index.d.ts.map +1 -1
  14. package/dist/patterns/theme-composer/index.js.map +1 -1
  15. package/dist/patterns/theme-composer/types.d.ts +1 -13
  16. package/dist/patterns/theme-composer/types.d.ts.map +1 -1
  17. package/dist/patterns/theme-composer/types.js.map +1 -1
  18. package/dist/theme/createZoraThemeConfig.d.ts +1 -1
  19. package/dist/theme/createZoraThemeConfig.d.ts.map +1 -1
  20. package/dist/theme/createZoraThemeConfig.js +5 -6
  21. package/dist/theme/createZoraThemeConfig.js.map +1 -1
  22. package/dist/theme/index.d.ts +1 -1
  23. package/dist/theme/index.d.ts.map +1 -1
  24. package/dist/theme/index.js.map +1 -1
  25. package/dist/theme/types.d.ts +16 -11
  26. package/dist/theme/types.d.ts.map +1 -1
  27. package/dist/theme/types.js +1 -20
  28. package/dist/theme/types.js.map +1 -1
  29. package/dist/theme/useZoraTheme.d.ts +1 -1
  30. package/dist/theme/zoraDefaultTheme.js +1 -1
  31. package/dist/theme/zoraDefaultTheme.js.map +1 -1
  32. package/package.json +4 -4
  33. package/src/components/heading/resolveHeadingRecipe.test.ts +30 -5
  34. package/src/components/heading/resolveHeadingRecipe.ts +6 -6
  35. package/src/components/text/resolveTextRecipe.test.ts +30 -5
  36. package/src/components/text/resolveTextRecipe.ts +6 -6
  37. package/src/patterns/theme-composer/ThemeComposer.test.ts +9 -141
  38. package/src/patterns/theme-composer/ThemeComposer.tsx +10 -131
  39. package/src/patterns/theme-composer/index.ts +1 -6
  40. package/src/patterns/theme-composer/types.ts +1 -15
  41. package/src/showcaseCoverage.test.ts +14 -0
  42. package/src/theme/createZoraThemeConfig.test.ts +51 -26
  43. package/src/theme/createZoraThemeConfig.ts +7 -7
  44. package/src/theme/index.ts +1 -3
  45. package/src/theme/types.ts +22 -34
  46. package/src/theme/zoraDefaultTheme.ts +1 -1
  47. package/dist/internal/color/colorToneRecipes.d.ts +0 -23
  48. package/dist/internal/color/colorToneRecipes.d.ts.map +0 -1
  49. package/dist/internal/color/colorToneRecipes.js +0 -139
  50. package/dist/internal/color/colorToneRecipes.js.map +0 -1
  51. package/dist/internal/color/harmony.d.ts +0 -12
  52. package/dist/internal/color/harmony.d.ts.map +0 -1
  53. package/dist/internal/color/harmony.js +0 -69
  54. package/dist/internal/color/harmony.js.map +0 -1
  55. package/dist/internal/color/hue.d.ts +0 -3
  56. package/dist/internal/color/hue.d.ts.map +0 -1
  57. package/dist/internal/color/hue.js +0 -7
  58. package/dist/internal/color/hue.js.map +0 -1
  59. package/dist/internal/color/index.d.ts +0 -10
  60. package/dist/internal/color/index.d.ts.map +0 -1
  61. package/dist/internal/color/index.js +0 -10
  62. package/dist/internal/color/index.js.map +0 -1
  63. package/dist/internal/color/oklch.d.ts +0 -6
  64. package/dist/internal/color/oklch.d.ts.map +0 -1
  65. package/dist/internal/color/oklch.js +0 -50
  66. package/dist/internal/color/oklch.js.map +0 -1
  67. package/dist/internal/color/primary.d.ts +0 -3
  68. package/dist/internal/color/primary.d.ts.map +0 -1
  69. package/dist/internal/color/primary.js +0 -44
  70. package/dist/internal/color/primary.js.map +0 -1
  71. package/dist/internal/color/roleHues.d.ts +0 -15
  72. package/dist/internal/color/roleHues.d.ts.map +0 -1
  73. package/dist/internal/color/roleHues.js +0 -103
  74. package/dist/internal/color/roleHues.js.map +0 -1
  75. package/dist/internal/color/roleScales.d.ts +0 -20
  76. package/dist/internal/color/roleScales.d.ts.map +0 -1
  77. package/dist/internal/color/roleScales.js +0 -79
  78. package/dist/internal/color/roleScales.js.map +0 -1
  79. package/dist/internal/color/scales.d.ts +0 -19
  80. package/dist/internal/color/scales.d.ts.map +0 -1
  81. package/dist/internal/color/scales.js +0 -135
  82. package/dist/internal/color/scales.js.map +0 -1
  83. package/dist/internal/color/semanticTokens.d.ts +0 -28
  84. package/dist/internal/color/semanticTokens.d.ts.map +0 -1
  85. package/dist/internal/color/semanticTokens.js +0 -84
  86. package/dist/internal/color/semanticTokens.js.map +0 -1
  87. package/dist/internal/color/types.d.ts +0 -10
  88. package/dist/internal/color/types.d.ts.map +0 -1
  89. package/dist/internal/color/types.js +0 -4
  90. package/dist/internal/color/types.js.map +0 -1
  91. package/dist/patterns/theme-composer/recommendations.d.ts +0 -14
  92. package/dist/patterns/theme-composer/recommendations.d.ts.map +0 -1
  93. package/dist/patterns/theme-composer/recommendations.js +0 -58
  94. package/dist/patterns/theme-composer/recommendations.js.map +0 -1
  95. package/src/internal/color/colorToneRecipes.test.ts +0 -89
  96. package/src/internal/color/colorToneRecipes.ts +0 -167
  97. package/src/internal/color/harmony.test.ts +0 -145
  98. package/src/internal/color/harmony.ts +0 -96
  99. package/src/internal/color/hue.test.ts +0 -28
  100. package/src/internal/color/hue.ts +0 -7
  101. package/src/internal/color/index.ts +0 -44
  102. package/src/internal/color/oklch.ts +0 -65
  103. package/src/internal/color/primary.test.ts +0 -105
  104. package/src/internal/color/primary.ts +0 -64
  105. package/src/internal/color/roleHues.test.ts +0 -197
  106. package/src/internal/color/roleHues.ts +0 -142
  107. package/src/internal/color/roleScales.test.ts +0 -220
  108. package/src/internal/color/roleScales.ts +0 -127
  109. package/src/internal/color/scales.test.ts +0 -151
  110. package/src/internal/color/scales.ts +0 -194
  111. package/src/internal/color/semanticTokens.test.ts +0 -170
  112. package/src/internal/color/semanticTokens.ts +0 -114
  113. package/src/internal/color/types.ts +0 -15
  114. 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
- return HEX_RE.test(value);
19
+ try {
20
+ parseHexColorOrThrow(value);
21
+ return true;
22
+ } catch {
23
+ return false;
24
+ }
32
25
  }
33
26
 
34
- const HARMONY_OPTIONS = ZORA_COLOR_HARMONIES.map((h) => ({ value: h, label: h }));
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 as ZoraTheme['primaryColor'] });
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,7 +1,2 @@
1
1
  export { ThemeComposer } from './ThemeComposer';
2
- export type {
3
- ThemeComposerAppCategory,
4
- ThemeComposerAppMood,
5
- ThemeComposerProps,
6
- ThemeComposerRecommendation,
7
- } from './types';
2
+ export type { ThemeComposerProps } from './types';
@@ -1,24 +1,10 @@
1
- import type { ZoraColorHarmony, ZoraColorTone, ZoraTheme, ZoraThemeMode } from '../../theme/types';
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 type { ZoraHexColor } from './types';
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(isSixDigitHexColor(themeConfig.light.primaryColor)).toBe(true);
12
+ expect(themeConfig.light.primaryColor).toBe(zoraDefaultTheme.primaryColor);
18
13
  expect(themeConfig.light.harmony).toBe('analogous');
19
- expect(themeConfig.light.colorTone).toBe('jewel');
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('falls back to id when name is omitted', () => {
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('studio');
41
- expect(isSixDigitHexColor(themeConfig.light.primaryColor)).toBe(true);
42
- expect(themeConfig.light.harmony).toBe('analogous');
43
- expect(themeConfig.light.colorTone).toBe('jewel');
44
- expect(isSixDigitHexColor(themeConfig.dark.primaryColor)).toBe(true);
45
- expect(themeConfig.dark.harmony).toBe('analogous');
46
- expect(themeConfig.dark.colorTone).toBe('jewel');
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.light.primaryColor).not.toBe(themeConfig.dark.primaryColor);
73
+ expect(themeConfig.name).toBe('My Theme');
49
74
  });
50
75
  });
@@ -1,22 +1,22 @@
1
- import type { ThemeConfig } from '@ankhorage/surface';
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 ?? theme.id,
12
+ name: theme.name,
11
13
  light: {
12
- primaryColor: resolveModePrimaryColor(theme.primaryColor, 'light'),
14
+ primaryColor,
13
15
  harmony: theme.harmony,
14
- colorTone: theme.colorTone,
15
16
  },
16
17
  dark: {
17
- primaryColor: resolveModePrimaryColor(theme.primaryColor, 'dark'),
18
+ primaryColor,
18
19
  harmony: theme.harmony,
19
- colorTone: theme.colorTone,
20
20
  },
21
21
  };
22
22
  }
@@ -1,9 +1,7 @@
1
1
  export { createZoraThemeConfig } from './createZoraThemeConfig';
2
2
  export type {
3
- ZoraColorHarmony,
4
- ZoraColorTone,
5
3
  ZoraComputedTheme,
6
- ZoraHexColor,
4
+ ZoraComputedThemeMode,
7
5
  ZoraTheme,
8
6
  ZoraThemeId,
9
7
  ZoraThemeMode,
@@ -1,49 +1,37 @@
1
- import type { ColorHarmony, ColorTone, ThemeConfig } from '@ankhorage/surface';
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?: string;
38
- primaryColor: ZoraHexColor;
39
- harmony: ZoraColorHarmony;
40
- colorTone: ZoraColorTone;
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
  }
@@ -3,7 +3,7 @@ import type { ZoraTheme } from './types';
3
3
  export const zoraDefaultTheme: ZoraTheme = {
4
4
  id: 'zora',
5
5
  name: 'ZORA',
6
+ appCategory: 'developer_tools',
6
7
  primaryColor: '#0f766e',
7
8
  harmony: 'analogous',
8
- colorTone: 'jewel',
9
9
  };
@@ -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"]}