@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
package/dist/theme/types.d.ts
CHANGED
|
@@ -1,23 +1,28 @@
|
|
|
1
|
-
import type { ColorHarmony,
|
|
1
|
+
import type { ColorHarmony, GeneratedThemeModeColors, GeneratedThemeSwatches, SemanticColorToken } from '@ankhorage/color-theory';
|
|
2
|
+
import type { AppCategory, ThemeConfig } from '@ankhorage/contracts';
|
|
3
|
+
import type { SurfaceTheme } from '@ankhorage/surface';
|
|
2
4
|
export type ZoraThemeId = string;
|
|
3
5
|
export type ZoraThemeMode = 'light' | 'dark';
|
|
4
|
-
export type ZoraHexColor = `#${string}`;
|
|
5
|
-
export declare const ZORA_COLOR_HARMONIES: readonly ["monochromatic", "analogous", "complementary", "splitComplementary", "triadic", "tetradic"];
|
|
6
|
-
export type ZoraColorHarmony = ColorHarmony;
|
|
7
|
-
export declare const ZORA_COLOR_TONES: readonly ["neutral", "pastel", "earth", "mineral", "muted", "jewel", "fluorescent", "obsidian", "vaporwave", "monochromeAccent"];
|
|
8
|
-
export type ZoraColorTone = ColorTone;
|
|
9
6
|
export interface ZoraTheme {
|
|
10
7
|
id: ZoraThemeId;
|
|
11
|
-
name
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
name: string;
|
|
9
|
+
appCategory: AppCategory;
|
|
10
|
+
primaryColor: string;
|
|
11
|
+
harmony: ColorHarmony;
|
|
12
|
+
}
|
|
13
|
+
export interface ZoraComputedThemeMode {
|
|
14
|
+
mode: ZoraThemeMode;
|
|
15
|
+
surfaceTheme: SurfaceTheme;
|
|
16
|
+
generated: GeneratedThemeModeColors;
|
|
17
|
+
swatches: GeneratedThemeSwatches;
|
|
18
|
+
semanticColors?: Record<SemanticColorToken, string>;
|
|
15
19
|
}
|
|
16
20
|
export interface ZoraComputedTheme {
|
|
17
21
|
id: ZoraThemeId;
|
|
18
22
|
name: string;
|
|
19
|
-
mode: ZoraThemeMode;
|
|
20
23
|
source: ZoraTheme;
|
|
21
24
|
surfaceConfig: ThemeConfig;
|
|
25
|
+
light: ZoraComputedThemeMode;
|
|
26
|
+
dark: ZoraComputedThemeMode;
|
|
22
27
|
}
|
|
23
28
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/theme/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/theme/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,wBAAwB,EACxB,sBAAsB,EACtB,kBAAkB,EACnB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC;AAEjC,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,CAAC;AAE7C,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,WAAW,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,WAAW,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,YAAY,CAAC;CACvB;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,aAAa,CAAC;IACpB,YAAY,EAAE,YAAY,CAAC;IAC3B,SAAS,EAAE,wBAAwB,CAAC;IACpC,QAAQ,EAAE,sBAAsB,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,WAAW,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,CAAC;IAClB,aAAa,EAAE,WAAW,CAAC;IAC3B,KAAK,EAAE,qBAAqB,CAAC;IAC7B,IAAI,EAAE,qBAAqB,CAAC;CAC7B"}
|
package/dist/theme/types.js
CHANGED
|
@@ -1,21 +1,2 @@
|
|
|
1
|
-
export
|
|
2
|
-
'monochromatic',
|
|
3
|
-
'analogous',
|
|
4
|
-
'complementary',
|
|
5
|
-
'splitComplementary',
|
|
6
|
-
'triadic',
|
|
7
|
-
'tetradic',
|
|
8
|
-
];
|
|
9
|
-
export const ZORA_COLOR_TONES = [
|
|
10
|
-
'neutral',
|
|
11
|
-
'pastel',
|
|
12
|
-
'earth',
|
|
13
|
-
'mineral',
|
|
14
|
-
'muted',
|
|
15
|
-
'jewel',
|
|
16
|
-
'fluorescent',
|
|
17
|
-
'obsidian',
|
|
18
|
-
'vaporwave',
|
|
19
|
-
'monochromeAccent',
|
|
20
|
-
];
|
|
1
|
+
export {};
|
|
21
2
|
//# sourceMappingURL=types.js.map
|
package/dist/theme/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/theme/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/theme/types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n ColorHarmony,\n GeneratedThemeModeColors,\n GeneratedThemeSwatches,\n SemanticColorToken,\n} from '@ankhorage/color-theory';\nimport type { AppCategory, ThemeConfig } from '@ankhorage/contracts';\nimport type { SurfaceTheme } from '@ankhorage/surface';\n\nexport type ZoraThemeId = string;\n\nexport type ZoraThemeMode = 'light' | 'dark';\n\nexport interface ZoraTheme {\n id: ZoraThemeId;\n name: string;\n appCategory: AppCategory;\n primaryColor: string;\n harmony: ColorHarmony;\n}\n\nexport interface ZoraComputedThemeMode {\n mode: ZoraThemeMode;\n surfaceTheme: SurfaceTheme;\n generated: GeneratedThemeModeColors;\n swatches: GeneratedThemeSwatches;\n semanticColors?: Record<SemanticColorToken, string>;\n}\n\nexport interface ZoraComputedTheme {\n id: ZoraThemeId;\n name: string;\n source: ZoraTheme;\n surfaceConfig: ThemeConfig;\n light: ZoraComputedThemeMode;\n dark: ZoraComputedThemeMode;\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare function useZoraTheme(): {
|
|
2
|
-
theme: import("@ankhorage/surface").
|
|
2
|
+
theme: import("@ankhorage/surface").SurfaceTheme;
|
|
3
3
|
mode: "light" | "dark";
|
|
4
4
|
setThemeConfig: (config: Partial<import("@ankhorage/contracts").ThemeConfig>) => void;
|
|
5
5
|
setMode: (mode: "light" | "dark") => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zoraDefaultTheme.js","sourceRoot":"","sources":["../../src/theme/zoraDefaultTheme.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,gBAAgB,GAAc;IACzC,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,MAAM;IACZ,YAAY,EAAE,SAAS;IACvB,OAAO,EAAE,WAAW;
|
|
1
|
+
{"version":3,"file":"zoraDefaultTheme.js","sourceRoot":"","sources":["../../src/theme/zoraDefaultTheme.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,gBAAgB,GAAc;IACzC,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,MAAM;IACZ,WAAW,EAAE,iBAAiB;IAC9B,YAAY,EAAE,SAAS;IACvB,OAAO,EAAE,WAAW;CACrB,CAAC","sourcesContent":["import type { ZoraTheme } from './types';\n\nexport const zoraDefaultTheme: ZoraTheme = {\n id: 'zora',\n name: 'ZORA',\n appCategory: 'developer_tools',\n primaryColor: '#0f766e',\n harmony: 'analogous',\n};\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ankhorage/zora",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "1.0.0",
|
|
5
5
|
"description": "Opinionated React Native and React Native Web UI kit built on @ankhorage/surface.",
|
|
6
6
|
"homepage": "https://github.com/ankhorage/zora#readme",
|
|
7
7
|
"bugs": {
|
|
@@ -43,8 +43,9 @@
|
|
|
43
43
|
}
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@ankhorage/
|
|
47
|
-
"
|
|
46
|
+
"@ankhorage/color-theory": "^0.0.2",
|
|
47
|
+
"@ankhorage/contracts": "^1.1.0",
|
|
48
|
+
"@ankhorage/surface": "^1.0.0"
|
|
48
49
|
},
|
|
49
50
|
"files": [
|
|
50
51
|
"dist",
|
|
@@ -86,7 +87,6 @@
|
|
|
86
87
|
"@changesets/cli": "^2.31.0",
|
|
87
88
|
"@expo/vector-icons": "^15.1.1",
|
|
88
89
|
"@react-native-picker/picker": "^2.11.4",
|
|
89
|
-
"@types/culori": "^4.0.1",
|
|
90
90
|
"@types/bun": "^1.3.13",
|
|
91
91
|
"@types/node": "^25.6.0",
|
|
92
92
|
"@types/react": "^19.2.14",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FontWeight, RoleSemantics, SurfaceTheme } from '@ankhorage/surface';
|
|
2
2
|
import { describe, expect, test } from 'bun:test';
|
|
3
3
|
|
|
4
4
|
import { resolveHeadingRecipe, resolveHeadingSizeFromLevel } from './resolveHeadingRecipe';
|
|
@@ -30,7 +30,7 @@ function createRole(base: string): RoleSemantics {
|
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
function createTestTheme():
|
|
33
|
+
function createTestTheme(): SurfaceTheme {
|
|
34
34
|
return {
|
|
35
35
|
colors: {
|
|
36
36
|
primary: '#0f766e',
|
|
@@ -52,12 +52,10 @@ function createTestTheme(): AnkhTheme {
|
|
|
52
52
|
light: {
|
|
53
53
|
primaryColor: '#0f766e',
|
|
54
54
|
harmony: 'analogous',
|
|
55
|
-
colorTone: 'jewel',
|
|
56
55
|
},
|
|
57
56
|
dark: {
|
|
58
57
|
primaryColor: '#2dd4bf',
|
|
59
58
|
harmony: 'analogous',
|
|
60
|
-
colorTone: 'jewel',
|
|
61
59
|
},
|
|
62
60
|
},
|
|
63
61
|
radii: {
|
|
@@ -67,7 +65,34 @@ function createTestTheme(): AnkhTheme {
|
|
|
67
65
|
l: 16,
|
|
68
66
|
full: 9999,
|
|
69
67
|
},
|
|
70
|
-
|
|
68
|
+
swatches: {
|
|
69
|
+
primary: {
|
|
70
|
+
50: '#f0fdfa',
|
|
71
|
+
100: '#ccfbf1',
|
|
72
|
+
200: '#99f6e4',
|
|
73
|
+
300: '#5eead4',
|
|
74
|
+
400: '#2dd4bf',
|
|
75
|
+
500: '#14b8a6',
|
|
76
|
+
600: '#0d9488',
|
|
77
|
+
700: '#0f766e',
|
|
78
|
+
800: '#115e59',
|
|
79
|
+
900: '#134e4a',
|
|
80
|
+
950: '#042f2e',
|
|
81
|
+
},
|
|
82
|
+
neutral: {
|
|
83
|
+
50: '#f9fafb',
|
|
84
|
+
100: '#f3f4f6',
|
|
85
|
+
200: '#e5e7eb',
|
|
86
|
+
300: '#d1d5db',
|
|
87
|
+
400: '#9ca3af',
|
|
88
|
+
500: '#6b7280',
|
|
89
|
+
600: '#4b5563',
|
|
90
|
+
700: '#374151',
|
|
91
|
+
800: '#1f2937',
|
|
92
|
+
900: '#111827',
|
|
93
|
+
950: '#030712',
|
|
94
|
+
},
|
|
95
|
+
},
|
|
71
96
|
semantics: {
|
|
72
97
|
neutral: {
|
|
73
98
|
bg: '#ffffff',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FontWeight, SurfaceTheme } from '@ankhorage/surface';
|
|
2
2
|
import type { TextStyle } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import type { HeadingAlign, HeadingLevel, HeadingSize, HeadingTone, HeadingWeight } from './types';
|
|
@@ -52,7 +52,7 @@ function resolveHeadingLevelFromSize(size: Exclude<HeadingSize, 'display'>): Hea
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
function resolveSizeRecipe(theme:
|
|
55
|
+
function resolveSizeRecipe(theme: SurfaceTheme, size: HeadingSize): HeadingRecipe {
|
|
56
56
|
if (size === 'display') {
|
|
57
57
|
const fontSize = theme.typography.sizes['3xl'];
|
|
58
58
|
|
|
@@ -72,7 +72,7 @@ function resolveSizeRecipe(theme: AnkhTheme, size: HeadingSize): HeadingRecipe {
|
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
function resolveToneColor(theme:
|
|
75
|
+
function resolveToneColor(theme: SurfaceTheme, tone: HeadingTone): string {
|
|
76
76
|
switch (tone) {
|
|
77
77
|
case 'muted':
|
|
78
78
|
return theme.semantics.content.muted;
|
|
@@ -94,7 +94,7 @@ function resolveToneColor(theme: AnkhTheme, tone: HeadingTone): string {
|
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
function resolveWeight(theme:
|
|
97
|
+
function resolveWeight(theme: SurfaceTheme, weight: HeadingWeight): FontWeight {
|
|
98
98
|
return theme.typography.weights[weight];
|
|
99
99
|
}
|
|
100
100
|
|
|
@@ -103,7 +103,7 @@ function resolveFontFamily({
|
|
|
103
103
|
weight,
|
|
104
104
|
italic,
|
|
105
105
|
}: {
|
|
106
|
-
theme:
|
|
106
|
+
theme: SurfaceTheme;
|
|
107
107
|
weight: FontWeight;
|
|
108
108
|
italic: boolean;
|
|
109
109
|
}): string | undefined {
|
|
@@ -111,7 +111,7 @@ function resolveFontFamily({
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
export function resolveHeadingRecipe(
|
|
114
|
-
theme:
|
|
114
|
+
theme: SurfaceTheme,
|
|
115
115
|
{ align, italic = false, level, size, tone = 'default', weight }: ResolveHeadingRecipeOptions,
|
|
116
116
|
): TextStyle {
|
|
117
117
|
const recipe = resolveSizeRecipe(theme, size ?? resolveHeadingSizeFromLevel(level));
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
AnkhTheme,
|
|
3
2
|
Breakpoint,
|
|
4
3
|
FontWeight,
|
|
5
4
|
Responsive,
|
|
6
5
|
RoleSemantics,
|
|
6
|
+
SurfaceTheme,
|
|
7
7
|
} from '@ankhorage/surface';
|
|
8
8
|
import { describe, expect, mock, test } from 'bun:test';
|
|
9
9
|
|
|
@@ -64,7 +64,7 @@ function createRole(base: string): RoleSemantics {
|
|
|
64
64
|
};
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
function createTestTheme():
|
|
67
|
+
function createTestTheme(): SurfaceTheme {
|
|
68
68
|
return {
|
|
69
69
|
colors: {
|
|
70
70
|
primary: '#0f766e',
|
|
@@ -86,12 +86,10 @@ function createTestTheme(): AnkhTheme {
|
|
|
86
86
|
light: {
|
|
87
87
|
primaryColor: '#0f766e',
|
|
88
88
|
harmony: 'analogous',
|
|
89
|
-
colorTone: 'jewel',
|
|
90
89
|
},
|
|
91
90
|
dark: {
|
|
92
91
|
primaryColor: '#2dd4bf',
|
|
93
92
|
harmony: 'analogous',
|
|
94
|
-
colorTone: 'jewel',
|
|
95
93
|
},
|
|
96
94
|
},
|
|
97
95
|
radii: {
|
|
@@ -101,7 +99,34 @@ function createTestTheme(): AnkhTheme {
|
|
|
101
99
|
l: 16,
|
|
102
100
|
full: 9999,
|
|
103
101
|
},
|
|
104
|
-
|
|
102
|
+
swatches: {
|
|
103
|
+
primary: {
|
|
104
|
+
50: '#f0fdfa',
|
|
105
|
+
100: '#ccfbf1',
|
|
106
|
+
200: '#99f6e4',
|
|
107
|
+
300: '#5eead4',
|
|
108
|
+
400: '#2dd4bf',
|
|
109
|
+
500: '#14b8a6',
|
|
110
|
+
600: '#0d9488',
|
|
111
|
+
700: '#0f766e',
|
|
112
|
+
800: '#115e59',
|
|
113
|
+
900: '#134e4a',
|
|
114
|
+
950: '#042f2e',
|
|
115
|
+
},
|
|
116
|
+
neutral: {
|
|
117
|
+
50: '#f9fafb',
|
|
118
|
+
100: '#f3f4f6',
|
|
119
|
+
200: '#e5e7eb',
|
|
120
|
+
300: '#d1d5db',
|
|
121
|
+
400: '#9ca3af',
|
|
122
|
+
500: '#6b7280',
|
|
123
|
+
600: '#4b5563',
|
|
124
|
+
700: '#374151',
|
|
125
|
+
800: '#1f2937',
|
|
126
|
+
900: '#111827',
|
|
127
|
+
950: '#030712',
|
|
128
|
+
},
|
|
129
|
+
},
|
|
105
130
|
semantics: {
|
|
106
131
|
neutral: {
|
|
107
132
|
bg: '#ffffff',
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
-
type AnkhTheme,
|
|
3
2
|
type Breakpoint,
|
|
4
3
|
type FontWeight,
|
|
5
4
|
resolveResponsive,
|
|
6
5
|
type Responsive,
|
|
6
|
+
type SurfaceTheme,
|
|
7
7
|
} from '@ankhorage/surface';
|
|
8
8
|
import type { TextStyle } from 'react-native';
|
|
9
9
|
|
|
@@ -18,7 +18,7 @@ interface VariantRecipe {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
interface ResolveTextStyleOptions {
|
|
21
|
-
theme:
|
|
21
|
+
theme: SurfaceTheme;
|
|
22
22
|
breakpoint: Breakpoint;
|
|
23
23
|
variant?: Responsive<TextVariant>;
|
|
24
24
|
tone?: Responsive<TextTone>;
|
|
@@ -31,7 +31,7 @@ function isMediumBreakpointOrLarger(breakpoint: Breakpoint): boolean {
|
|
|
31
31
|
return breakpoint === 'md' || breakpoint === 'lg' || breakpoint === 'xl';
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
function resolveWeight(theme:
|
|
34
|
+
function resolveWeight(theme: SurfaceTheme, weight: TextWeight): FontWeight {
|
|
35
35
|
return theme.typography.weights[weight];
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -41,7 +41,7 @@ function resolveFontFamily({
|
|
|
41
41
|
weight,
|
|
42
42
|
italic,
|
|
43
43
|
}: {
|
|
44
|
-
theme:
|
|
44
|
+
theme: SurfaceTheme;
|
|
45
45
|
variant: TextVariant;
|
|
46
46
|
weight: FontWeight;
|
|
47
47
|
italic: boolean;
|
|
@@ -54,7 +54,7 @@ function resolveFontFamily({
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
function resolveVariantRecipe(
|
|
57
|
-
theme:
|
|
57
|
+
theme: SurfaceTheme,
|
|
58
58
|
variant: TextVariant,
|
|
59
59
|
breakpoint: Breakpoint,
|
|
60
60
|
): VariantRecipe {
|
|
@@ -109,7 +109,7 @@ function resolveVariantRecipe(
|
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
function resolveToneColor(theme:
|
|
112
|
+
function resolveToneColor(theme: SurfaceTheme, tone: TextTone): string {
|
|
113
113
|
switch (tone) {
|
|
114
114
|
case 'muted':
|
|
115
115
|
return theme.semantics.content.muted;
|
|
@@ -1,14 +1,8 @@
|
|
|
1
|
+
import { COLOR_HARMONIES } from '@ankhorage/color-theory';
|
|
1
2
|
import { describe, expect, test } from 'bun:test';
|
|
2
3
|
|
|
3
|
-
import { ZORA_COLOR_HARMONIES, ZORA_COLOR_TONES } from '../../theme/types';
|
|
4
4
|
import { zoraDefaultTheme } from '../../theme/zoraDefaultTheme';
|
|
5
|
-
import {
|
|
6
|
-
createThemeFromThemeComposerRecommendation,
|
|
7
|
-
findThemeComposerRecommendation,
|
|
8
|
-
formatThemeComposerLabel,
|
|
9
|
-
hueDegreesToZoraHexColor,
|
|
10
|
-
} from './recommendations';
|
|
11
|
-
import type { ThemeComposerProps, ThemeComposerRecommendation } from './types';
|
|
5
|
+
import type { ThemeComposerProps } from './types';
|
|
12
6
|
|
|
13
7
|
// Validate the exported types compile correctly by asserting shape
|
|
14
8
|
describe('ThemeComposerProps', () => {
|
|
@@ -17,9 +11,10 @@ describe('ThemeComposerProps', () => {
|
|
|
17
11
|
value: zoraDefaultTheme,
|
|
18
12
|
onChange: () => undefined,
|
|
19
13
|
};
|
|
20
|
-
expect(props.value.primaryColor).toBe(
|
|
14
|
+
expect(props.value.primaryColor).toBe(zoraDefaultTheme.primaryColor);
|
|
21
15
|
expect(props.value.harmony).toBe('analogous');
|
|
22
|
-
expect(props.value.
|
|
16
|
+
expect(props.value.appCategory).toBe('developer_tools');
|
|
17
|
+
expect(props.value.name).toBe('ZORA');
|
|
23
18
|
});
|
|
24
19
|
|
|
25
20
|
test('accepts optional mode and onModeChange', () => {
|
|
@@ -44,56 +39,19 @@ describe('ThemeComposerProps', () => {
|
|
|
44
39
|
props.onSubmit?.(zoraDefaultTheme);
|
|
45
40
|
expect(submitted).toBe(true);
|
|
46
41
|
});
|
|
47
|
-
|
|
48
|
-
test('accepts optional recommendation inputs', () => {
|
|
49
|
-
const recommendation: ThemeComposerRecommendation = {
|
|
50
|
-
appCategory: 'finance_money',
|
|
51
|
-
appMood: 'trustworthy',
|
|
52
|
-
suggestedColorTone: 'mineral',
|
|
53
|
-
suggestedHarmony: 'analogous',
|
|
54
|
-
suggestedPrimaryHueDegrees: 195,
|
|
55
|
-
};
|
|
56
|
-
const props: ThemeComposerProps = {
|
|
57
|
-
value: zoraDefaultTheme,
|
|
58
|
-
onChange: () => undefined,
|
|
59
|
-
appCategory: 'finance_money',
|
|
60
|
-
appMood: 'trustworthy',
|
|
61
|
-
recommendations: [recommendation],
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
expect(props.recommendations?.[0]?.suggestedColorTone).toBe('mineral');
|
|
65
|
-
});
|
|
66
42
|
});
|
|
67
43
|
|
|
68
|
-
describe('
|
|
69
|
-
test('all harmony values are present for the Select options', () => {
|
|
44
|
+
describe('COLOR_HARMONIES coverage for ThemeComposer', () => {
|
|
45
|
+
test('all canonical harmony values are present for the Select options', () => {
|
|
70
46
|
const expected = [
|
|
71
47
|
'monochromatic',
|
|
72
48
|
'analogous',
|
|
73
49
|
'complementary',
|
|
74
|
-
'splitComplementary',
|
|
75
50
|
'triadic',
|
|
76
51
|
'tetradic',
|
|
52
|
+
'splitComplementary',
|
|
77
53
|
] as const;
|
|
78
|
-
expect(
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
describe('ZORA_COLOR_TONES coverage for ThemeComposer', () => {
|
|
83
|
-
test('all color tones are present for the Select options', () => {
|
|
84
|
-
const expected = [
|
|
85
|
-
'neutral',
|
|
86
|
-
'pastel',
|
|
87
|
-
'earth',
|
|
88
|
-
'mineral',
|
|
89
|
-
'muted',
|
|
90
|
-
'jewel',
|
|
91
|
-
'fluorescent',
|
|
92
|
-
'obsidian',
|
|
93
|
-
'vaporwave',
|
|
94
|
-
'monochromeAccent',
|
|
95
|
-
] as const;
|
|
96
|
-
expect(ZORA_COLOR_TONES).toEqual(expected);
|
|
54
|
+
expect(COLOR_HARMONIES).toEqual(expected);
|
|
97
55
|
});
|
|
98
56
|
});
|
|
99
57
|
|
|
@@ -109,94 +67,4 @@ describe('onChange propagation contract', () => {
|
|
|
109
67
|
expect(received).toHaveLength(1);
|
|
110
68
|
expect(received[0]?.harmony).toBe('triadic');
|
|
111
69
|
});
|
|
112
|
-
|
|
113
|
-
test('onChange receives updated theme with new colorTone', () => {
|
|
114
|
-
const received: (typeof zoraDefaultTheme)[] = [];
|
|
115
|
-
const props: ThemeComposerProps = {
|
|
116
|
-
value: zoraDefaultTheme,
|
|
117
|
-
onChange: (t) => received.push(t),
|
|
118
|
-
};
|
|
119
|
-
props.onChange({ ...zoraDefaultTheme, colorTone: 'obsidian' });
|
|
120
|
-
expect(received[0]?.colorTone).toBe('obsidian');
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
describe('ThemeComposer recommendation helpers', () => {
|
|
125
|
-
const recommendations: readonly ThemeComposerRecommendation[] = [
|
|
126
|
-
{
|
|
127
|
-
appCategory: 'finance_money',
|
|
128
|
-
appMood: 'trustworthy',
|
|
129
|
-
suggestedColorTone: 'mineral',
|
|
130
|
-
suggestedHarmony: 'analogous',
|
|
131
|
-
suggestedPrimaryHueDegrees: 195,
|
|
132
|
-
},
|
|
133
|
-
{
|
|
134
|
-
appCategory: 'graphics_design',
|
|
135
|
-
appMood: 'creative',
|
|
136
|
-
suggestedColorTone: 'vaporwave',
|
|
137
|
-
suggestedHarmony: 'splitComplementary',
|
|
138
|
-
},
|
|
139
|
-
];
|
|
140
|
-
|
|
141
|
-
test('finds a recommendation by app category and mood', () => {
|
|
142
|
-
const found = findThemeComposerRecommendation({
|
|
143
|
-
appCategory: 'finance_money',
|
|
144
|
-
appMood: 'trustworthy',
|
|
145
|
-
recommendations,
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
expect(found?.suggestedColorTone).toBe('mineral');
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
test('does not find a recommendation with mismatched mood', () => {
|
|
152
|
-
const found = findThemeComposerRecommendation({
|
|
153
|
-
appCategory: 'finance_money',
|
|
154
|
-
appMood: 'creative',
|
|
155
|
-
recommendations,
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
expect(found).toBeUndefined();
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
test('creates an updated theme only when recommendation is explicitly applied', () => {
|
|
162
|
-
const [recommendation] = recommendations;
|
|
163
|
-
if (recommendation === undefined) {
|
|
164
|
-
throw new Error('Expected fixture recommendation.');
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const updated = createThemeFromThemeComposerRecommendation({
|
|
168
|
-
value: zoraDefaultTheme,
|
|
169
|
-
recommendation,
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
expect(zoraDefaultTheme.colorTone).toBe('jewel');
|
|
173
|
-
expect(updated.colorTone).toBe('mineral');
|
|
174
|
-
expect(updated.harmony).toBe('analogous');
|
|
175
|
-
expect(updated.primaryColor).not.toBe(zoraDefaultTheme.primaryColor);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
test('keeps the current primary color when recommendation has no hue', () => {
|
|
179
|
-
const [, recommendation] = recommendations;
|
|
180
|
-
if (recommendation === undefined) {
|
|
181
|
-
throw new Error('Expected fixture recommendation.');
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const updated = createThemeFromThemeComposerRecommendation({
|
|
185
|
-
value: zoraDefaultTheme,
|
|
186
|
-
recommendation,
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
expect(updated.primaryColor).toBe(zoraDefaultTheme.primaryColor);
|
|
190
|
-
expect(updated.colorTone).toBe('vaporwave');
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
test('formats serialized labels for display', () => {
|
|
194
|
-
expect(formatThemeComposerLabel('finance_money')).toBe('Finance money');
|
|
195
|
-
expect(formatThemeComposerLabel('splitComplementary')).toBe('Split Complementary');
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
test('converts hue degrees to lowercase hex', () => {
|
|
199
|
-
expect(hueDegreesToZoraHexColor(195)).toMatch(/^#[0-9a-f]{6}$/);
|
|
200
|
-
expect(hueDegreesToZoraHexColor(555)).toBe(hueDegreesToZoraHexColor(195));
|
|
201
|
-
});
|
|
202
70
|
});
|