@ankhorage/zora 0.12.2 → 0.13.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.
Files changed (37) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/internal/color/colorToneRecipes.d.ts +23 -0
  3. package/dist/internal/color/colorToneRecipes.d.ts.map +1 -0
  4. package/dist/internal/color/colorToneRecipes.js +139 -0
  5. package/dist/internal/color/colorToneRecipes.js.map +1 -0
  6. package/dist/internal/color/index.d.ts +3 -1
  7. package/dist/internal/color/index.d.ts.map +1 -1
  8. package/dist/internal/color/index.js +2 -0
  9. package/dist/internal/color/index.js.map +1 -1
  10. package/dist/internal/color/roleScales.d.ts +20 -0
  11. package/dist/internal/color/roleScales.d.ts.map +1 -0
  12. package/dist/internal/color/roleScales.js +79 -0
  13. package/dist/internal/color/roleScales.js.map +1 -0
  14. package/dist/internal/color/scales.d.ts +9 -0
  15. package/dist/internal/color/scales.d.ts.map +1 -1
  16. package/dist/internal/color/scales.js +25 -0
  17. package/dist/internal/color/scales.js.map +1 -1
  18. package/dist/theme/createZoraThemeConfig.js +2 -2
  19. package/dist/theme/createZoraThemeConfig.js.map +1 -1
  20. package/dist/theme/types.d.ts +6 -4
  21. package/dist/theme/types.d.ts.map +1 -1
  22. package/dist/theme/types.js +20 -1
  23. package/dist/theme/types.js.map +1 -1
  24. package/dist/theme/zoraDefaultTheme.js +1 -1
  25. package/dist/theme/zoraDefaultTheme.js.map +1 -1
  26. package/package.json +2 -2
  27. package/src/internal/color/colorToneRecipes.test.ts +89 -0
  28. package/src/internal/color/colorToneRecipes.ts +167 -0
  29. package/src/internal/color/index.ts +17 -0
  30. package/src/internal/color/roleHues.test.ts +1 -1
  31. package/src/internal/color/roleScales.test.ts +220 -0
  32. package/src/internal/color/roleScales.ts +127 -0
  33. package/src/internal/color/scales.ts +49 -0
  34. package/src/theme/createZoraThemeConfig.test.ts +5 -5
  35. package/src/theme/createZoraThemeConfig.ts +2 -2
  36. package/src/theme/types.ts +26 -10
  37. package/src/theme/zoraDefaultTheme.ts +1 -1
@@ -1,4 +1,6 @@
1
1
  import type { ZoraHexColor } from '../../theme/types';
2
+ import type { ZoraColorToneRecipe } from './colorToneRecipes';
3
+ import { getZoraColorToneRoleChromaFactor } from './colorToneRecipes';
2
4
  import { clampOklchToGamut, formatOklchAsHex, parseHexToOklch } from './oklch';
3
5
  import { type ZoraColorScale, type ZoraColorScaleStep } from './types';
4
6
 
@@ -7,6 +9,15 @@ export interface CreateZoraColorScaleOptions {
7
9
  role?: 'primary' | 'neutral';
8
10
  }
9
11
 
12
+ export type ZoraHueScaleRoleId = 'primary' | 'secondary' | 'accent' | 'highlight' | 'surfaceTint';
13
+
14
+ export interface CreateZoraHueScaleOptions {
15
+ hue: number;
16
+ seedChroma: number;
17
+ role: ZoraHueScaleRoleId;
18
+ colorToneRecipe: ZoraColorToneRecipe;
19
+ }
20
+
10
21
  const PRIMARY_LIGHTNESS_BY_STEP: Record<ZoraColorScaleStep, number> = {
11
22
  50: 0.97,
12
23
  100: 0.93,
@@ -69,6 +80,36 @@ function resolvePrimaryScaleChroma(seedChroma: number, step: ZoraColorScaleStep)
69
80
  return shouldEnforceMin ? Math.max(bounded, MIN_PRIMARY_SCALE_CHROMA) : bounded;
70
81
  }
71
82
 
83
+ function resolveRoleScaleChroma(options: {
84
+ seedChroma: number;
85
+ step: ZoraColorScaleStep;
86
+ maxChroma: number;
87
+ minMidChroma: number;
88
+ }): number {
89
+ const cappedSeedChroma = clampNumber(options.seedChroma, 0, options.maxChroma);
90
+ const multiplier = PRIMARY_CHROMA_MULTIPLIER_BY_STEP[options.step];
91
+ const scaled = cappedSeedChroma * multiplier;
92
+ const bounded = clampNumber(scaled, 0, options.maxChroma);
93
+ const shouldEnforceMin =
94
+ options.step >= 300 && options.step <= 700 && options.seedChroma >= options.minMidChroma;
95
+
96
+ return shouldEnforceMin ? Math.max(bounded, options.minMidChroma) : bounded;
97
+ }
98
+
99
+ function resolveHueScaleChroma(
100
+ options: CreateZoraHueScaleOptions,
101
+ step: ZoraColorScaleStep,
102
+ ): number {
103
+ const factor = getZoraColorToneRoleChromaFactor(options.colorToneRecipe, options.role);
104
+
105
+ return resolveRoleScaleChroma({
106
+ seedChroma: options.seedChroma * factor,
107
+ step,
108
+ maxChroma: options.colorToneRecipe.maxChroma,
109
+ minMidChroma: options.colorToneRecipe.minMidChroma,
110
+ });
111
+ }
112
+
72
113
  function createScaleEntries(options: CreateZoraColorScaleOptions): ZoraColorScale {
73
114
  const seed = parseHexToOklch(options.seed);
74
115
 
@@ -143,3 +184,11 @@ export function createZoraPrimaryScale(seed: ZoraHexColor): ZoraColorScale {
143
184
  export function createZoraNeutralScale(seed: ZoraHexColor = '#94a3b8'): ZoraColorScale {
144
185
  return createZoraColorScale({ seed, role: 'neutral' });
145
186
  }
187
+
188
+ export function createZoraHueScale(options: CreateZoraHueScaleOptions): ZoraColorScale {
189
+ return createScaleFromRamp({
190
+ hue: options.hue,
191
+ chromaByStep: (step) => resolveHueScaleChroma(options, step),
192
+ lightnessByStep: PRIMARY_LIGHTNESS_BY_STEP,
193
+ });
194
+ }
@@ -16,10 +16,10 @@ describe('createZoraThemeConfig', () => {
16
16
  expect(themeConfig.name).toBe('ZORA');
17
17
  expect(isSixDigitHexColor(themeConfig.light.primaryColor)).toBe(true);
18
18
  expect(themeConfig.light.harmony).toBe('analogous');
19
- expect(themeConfig.light.systemTone).toBe('jewel');
19
+ expect(themeConfig.light.colorTone).toBe('jewel');
20
20
  expect(isSixDigitHexColor(themeConfig.dark.primaryColor)).toBe(true);
21
21
  expect(themeConfig.dark.harmony).toBe('analogous');
22
- expect(themeConfig.dark.systemTone).toBe('jewel');
22
+ expect(themeConfig.dark.colorTone).toBe('jewel');
23
23
 
24
24
  expect(themeConfig.light.primaryColor).not.toBe(themeConfig.dark.primaryColor);
25
25
 
@@ -33,17 +33,17 @@ describe('createZoraThemeConfig', () => {
33
33
  id: 'studio',
34
34
  primaryColor: '#0f766e',
35
35
  harmony: 'analogous',
36
- tone: 'jewel',
36
+ colorTone: 'jewel',
37
37
  });
38
38
 
39
39
  expect(themeConfig.id).toBe('studio');
40
40
  expect(themeConfig.name).toBe('studio');
41
41
  expect(isSixDigitHexColor(themeConfig.light.primaryColor)).toBe(true);
42
42
  expect(themeConfig.light.harmony).toBe('analogous');
43
- expect(themeConfig.light.systemTone).toBe('jewel');
43
+ expect(themeConfig.light.colorTone).toBe('jewel');
44
44
  expect(isSixDigitHexColor(themeConfig.dark.primaryColor)).toBe(true);
45
45
  expect(themeConfig.dark.harmony).toBe('analogous');
46
- expect(themeConfig.dark.systemTone).toBe('jewel');
46
+ expect(themeConfig.dark.colorTone).toBe('jewel');
47
47
 
48
48
  expect(themeConfig.light.primaryColor).not.toBe(themeConfig.dark.primaryColor);
49
49
  });
@@ -11,12 +11,12 @@ export function createZoraThemeConfig(theme: ZoraTheme = zoraDefaultTheme): Them
11
11
  light: {
12
12
  primaryColor: resolveModePrimaryColor(theme.primaryColor, 'light'),
13
13
  harmony: theme.harmony,
14
- systemTone: theme.tone,
14
+ colorTone: theme.colorTone,
15
15
  },
16
16
  dark: {
17
17
  primaryColor: resolveModePrimaryColor(theme.primaryColor, 'dark'),
18
18
  harmony: theme.harmony,
19
- systemTone: theme.tone,
19
+ colorTone: theme.colorTone,
20
20
  },
21
21
  };
22
22
  }
@@ -1,4 +1,4 @@
1
- import type { ThemeConfig } from '@ankhorage/surface';
1
+ import type { ColorHarmony, ColorTone, ThemeConfig } from '@ankhorage/surface';
2
2
 
3
3
  export type ZoraThemeId = string;
4
4
 
@@ -6,22 +6,38 @@ export type ZoraThemeMode = 'light' | 'dark';
6
6
 
7
7
  export type ZoraHexColor = `#${string}`;
8
8
 
9
- export type ZoraColorHarmony =
10
- | 'monochromatic'
11
- | 'analogous'
12
- | 'complementary'
13
- | 'splitComplementary'
14
- | 'triadic'
15
- | 'tetradic';
9
+ export const ZORA_COLOR_HARMONIES = [
10
+ 'monochromatic',
11
+ 'analogous',
12
+ 'complementary',
13
+ 'splitComplementary',
14
+ 'triadic',
15
+ 'tetradic',
16
+ ] as const satisfies readonly ColorHarmony[];
16
17
 
17
- export type ZoraColorTone = 'neutral' | 'pastel' | 'earth' | 'jewel' | 'fluorescent';
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;
18
34
 
19
35
  export interface ZoraTheme {
20
36
  id: ZoraThemeId;
21
37
  name?: string;
22
38
  primaryColor: ZoraHexColor;
23
39
  harmony: ZoraColorHarmony;
24
- tone: ZoraColorTone;
40
+ colorTone: ZoraColorTone;
25
41
  }
26
42
 
27
43
  export interface ZoraComputedTheme {
@@ -5,5 +5,5 @@ export const zoraDefaultTheme: ZoraTheme = {
5
5
  name: 'ZORA',
6
6
  primaryColor: '#0f766e',
7
7
  harmony: 'analogous',
8
- tone: 'jewel',
8
+ colorTone: 'jewel',
9
9
  };