@ankhorage/zora 3.3.4 → 4.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 +6 -0
- package/dist/components/heading/resolveHeadingThemeRecipe.d.ts +1 -1
- package/dist/components/text/resolveTextThemeRecipe.d.ts +1 -1
- package/dist/theme/compileZoraTheme.d.ts +8 -0
- package/dist/theme/compileZoraTheme.d.ts.map +1 -0
- package/dist/theme/compileZoraTheme.js +128 -0
- package/dist/theme/compileZoraTheme.js.map +1 -0
- package/dist/theme/createZoraThemeConfig.d.ts +1 -0
- package/dist/theme/createZoraThemeConfig.d.ts.map +1 -1
- package/dist/theme/createZoraThemeConfig.js +1 -0
- package/dist/theme/createZoraThemeConfig.js.map +1 -1
- package/dist/theme/index.d.ts +1 -3
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +1 -2
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/public.d.ts +5 -0
- package/dist/theme/public.d.ts.map +1 -0
- package/dist/theme/public.js +4 -0
- package/dist/theme/public.js.map +1 -0
- package/dist/theme/types.d.ts +44 -13
- package/dist/theme/types.d.ts.map +1 -1
- package/dist/theme/types.js.map +1 -1
- package/package.json +10 -2
- package/src/theme/compileZoraTheme.test.ts +162 -0
- package/src/theme/compileZoraTheme.ts +177 -0
- package/src/theme/createZoraThemeConfig.ts +1 -0
- package/src/theme/index.ts +1 -9
- package/src/theme/public.ts +18 -0
- package/src/theme/types.ts +56 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 92c375f: Add a pure `@ankhorage/zora/theme` compiler that returns canonical Color Theory evidence, resolved Surface semantics, measurable selection results, diagnostics, and provenance for both theme modes. Replace the previously unconstructed computed-theme type shape with the compiler's real result.
|
|
8
|
+
|
|
3
9
|
## 3.3.4
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
|
@@ -2,7 +2,7 @@ import type { ThemeRecipeOverrideValue } from '@ankhorage/contracts';
|
|
|
2
2
|
import type { HeadingAlign, HeadingSize, HeadingWeight } from './types';
|
|
3
3
|
export declare function resolveHeadingThemeRecipe(fields: Readonly<Record<string, ThemeRecipeOverrideValue>>): {
|
|
4
4
|
size: HeadingSize | undefined;
|
|
5
|
-
color: "primary" | "secondary" | "tertiary" | "quaternary" | "neutral" | "
|
|
5
|
+
color: "primary" | "secondary" | "tertiary" | "quaternary" | "neutral" | "error" | "success" | "warning" | "info" | "danger" | undefined;
|
|
6
6
|
emphasis: "default" | "muted" | "subtle" | "inverse" | undefined;
|
|
7
7
|
align: HeadingAlign | undefined;
|
|
8
8
|
weight: HeadingWeight | undefined;
|
|
@@ -2,7 +2,7 @@ import type { ThemeRecipeOverrideValue } from '@ankhorage/contracts';
|
|
|
2
2
|
import type { TextAlign, TextVariant, TextWeight } from './types';
|
|
3
3
|
export declare function resolveTextThemeRecipe(fields: Readonly<Record<string, ThemeRecipeOverrideValue>>): {
|
|
4
4
|
variant: TextVariant | undefined;
|
|
5
|
-
color: "primary" | "secondary" | "tertiary" | "quaternary" | "neutral" | "
|
|
5
|
+
color: "primary" | "secondary" | "tertiary" | "quaternary" | "neutral" | "error" | "success" | "warning" | "info" | "danger" | undefined;
|
|
6
6
|
emphasis: "default" | "muted" | "subtle" | "inverse" | undefined;
|
|
7
7
|
align: TextAlign | undefined;
|
|
8
8
|
weight: TextWeight | undefined;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ThemeConfig } from '@ankhorage/contracts';
|
|
2
|
+
import type { ZoraComputedTheme, ZoraThemeCompilationOptions } from './types';
|
|
3
|
+
/***
|
|
4
|
+
* Compile canonical theme source into Color Theory evidence and resolved Surface themes.
|
|
5
|
+
* This function is pure and does not mount React or reproduce owner algorithms.
|
|
6
|
+
*/
|
|
7
|
+
export declare function compileZoraTheme(themeConfig: ThemeConfig, options?: ZoraThemeCompilationOptions): ZoraComputedTheme;
|
|
8
|
+
//# sourceMappingURL=compileZoraTheme.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compileZoraTheme.d.ts","sourceRoot":"","sources":["../../src/theme/compileZoraTheme.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGxD,OAAO,KAAK,EACV,iBAAiB,EAGjB,2BAA2B,EAK5B,MAAM,SAAS,CAAC;AAOjB;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,WAAW,EACxB,OAAO,GAAE,2BAAgC,GACxC,iBAAiB,CAYnB"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { generateThemeModeColors, selectColorSwatchStep } from '@ankhorage/color-theory';
|
|
2
|
+
import { createTheme } from '@ankhorage/surface/theme';
|
|
3
|
+
const CONTRACTS_OWNER = '@ankhorage/contracts';
|
|
4
|
+
const COLOR_THEORY_OWNER = '@ankhorage/color-theory';
|
|
5
|
+
const SURFACE_OWNER = '@ankhorage/surface';
|
|
6
|
+
const ZORA_OWNER = '@ankhorage/zora';
|
|
7
|
+
/***
|
|
8
|
+
* Compile canonical theme source into Color Theory evidence and resolved Surface themes.
|
|
9
|
+
* This function is pure and does not mount React or reproduce owner algorithms.
|
|
10
|
+
*/
|
|
11
|
+
export function compileZoraTheme(themeConfig, options = {}) {
|
|
12
|
+
const light = compileMode(themeConfig, 'light', options.selectionTargets?.light ?? []);
|
|
13
|
+
const dark = compileMode(themeConfig, 'dark', options.selectionTargets?.dark ?? []);
|
|
14
|
+
const diagnostics = [...light.diagnostics, ...dark.diagnostics];
|
|
15
|
+
return {
|
|
16
|
+
themeConfig,
|
|
17
|
+
light,
|
|
18
|
+
dark,
|
|
19
|
+
diagnostics,
|
|
20
|
+
provenance: createRootProvenance(themeConfig),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/*** Compile one canonical mode and compare the independent owner results. */
|
|
24
|
+
function compileMode(themeConfig, mode, targets) {
|
|
25
|
+
const modeConfig = mode === 'light' ? themeConfig.light : themeConfig.dark;
|
|
26
|
+
const generated = generateThemeModeColors(modeConfig);
|
|
27
|
+
const surfaceTheme = createTheme(themeConfig, mode);
|
|
28
|
+
const { diagnostics, selections } = resolveSelections(generated, mode, targets);
|
|
29
|
+
if (!outputsAgree(generated, surfaceTheme.colorDiagnostics.generated)) {
|
|
30
|
+
diagnostics.push({
|
|
31
|
+
code: 'generated-output-divergence',
|
|
32
|
+
message: 'Surface and Color Theory returned different generated color evidence.',
|
|
33
|
+
mode,
|
|
34
|
+
path: `${mode}.generated`,
|
|
35
|
+
severity: 'error',
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
mode,
|
|
40
|
+
generated,
|
|
41
|
+
surfaceTheme,
|
|
42
|
+
selections,
|
|
43
|
+
diagnostics,
|
|
44
|
+
provenance: createModeProvenance(mode, targets),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/*** Resolve measurable swatch targets without substituting unavailable roles. */
|
|
48
|
+
function resolveSelections(generated, mode, targets) {
|
|
49
|
+
const diagnostics = [];
|
|
50
|
+
const selections = targets.map((request) => {
|
|
51
|
+
const swatch = generated.swatches[request.swatch];
|
|
52
|
+
const result = swatch
|
|
53
|
+
? selectColorSwatchStep(swatch, request.target, request.contexts, request.tiePolicy)
|
|
54
|
+
: null;
|
|
55
|
+
if (!swatch)
|
|
56
|
+
diagnostics.push(createMissingSwatchDiagnostic(mode, request));
|
|
57
|
+
else if (!result?.selected)
|
|
58
|
+
diagnostics.push(createUnresolvedTargetDiagnostic(mode, request));
|
|
59
|
+
return { request, result };
|
|
60
|
+
});
|
|
61
|
+
return { diagnostics, selections };
|
|
62
|
+
}
|
|
63
|
+
/*** Describe a requested swatch that the selected harmony does not generate. */
|
|
64
|
+
function createMissingSwatchDiagnostic(mode, request) {
|
|
65
|
+
return {
|
|
66
|
+
code: 'missing-target-swatch',
|
|
67
|
+
message: `The selected harmony does not generate the '${request.swatch}' swatch.`,
|
|
68
|
+
mode,
|
|
69
|
+
path: `${mode}.selectionTargets.${request.id}`,
|
|
70
|
+
severity: 'error',
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/*** Describe a measurable target that has no passing swatch candidate. */
|
|
74
|
+
function createUnresolvedTargetDiagnostic(mode, request) {
|
|
75
|
+
return {
|
|
76
|
+
code: 'unresolved-selection-target',
|
|
77
|
+
message: `No '${request.swatch}' step satisfies selection target '${request.id}'.`,
|
|
78
|
+
mode,
|
|
79
|
+
path: `${mode}.selectionTargets.${request.id}`,
|
|
80
|
+
severity: 'error',
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/*** Record direct source and Surface-owned default provenance. */
|
|
84
|
+
function createRootProvenance(themeConfig) {
|
|
85
|
+
return [
|
|
86
|
+
{ path: 'themeConfig', origin: 'direct', owner: CONTRACTS_OWNER },
|
|
87
|
+
{
|
|
88
|
+
path: 'themeConfig.tokens',
|
|
89
|
+
origin: themeConfig.tokens ? 'direct' : 'defaulted',
|
|
90
|
+
owner: themeConfig.tokens ? CONTRACTS_OWNER : SURFACE_OWNER,
|
|
91
|
+
},
|
|
92
|
+
];
|
|
93
|
+
}
|
|
94
|
+
/*** Record the owner transformation chain for one compiled mode. */
|
|
95
|
+
function createModeProvenance(mode, targets) {
|
|
96
|
+
const inputPath = `themeConfig.${mode}`;
|
|
97
|
+
const entries = [
|
|
98
|
+
{ path: inputPath, origin: 'direct', owner: CONTRACTS_OWNER },
|
|
99
|
+
{
|
|
100
|
+
path: `${mode}.generated`,
|
|
101
|
+
origin: 'derived',
|
|
102
|
+
owner: COLOR_THEORY_OWNER,
|
|
103
|
+
inputs: [inputPath],
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
path: `${mode}.surfaceTheme`,
|
|
107
|
+
origin: 'derived',
|
|
108
|
+
owner: SURFACE_OWNER,
|
|
109
|
+
inputs: ['themeConfig', inputPath],
|
|
110
|
+
},
|
|
111
|
+
];
|
|
112
|
+
for (const target of targets) {
|
|
113
|
+
const targetPath = `${mode}.selectionTargets.${target.id}`;
|
|
114
|
+
entries.push({ path: targetPath, origin: 'direct', owner: ZORA_OWNER });
|
|
115
|
+
entries.push({
|
|
116
|
+
path: `${mode}.selections.${target.id}`,
|
|
117
|
+
origin: 'derived',
|
|
118
|
+
owner: COLOR_THEORY_OWNER,
|
|
119
|
+
inputs: [`${mode}.generated.swatches.${target.swatch}`, targetPath],
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
return entries;
|
|
123
|
+
}
|
|
124
|
+
/*** Compare deterministic owner result records for divergence diagnostics. */
|
|
125
|
+
function outputsAgree(left, right) {
|
|
126
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=compileZoraTheme.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compileZoraTheme.js","sourceRoot":"","sources":["../../src/theme/compileZoraTheme.ts"],"names":[],"mappings":"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","sourcesContent":["import { generateThemeModeColors, selectColorSwatchStep } from '@ankhorage/color-theory';\nimport type { ThemeConfig } from '@ankhorage/contracts';\nimport { createTheme } from '@ankhorage/surface/theme';\n\nimport type {\n ZoraComputedTheme,\n ZoraComputedThemeMode,\n ZoraThemeCompilationDiagnostic,\n ZoraThemeCompilationOptions,\n ZoraThemeMode,\n ZoraThemeProvenanceEntry,\n ZoraThemeSelectionResult,\n ZoraThemeSelectionTarget,\n} from './types';\n\nconst CONTRACTS_OWNER = '@ankhorage/contracts';\nconst COLOR_THEORY_OWNER = '@ankhorage/color-theory';\nconst SURFACE_OWNER = '@ankhorage/surface';\nconst ZORA_OWNER = '@ankhorage/zora';\n\n/***\n * Compile canonical theme source into Color Theory evidence and resolved Surface themes.\n * This function is pure and does not mount React or reproduce owner algorithms.\n */\nexport function compileZoraTheme(\n themeConfig: ThemeConfig,\n options: ZoraThemeCompilationOptions = {},\n): ZoraComputedTheme {\n const light = compileMode(themeConfig, 'light', options.selectionTargets?.light ?? []);\n const dark = compileMode(themeConfig, 'dark', options.selectionTargets?.dark ?? []);\n const diagnostics = [...light.diagnostics, ...dark.diagnostics];\n\n return {\n themeConfig,\n light,\n dark,\n diagnostics,\n provenance: createRootProvenance(themeConfig),\n };\n}\n\n/*** Compile one canonical mode and compare the independent owner results. */\nfunction compileMode(\n themeConfig: ThemeConfig,\n mode: ZoraThemeMode,\n targets: readonly ZoraThemeSelectionTarget[],\n): ZoraComputedThemeMode {\n const modeConfig = mode === 'light' ? themeConfig.light : themeConfig.dark;\n const generated = generateThemeModeColors(modeConfig);\n const surfaceTheme = createTheme(themeConfig, mode);\n const { diagnostics, selections } = resolveSelections(generated, mode, targets);\n\n if (!outputsAgree(generated, surfaceTheme.colorDiagnostics.generated)) {\n diagnostics.push({\n code: 'generated-output-divergence',\n message: 'Surface and Color Theory returned different generated color evidence.',\n mode,\n path: `${mode}.generated`,\n severity: 'error',\n });\n }\n\n return {\n mode,\n generated,\n surfaceTheme,\n selections,\n diagnostics,\n provenance: createModeProvenance(mode, targets),\n };\n}\n\n/*** Resolve measurable swatch targets without substituting unavailable roles. */\nfunction resolveSelections(\n generated: ZoraComputedThemeMode['generated'],\n mode: ZoraThemeMode,\n targets: readonly ZoraThemeSelectionTarget[],\n): {\n diagnostics: ZoraThemeCompilationDiagnostic[];\n selections: ZoraThemeSelectionResult[];\n} {\n const diagnostics: ZoraThemeCompilationDiagnostic[] = [];\n const selections = targets.map((request) => {\n const swatch = generated.swatches[request.swatch];\n const result = swatch\n ? selectColorSwatchStep(swatch, request.target, request.contexts, request.tiePolicy)\n : null;\n\n if (!swatch) diagnostics.push(createMissingSwatchDiagnostic(mode, request));\n else if (!result?.selected) diagnostics.push(createUnresolvedTargetDiagnostic(mode, request));\n\n return { request, result };\n });\n\n return { diagnostics, selections };\n}\n\n/*** Describe a requested swatch that the selected harmony does not generate. */\nfunction createMissingSwatchDiagnostic(\n mode: ZoraThemeMode,\n request: ZoraThemeSelectionTarget,\n): ZoraThemeCompilationDiagnostic {\n return {\n code: 'missing-target-swatch',\n message: `The selected harmony does not generate the '${request.swatch}' swatch.`,\n mode,\n path: `${mode}.selectionTargets.${request.id}`,\n severity: 'error',\n };\n}\n\n/*** Describe a measurable target that has no passing swatch candidate. */\nfunction createUnresolvedTargetDiagnostic(\n mode: ZoraThemeMode,\n request: ZoraThemeSelectionTarget,\n): ZoraThemeCompilationDiagnostic {\n return {\n code: 'unresolved-selection-target',\n message: `No '${request.swatch}' step satisfies selection target '${request.id}'.`,\n mode,\n path: `${mode}.selectionTargets.${request.id}`,\n severity: 'error',\n };\n}\n\n/*** Record direct source and Surface-owned default provenance. */\nfunction createRootProvenance(themeConfig: ThemeConfig): ZoraThemeProvenanceEntry[] {\n return [\n { path: 'themeConfig', origin: 'direct', owner: CONTRACTS_OWNER },\n {\n path: 'themeConfig.tokens',\n origin: themeConfig.tokens ? 'direct' : 'defaulted',\n owner: themeConfig.tokens ? CONTRACTS_OWNER : SURFACE_OWNER,\n },\n ];\n}\n\n/*** Record the owner transformation chain for one compiled mode. */\nfunction createModeProvenance(\n mode: ZoraThemeMode,\n targets: readonly ZoraThemeSelectionTarget[],\n): ZoraThemeProvenanceEntry[] {\n const inputPath = `themeConfig.${mode}`;\n const entries: ZoraThemeProvenanceEntry[] = [\n { path: inputPath, origin: 'direct', owner: CONTRACTS_OWNER },\n {\n path: `${mode}.generated`,\n origin: 'derived',\n owner: COLOR_THEORY_OWNER,\n inputs: [inputPath],\n },\n {\n path: `${mode}.surfaceTheme`,\n origin: 'derived',\n owner: SURFACE_OWNER,\n inputs: ['themeConfig', inputPath],\n },\n ];\n\n for (const target of targets) {\n const targetPath = `${mode}.selectionTargets.${target.id}`;\n entries.push({ path: targetPath, origin: 'direct', owner: ZORA_OWNER });\n entries.push({\n path: `${mode}.selections.${target.id}`,\n origin: 'derived',\n owner: COLOR_THEORY_OWNER,\n inputs: [`${mode}.generated.swatches.${target.swatch}`, targetPath],\n });\n }\n\n return entries;\n}\n\n/*** Compare deterministic owner result records for divergence diagnostics. */\nfunction outputsAgree(left: unknown, right: unknown): boolean {\n return JSON.stringify(left) === JSON.stringify(right);\n}\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ThemeConfig } from '@ankhorage/contracts';
|
|
2
2
|
import type { ZoraTheme } from './types';
|
|
3
|
+
/*** Convert the concise ZORA design seed into canonical persisted theme source. */
|
|
3
4
|
export declare function createZoraThemeConfig(theme?: ZoraTheme): ThemeConfig;
|
|
4
5
|
//# sourceMappingURL=createZoraThemeConfig.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createZoraThemeConfig.d.ts","sourceRoot":"","sources":["../../src/theme/createZoraThemeConfig.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAGzC,wBAAgB,qBAAqB,CAAC,KAAK,GAAE,SAA4B,GAAG,WAAW,CAetF"}
|
|
1
|
+
{"version":3,"file":"createZoraThemeConfig.d.ts","sourceRoot":"","sources":["../../src/theme/createZoraThemeConfig.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAGzC,mFAAmF;AACnF,wBAAgB,qBAAqB,CAAC,KAAK,GAAE,SAA4B,GAAG,WAAW,CAetF"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { parseHexColorOrThrow } from '@ankhorage/color-theory';
|
|
2
2
|
import { zoraDefaultTheme } from './zoraDefaultTheme';
|
|
3
|
+
/*** Convert the concise ZORA design seed into canonical persisted theme source. */
|
|
3
4
|
export function createZoraThemeConfig(theme = zoraDefaultTheme) {
|
|
4
5
|
const primaryColor = parseHexColorOrThrow(theme.primaryColor);
|
|
5
6
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createZoraThemeConfig.js","sourceRoot":"","sources":["../../src/theme/createZoraThemeConfig.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAI/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,MAAM,UAAU,qBAAqB,CAAC,QAAmB,gBAAgB;IACvE,MAAM,YAAY,GAAG,oBAAoB,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAE9D,OAAO;QACL,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,KAAK,EAAE;YACL,YAAY;YACZ,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB;QACD,IAAI,EAAE;YACJ,YAAY;YACZ,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB;KACF,CAAC;AACJ,CAAC","sourcesContent":["import { parseHexColorOrThrow } from '@ankhorage/color-theory';\nimport type { ThemeConfig } from '@ankhorage/contracts';\n\nimport type { ZoraTheme } from './types';\nimport { zoraDefaultTheme } from './zoraDefaultTheme';\n\nexport function createZoraThemeConfig(theme: ZoraTheme = zoraDefaultTheme): ThemeConfig {\n const primaryColor = parseHexColorOrThrow(theme.primaryColor);\n\n return {\n id: theme.id,\n name: theme.name,\n light: {\n primaryColor,\n harmony: theme.harmony,\n },\n dark: {\n primaryColor,\n harmony: theme.harmony,\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"createZoraThemeConfig.js","sourceRoot":"","sources":["../../src/theme/createZoraThemeConfig.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAI/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,mFAAmF;AACnF,MAAM,UAAU,qBAAqB,CAAC,QAAmB,gBAAgB;IACvE,MAAM,YAAY,GAAG,oBAAoB,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAE9D,OAAO;QACL,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,KAAK,EAAE;YACL,YAAY;YACZ,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB;QACD,IAAI,EAAE;YACJ,YAAY;YACZ,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB;KACF,CAAC;AACJ,CAAC","sourcesContent":["import { parseHexColorOrThrow } from '@ankhorage/color-theory';\nimport type { ThemeConfig } from '@ankhorage/contracts';\n\nimport type { ZoraTheme } from './types';\nimport { zoraDefaultTheme } from './zoraDefaultTheme';\n\n/*** Convert the concise ZORA design seed into canonical persisted theme source. */\nexport function createZoraThemeConfig(theme: ZoraTheme = zoraDefaultTheme): ThemeConfig {\n const primaryColor = parseHexColorOrThrow(theme.primaryColor);\n\n return {\n id: theme.id,\n name: theme.name,\n light: {\n primaryColor,\n harmony: theme.harmony,\n },\n dark: {\n primaryColor,\n harmony: theme.harmony,\n },\n };\n}\n"]}
|
package/dist/theme/index.d.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * from './public';
|
|
2
2
|
export { ThemeModeToggle } from './ThemeModeToggle';
|
|
3
3
|
export type { ThemeModeToggleProps } from './ThemeModeToggleProps';
|
|
4
|
-
export type { ZoraComputedTheme, ZoraComputedThemeMode, ZoraTheme, ZoraThemeId, ZoraThemeMode, } from './types';
|
|
5
4
|
export { useZoraTheme } from './useZoraTheme';
|
|
6
5
|
export { withZoraThemeScope } from './withZoraThemeScope';
|
|
7
6
|
export type { ZoraBaseProps } from './ZoraBaseProps';
|
|
8
|
-
export { zoraDefaultTheme } from './zoraDefaultTheme';
|
|
9
7
|
export type { ZoraProviderProps } from './ZoraProvider';
|
|
10
8
|
export { ZoraProvider } from './ZoraProvider';
|
|
11
9
|
export type { ZoraThemeScopeProps } from './ZoraThemeScope';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/theme/index.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/theme/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/theme/index.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * from './public';
|
|
2
2
|
export { ThemeModeToggle } from './ThemeModeToggle';
|
|
3
3
|
export { useZoraTheme } from './useZoraTheme';
|
|
4
4
|
export { withZoraThemeScope } from './withZoraThemeScope';
|
|
5
|
-
export { zoraDefaultTheme } from './zoraDefaultTheme';
|
|
6
5
|
export { ZoraProvider } from './ZoraProvider';
|
|
7
6
|
export { ZoraThemeScope } from './ZoraThemeScope';
|
|
8
7
|
//# sourceMappingURL=index.js.map
|
package/dist/theme/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/theme/index.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/theme/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAG1D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC","sourcesContent":["export * from './public';\nexport { ThemeModeToggle } from './ThemeModeToggle';\nexport type { ThemeModeToggleProps } from './ThemeModeToggleProps';\nexport { useZoraTheme } from './useZoraTheme';\nexport { withZoraThemeScope } from './withZoraThemeScope';\nexport type { ZoraBaseProps } from './ZoraBaseProps';\nexport type { ZoraProviderProps } from './ZoraProvider';\nexport { ZoraProvider } from './ZoraProvider';\nexport type { ZoraThemeScopeProps } from './ZoraThemeScope';\nexport { ZoraThemeScope } from './ZoraThemeScope';\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { compileZoraTheme } from './compileZoraTheme';
|
|
2
|
+
export { createZoraThemeConfig } from './createZoraThemeConfig';
|
|
3
|
+
export type { ZoraComputedTheme, ZoraComputedThemeMode, ZoraTheme, ZoraThemeCompilationDiagnostic, ZoraThemeCompilationDiagnosticCode, ZoraThemeCompilationOptions, ZoraThemeId, ZoraThemeMode, ZoraThemeProvenanceEntry, ZoraThemeSelectionResult, ZoraThemeSelectionTarget, ZoraThemeSwatchRole, ZoraThemeValueOrigin, } from './types';
|
|
4
|
+
export { zoraDefaultTheme } from './zoraDefaultTheme';
|
|
5
|
+
//# sourceMappingURL=public.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/theme/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,YAAY,EACV,iBAAiB,EACjB,qBAAqB,EACrB,SAAS,EACT,8BAA8B,EAC9B,kCAAkC,EAClC,2BAA2B,EAC3B,WAAW,EACX,aAAa,EACb,wBAAwB,EACxB,wBAAwB,EACxB,wBAAwB,EACxB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public.js","sourceRoot":"","sources":["../../src/theme/public.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAgBhE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC","sourcesContent":["export { compileZoraTheme } from './compileZoraTheme';\nexport { createZoraThemeConfig } from './createZoraThemeConfig';\nexport type {\n ZoraComputedTheme,\n ZoraComputedThemeMode,\n ZoraTheme,\n ZoraThemeCompilationDiagnostic,\n ZoraThemeCompilationDiagnosticCode,\n ZoraThemeCompilationOptions,\n ZoraThemeId,\n ZoraThemeMode,\n ZoraThemeProvenanceEntry,\n ZoraThemeSelectionResult,\n ZoraThemeSelectionTarget,\n ZoraThemeSwatchRole,\n ZoraThemeValueOrigin,\n} from './types';\nexport { zoraDefaultTheme } from './zoraDefaultTheme';\n"]}
|
package/dist/theme/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { ColorHarmony, GeneratedThemeModeColors, GeneratedThemeSwatches
|
|
1
|
+
import type { ColorContrastContext, ColorHarmony, ColorSelectionTarget, ColorSwatchSelectionResult, ColorSwatchTiePolicy, GeneratedThemeModeColors, GeneratedThemeSwatches } from '@ankhorage/color-theory';
|
|
2
2
|
import type { AppCategory, ThemeConfig } from '@ankhorage/contracts';
|
|
3
|
-
import type { SurfaceTheme } from '@ankhorage/surface';
|
|
3
|
+
import type { SurfaceTheme } from '@ankhorage/surface/theme';
|
|
4
4
|
export type ZoraThemeId = string;
|
|
5
5
|
export type ZoraThemeMode = 'light' | 'dark';
|
|
6
6
|
export interface ZoraTheme {
|
|
@@ -10,19 +10,50 @@ export interface ZoraTheme {
|
|
|
10
10
|
primaryColor: string;
|
|
11
11
|
harmony: ColorHarmony;
|
|
12
12
|
}
|
|
13
|
+
export type ZoraThemeSwatchRole = keyof GeneratedThemeSwatches;
|
|
14
|
+
/** A measurable request delegated to Color Theory's canonical swatch selector. */
|
|
15
|
+
export interface ZoraThemeSelectionTarget {
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly swatch: ZoraThemeSwatchRole;
|
|
18
|
+
readonly target: ColorSelectionTarget;
|
|
19
|
+
readonly contexts: readonly ColorContrastContext[];
|
|
20
|
+
readonly tiePolicy: ColorSwatchTiePolicy;
|
|
21
|
+
}
|
|
22
|
+
export interface ZoraThemeCompilationOptions {
|
|
23
|
+
readonly selectionTargets?: Partial<Record<ZoraThemeMode, readonly ZoraThemeSelectionTarget[]>>;
|
|
24
|
+
}
|
|
25
|
+
export interface ZoraThemeSelectionResult {
|
|
26
|
+
readonly request: ZoraThemeSelectionTarget;
|
|
27
|
+
readonly result: ColorSwatchSelectionResult | null;
|
|
28
|
+
}
|
|
29
|
+
export type ZoraThemeCompilationDiagnosticCode = 'generated-output-divergence' | 'missing-target-swatch' | 'unresolved-selection-target';
|
|
30
|
+
export interface ZoraThemeCompilationDiagnostic {
|
|
31
|
+
readonly code: ZoraThemeCompilationDiagnosticCode;
|
|
32
|
+
readonly message: string;
|
|
33
|
+
readonly mode: ZoraThemeMode;
|
|
34
|
+
readonly path: string;
|
|
35
|
+
readonly severity: 'error';
|
|
36
|
+
}
|
|
37
|
+
export type ZoraThemeValueOrigin = 'defaulted' | 'derived' | 'direct';
|
|
38
|
+
export interface ZoraThemeProvenanceEntry {
|
|
39
|
+
readonly path: string;
|
|
40
|
+
readonly origin: ZoraThemeValueOrigin;
|
|
41
|
+
readonly owner: string;
|
|
42
|
+
readonly inputs?: readonly string[];
|
|
43
|
+
}
|
|
13
44
|
export interface ZoraComputedThemeMode {
|
|
14
|
-
mode: ZoraThemeMode;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
45
|
+
readonly mode: ZoraThemeMode;
|
|
46
|
+
readonly generated: GeneratedThemeModeColors;
|
|
47
|
+
readonly surfaceTheme: SurfaceTheme;
|
|
48
|
+
readonly selections: readonly ZoraThemeSelectionResult[];
|
|
49
|
+
readonly diagnostics: readonly ZoraThemeCompilationDiagnostic[];
|
|
50
|
+
readonly provenance: readonly ZoraThemeProvenanceEntry[];
|
|
19
51
|
}
|
|
20
52
|
export interface ZoraComputedTheme {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
dark: ZoraComputedThemeMode;
|
|
53
|
+
readonly themeConfig: ThemeConfig;
|
|
54
|
+
readonly light: ZoraComputedThemeMode;
|
|
55
|
+
readonly dark: ZoraComputedThemeMode;
|
|
56
|
+
readonly diagnostics: readonly ZoraThemeCompilationDiagnostic[];
|
|
57
|
+
readonly provenance: readonly ZoraThemeProvenanceEntry[];
|
|
27
58
|
}
|
|
28
59
|
//# 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,EACV,YAAY,EACZ,wBAAwB,EACxB,sBAAsB,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/theme/types.ts"],"names":[],"mappings":"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"}
|
package/dist/theme/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/theme/types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n ColorHarmony,\n
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/theme/types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n ColorContrastContext,\n ColorHarmony,\n ColorSelectionTarget,\n ColorSwatchSelectionResult,\n ColorSwatchTiePolicy,\n GeneratedThemeModeColors,\n GeneratedThemeSwatches,\n} from '@ankhorage/color-theory';\nimport type { AppCategory, ThemeConfig } from '@ankhorage/contracts';\nimport type { SurfaceTheme } from '@ankhorage/surface/theme';\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 type ZoraThemeSwatchRole = keyof GeneratedThemeSwatches;\n\n/** A measurable request delegated to Color Theory's canonical swatch selector. */\nexport interface ZoraThemeSelectionTarget {\n readonly id: string;\n readonly swatch: ZoraThemeSwatchRole;\n readonly target: ColorSelectionTarget;\n readonly contexts: readonly ColorContrastContext[];\n readonly tiePolicy: ColorSwatchTiePolicy;\n}\n\nexport interface ZoraThemeCompilationOptions {\n readonly selectionTargets?: Partial<Record<ZoraThemeMode, readonly ZoraThemeSelectionTarget[]>>;\n}\n\nexport interface ZoraThemeSelectionResult {\n readonly request: ZoraThemeSelectionTarget;\n readonly result: ColorSwatchSelectionResult | null;\n}\n\nexport type ZoraThemeCompilationDiagnosticCode =\n 'generated-output-divergence' | 'missing-target-swatch' | 'unresolved-selection-target';\n\nexport interface ZoraThemeCompilationDiagnostic {\n readonly code: ZoraThemeCompilationDiagnosticCode;\n readonly message: string;\n readonly mode: ZoraThemeMode;\n readonly path: string;\n readonly severity: 'error';\n}\n\nexport type ZoraThemeValueOrigin = 'defaulted' | 'derived' | 'direct';\n\nexport interface ZoraThemeProvenanceEntry {\n readonly path: string;\n readonly origin: ZoraThemeValueOrigin;\n readonly owner: string;\n readonly inputs?: readonly string[];\n}\n\nexport interface ZoraComputedThemeMode {\n readonly mode: ZoraThemeMode;\n readonly generated: GeneratedThemeModeColors;\n readonly surfaceTheme: SurfaceTheme;\n readonly selections: readonly ZoraThemeSelectionResult[];\n readonly diagnostics: readonly ZoraThemeCompilationDiagnostic[];\n readonly provenance: readonly ZoraThemeProvenanceEntry[];\n}\n\nexport interface ZoraComputedTheme {\n readonly themeConfig: ThemeConfig;\n readonly light: ZoraComputedThemeMode;\n readonly dark: ZoraComputedThemeMode;\n readonly diagnostics: readonly ZoraThemeCompilationDiagnostic[];\n readonly provenance: readonly ZoraThemeProvenanceEntry[];\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ankhorage/zora",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "4.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": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@ankhorage/color-theory": "^0.3.0",
|
|
40
40
|
"@ankhorage/contracts": "^8.0.2",
|
|
41
|
-
"@ankhorage/surface": "^3.
|
|
41
|
+
"@ankhorage/surface": "^3.3.0"
|
|
42
42
|
},
|
|
43
43
|
"files": [
|
|
44
44
|
"dist",
|
|
@@ -63,6 +63,14 @@
|
|
|
63
63
|
"import": "./dist/metadata/index.js",
|
|
64
64
|
"default": "./dist/metadata/index.js"
|
|
65
65
|
},
|
|
66
|
+
"./theme": {
|
|
67
|
+
"bun": "./src/theme/public.ts",
|
|
68
|
+
"react-native": "./src/theme/public.ts",
|
|
69
|
+
"browser": "./src/theme/public.ts",
|
|
70
|
+
"types": "./dist/theme/public.d.ts",
|
|
71
|
+
"import": "./dist/theme/public.js",
|
|
72
|
+
"default": "./dist/theme/public.js"
|
|
73
|
+
},
|
|
66
74
|
"./package.json": "./package.json"
|
|
67
75
|
},
|
|
68
76
|
"publishConfig": {
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import {
|
|
2
|
+
COLOR_HARMONIES,
|
|
3
|
+
generateThemeModeColors,
|
|
4
|
+
parseHexColorOrThrow,
|
|
5
|
+
} from '@ankhorage/color-theory';
|
|
6
|
+
import type { ThemeConfig } from '@ankhorage/contracts';
|
|
7
|
+
import { describe, expect, test } from 'bun:test';
|
|
8
|
+
|
|
9
|
+
import { compileZoraTheme, type ZoraComputedTheme, type ZoraThemeSelectionTarget } from './public';
|
|
10
|
+
|
|
11
|
+
const baseConfig: ThemeConfig = {
|
|
12
|
+
id: 'compiler-test',
|
|
13
|
+
name: 'Compiler test',
|
|
14
|
+
light: { primaryColor: '#2563eb', harmony: 'analogous' },
|
|
15
|
+
dark: { primaryColor: '#f97316', harmony: 'square' },
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function selectionTarget(
|
|
19
|
+
overrides: Partial<ZoraThemeSelectionTarget> = {},
|
|
20
|
+
): ZoraThemeSelectionTarget {
|
|
21
|
+
return {
|
|
22
|
+
id: 'primary-action',
|
|
23
|
+
swatch: 'primary',
|
|
24
|
+
target: { lightness: 0.55, chroma: 0.15 },
|
|
25
|
+
contexts: [
|
|
26
|
+
{
|
|
27
|
+
id: 'canvas',
|
|
28
|
+
against: parseHexColorOrThrow('#ffffff'),
|
|
29
|
+
minimumContrast: 1,
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
tiePolicy: 'lower-step',
|
|
33
|
+
...overrides,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
describe('compileZoraTheme', () => {
|
|
38
|
+
test('compiles different light and dark inputs through their canonical owners', () => {
|
|
39
|
+
const result: ZoraComputedTheme = compileZoraTheme(baseConfig);
|
|
40
|
+
|
|
41
|
+
expect(result.themeConfig).toBe(baseConfig);
|
|
42
|
+
expect(result.light.mode).toBe('light');
|
|
43
|
+
expect(result.dark.mode).toBe('dark');
|
|
44
|
+
expect(result.light.generated).toEqual(generateThemeModeColors(baseConfig.light));
|
|
45
|
+
expect(result.dark.generated).toEqual(generateThemeModeColors(baseConfig.dark));
|
|
46
|
+
expect(result.light.surfaceTheme.config).toBe(baseConfig);
|
|
47
|
+
expect(result.dark.surfaceTheme.config).toBe(baseConfig);
|
|
48
|
+
expect(result.light.surfaceTheme.colorDiagnostics.generated).toEqual(result.light.generated);
|
|
49
|
+
expect(result.dark.surfaceTheme.colorDiagnostics.generated).toEqual(result.dark.generated);
|
|
50
|
+
expect(result.diagnostics).toEqual([]);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('accepts every harmony from the canonical Color Theory catalog', () => {
|
|
54
|
+
for (const harmony of COLOR_HARMONIES) {
|
|
55
|
+
const result = compileZoraTheme({
|
|
56
|
+
...baseConfig,
|
|
57
|
+
light: { ...baseConfig.light, harmony },
|
|
58
|
+
dark: { ...baseConfig.dark, harmony },
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
expect(result.light.generated.harmonyRoleColors.harmony).toBe(harmony);
|
|
62
|
+
expect(result.dark.generated.harmonyRoleColors.harmony).toBe(harmony);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('delegates measurable swatch targets to Color Theory', () => {
|
|
67
|
+
const target = selectionTarget();
|
|
68
|
+
const result = compileZoraTheme(baseConfig, { selectionTargets: { light: [target] } });
|
|
69
|
+
|
|
70
|
+
expect(result.light.selections).toHaveLength(1);
|
|
71
|
+
expect(result.light.selections[0]?.request).toBe(target);
|
|
72
|
+
expect(result.light.selections[0]?.result?.selected).not.toBeNull();
|
|
73
|
+
expect(result.light.diagnostics).toEqual([]);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('reports a missing role instead of substituting another swatch', () => {
|
|
77
|
+
const config: ThemeConfig = {
|
|
78
|
+
...baseConfig,
|
|
79
|
+
light: { ...baseConfig.light, harmony: 'monochromatic' },
|
|
80
|
+
};
|
|
81
|
+
const target = selectionTarget({ id: 'quaternary-action', swatch: 'quaternary' });
|
|
82
|
+
const result = compileZoraTheme(config, { selectionTargets: { light: [target] } });
|
|
83
|
+
|
|
84
|
+
expect(result.light.selections[0]?.result).toBeNull();
|
|
85
|
+
expect(result.light.diagnostics).toEqual([
|
|
86
|
+
expect.objectContaining({
|
|
87
|
+
code: 'missing-target-swatch',
|
|
88
|
+
path: 'light.selectionTargets.quaternary-action',
|
|
89
|
+
}),
|
|
90
|
+
]);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('reports targets that cannot satisfy their measured contrast contexts', () => {
|
|
94
|
+
const target = selectionTarget({
|
|
95
|
+
contexts: [
|
|
96
|
+
{
|
|
97
|
+
id: 'impossible',
|
|
98
|
+
against: parseHexColorOrThrow('#ffffff'),
|
|
99
|
+
minimumContrast: 21,
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
});
|
|
103
|
+
const result = compileZoraTheme(baseConfig, { selectionTargets: { light: [target] } });
|
|
104
|
+
|
|
105
|
+
expect(result.light.selections[0]?.result?.selected).toBeNull();
|
|
106
|
+
expect(result.light.diagnostics[0]?.code).toBe('unresolved-selection-target');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('records direct, defaulted, and derived provenance without persisting computed output', () => {
|
|
110
|
+
const result = compileZoraTheme(baseConfig, {
|
|
111
|
+
selectionTargets: { light: [selectionTarget()] },
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
expect(result.provenance).toContainEqual({
|
|
115
|
+
path: 'themeConfig',
|
|
116
|
+
origin: 'direct',
|
|
117
|
+
owner: '@ankhorage/contracts',
|
|
118
|
+
});
|
|
119
|
+
expect(result.provenance).toContainEqual({
|
|
120
|
+
path: 'themeConfig.tokens',
|
|
121
|
+
origin: 'defaulted',
|
|
122
|
+
owner: '@ankhorage/surface',
|
|
123
|
+
});
|
|
124
|
+
expect(
|
|
125
|
+
result.light.provenance.some(
|
|
126
|
+
({ origin, owner, path }) =>
|
|
127
|
+
path === 'light.generated' && origin === 'derived' && owner === '@ankhorage/color-theory',
|
|
128
|
+
),
|
|
129
|
+
).toBe(true);
|
|
130
|
+
expect('generated' in result.themeConfig).toBe(false);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test('rejects invalid canonical source colors', () => {
|
|
134
|
+
expect(() =>
|
|
135
|
+
compileZoraTheme({
|
|
136
|
+
...baseConfig,
|
|
137
|
+
light: { ...baseConfig.light, primaryColor: 'blue' },
|
|
138
|
+
}),
|
|
139
|
+
).toThrow();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test('imports and executes through the public theme subpath in plain Bun', async () => {
|
|
143
|
+
const script = [
|
|
144
|
+
"import { compileZoraTheme } from '@ankhorage/zora/theme';",
|
|
145
|
+
`const result = compileZoraTheme(${JSON.stringify(baseConfig)});`,
|
|
146
|
+
'process.stdout.write(`${result.light.mode}:${result.dark.mode}`);',
|
|
147
|
+
].join('\n');
|
|
148
|
+
const subprocess = Bun.spawn({
|
|
149
|
+
cmd: [process.execPath, '-e', script],
|
|
150
|
+
stdout: 'pipe',
|
|
151
|
+
stderr: 'pipe',
|
|
152
|
+
});
|
|
153
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
154
|
+
new Response(subprocess.stdout).text(),
|
|
155
|
+
new Response(subprocess.stderr).text(),
|
|
156
|
+
subprocess.exited,
|
|
157
|
+
]);
|
|
158
|
+
|
|
159
|
+
expect(exitCode, stderr).toBe(0);
|
|
160
|
+
expect(stdout).toBe('light:dark');
|
|
161
|
+
});
|
|
162
|
+
});
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { generateThemeModeColors, selectColorSwatchStep } from '@ankhorage/color-theory';
|
|
2
|
+
import type { ThemeConfig } from '@ankhorage/contracts';
|
|
3
|
+
import { createTheme } from '@ankhorage/surface/theme';
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
ZoraComputedTheme,
|
|
7
|
+
ZoraComputedThemeMode,
|
|
8
|
+
ZoraThemeCompilationDiagnostic,
|
|
9
|
+
ZoraThemeCompilationOptions,
|
|
10
|
+
ZoraThemeMode,
|
|
11
|
+
ZoraThemeProvenanceEntry,
|
|
12
|
+
ZoraThemeSelectionResult,
|
|
13
|
+
ZoraThemeSelectionTarget,
|
|
14
|
+
} from './types';
|
|
15
|
+
|
|
16
|
+
const CONTRACTS_OWNER = '@ankhorage/contracts';
|
|
17
|
+
const COLOR_THEORY_OWNER = '@ankhorage/color-theory';
|
|
18
|
+
const SURFACE_OWNER = '@ankhorage/surface';
|
|
19
|
+
const ZORA_OWNER = '@ankhorage/zora';
|
|
20
|
+
|
|
21
|
+
/***
|
|
22
|
+
* Compile canonical theme source into Color Theory evidence and resolved Surface themes.
|
|
23
|
+
* This function is pure and does not mount React or reproduce owner algorithms.
|
|
24
|
+
*/
|
|
25
|
+
export function compileZoraTheme(
|
|
26
|
+
themeConfig: ThemeConfig,
|
|
27
|
+
options: ZoraThemeCompilationOptions = {},
|
|
28
|
+
): ZoraComputedTheme {
|
|
29
|
+
const light = compileMode(themeConfig, 'light', options.selectionTargets?.light ?? []);
|
|
30
|
+
const dark = compileMode(themeConfig, 'dark', options.selectionTargets?.dark ?? []);
|
|
31
|
+
const diagnostics = [...light.diagnostics, ...dark.diagnostics];
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
themeConfig,
|
|
35
|
+
light,
|
|
36
|
+
dark,
|
|
37
|
+
diagnostics,
|
|
38
|
+
provenance: createRootProvenance(themeConfig),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/*** Compile one canonical mode and compare the independent owner results. */
|
|
43
|
+
function compileMode(
|
|
44
|
+
themeConfig: ThemeConfig,
|
|
45
|
+
mode: ZoraThemeMode,
|
|
46
|
+
targets: readonly ZoraThemeSelectionTarget[],
|
|
47
|
+
): ZoraComputedThemeMode {
|
|
48
|
+
const modeConfig = mode === 'light' ? themeConfig.light : themeConfig.dark;
|
|
49
|
+
const generated = generateThemeModeColors(modeConfig);
|
|
50
|
+
const surfaceTheme = createTheme(themeConfig, mode);
|
|
51
|
+
const { diagnostics, selections } = resolveSelections(generated, mode, targets);
|
|
52
|
+
|
|
53
|
+
if (!outputsAgree(generated, surfaceTheme.colorDiagnostics.generated)) {
|
|
54
|
+
diagnostics.push({
|
|
55
|
+
code: 'generated-output-divergence',
|
|
56
|
+
message: 'Surface and Color Theory returned different generated color evidence.',
|
|
57
|
+
mode,
|
|
58
|
+
path: `${mode}.generated`,
|
|
59
|
+
severity: 'error',
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
mode,
|
|
65
|
+
generated,
|
|
66
|
+
surfaceTheme,
|
|
67
|
+
selections,
|
|
68
|
+
diagnostics,
|
|
69
|
+
provenance: createModeProvenance(mode, targets),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/*** Resolve measurable swatch targets without substituting unavailable roles. */
|
|
74
|
+
function resolveSelections(
|
|
75
|
+
generated: ZoraComputedThemeMode['generated'],
|
|
76
|
+
mode: ZoraThemeMode,
|
|
77
|
+
targets: readonly ZoraThemeSelectionTarget[],
|
|
78
|
+
): {
|
|
79
|
+
diagnostics: ZoraThemeCompilationDiagnostic[];
|
|
80
|
+
selections: ZoraThemeSelectionResult[];
|
|
81
|
+
} {
|
|
82
|
+
const diagnostics: ZoraThemeCompilationDiagnostic[] = [];
|
|
83
|
+
const selections = targets.map((request) => {
|
|
84
|
+
const swatch = generated.swatches[request.swatch];
|
|
85
|
+
const result = swatch
|
|
86
|
+
? selectColorSwatchStep(swatch, request.target, request.contexts, request.tiePolicy)
|
|
87
|
+
: null;
|
|
88
|
+
|
|
89
|
+
if (!swatch) diagnostics.push(createMissingSwatchDiagnostic(mode, request));
|
|
90
|
+
else if (!result?.selected) diagnostics.push(createUnresolvedTargetDiagnostic(mode, request));
|
|
91
|
+
|
|
92
|
+
return { request, result };
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
return { diagnostics, selections };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/*** Describe a requested swatch that the selected harmony does not generate. */
|
|
99
|
+
function createMissingSwatchDiagnostic(
|
|
100
|
+
mode: ZoraThemeMode,
|
|
101
|
+
request: ZoraThemeSelectionTarget,
|
|
102
|
+
): ZoraThemeCompilationDiagnostic {
|
|
103
|
+
return {
|
|
104
|
+
code: 'missing-target-swatch',
|
|
105
|
+
message: `The selected harmony does not generate the '${request.swatch}' swatch.`,
|
|
106
|
+
mode,
|
|
107
|
+
path: `${mode}.selectionTargets.${request.id}`,
|
|
108
|
+
severity: 'error',
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/*** Describe a measurable target that has no passing swatch candidate. */
|
|
113
|
+
function createUnresolvedTargetDiagnostic(
|
|
114
|
+
mode: ZoraThemeMode,
|
|
115
|
+
request: ZoraThemeSelectionTarget,
|
|
116
|
+
): ZoraThemeCompilationDiagnostic {
|
|
117
|
+
return {
|
|
118
|
+
code: 'unresolved-selection-target',
|
|
119
|
+
message: `No '${request.swatch}' step satisfies selection target '${request.id}'.`,
|
|
120
|
+
mode,
|
|
121
|
+
path: `${mode}.selectionTargets.${request.id}`,
|
|
122
|
+
severity: 'error',
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/*** Record direct source and Surface-owned default provenance. */
|
|
127
|
+
function createRootProvenance(themeConfig: ThemeConfig): ZoraThemeProvenanceEntry[] {
|
|
128
|
+
return [
|
|
129
|
+
{ path: 'themeConfig', origin: 'direct', owner: CONTRACTS_OWNER },
|
|
130
|
+
{
|
|
131
|
+
path: 'themeConfig.tokens',
|
|
132
|
+
origin: themeConfig.tokens ? 'direct' : 'defaulted',
|
|
133
|
+
owner: themeConfig.tokens ? CONTRACTS_OWNER : SURFACE_OWNER,
|
|
134
|
+
},
|
|
135
|
+
];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/*** Record the owner transformation chain for one compiled mode. */
|
|
139
|
+
function createModeProvenance(
|
|
140
|
+
mode: ZoraThemeMode,
|
|
141
|
+
targets: readonly ZoraThemeSelectionTarget[],
|
|
142
|
+
): ZoraThemeProvenanceEntry[] {
|
|
143
|
+
const inputPath = `themeConfig.${mode}`;
|
|
144
|
+
const entries: ZoraThemeProvenanceEntry[] = [
|
|
145
|
+
{ path: inputPath, origin: 'direct', owner: CONTRACTS_OWNER },
|
|
146
|
+
{
|
|
147
|
+
path: `${mode}.generated`,
|
|
148
|
+
origin: 'derived',
|
|
149
|
+
owner: COLOR_THEORY_OWNER,
|
|
150
|
+
inputs: [inputPath],
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
path: `${mode}.surfaceTheme`,
|
|
154
|
+
origin: 'derived',
|
|
155
|
+
owner: SURFACE_OWNER,
|
|
156
|
+
inputs: ['themeConfig', inputPath],
|
|
157
|
+
},
|
|
158
|
+
];
|
|
159
|
+
|
|
160
|
+
for (const target of targets) {
|
|
161
|
+
const targetPath = `${mode}.selectionTargets.${target.id}`;
|
|
162
|
+
entries.push({ path: targetPath, origin: 'direct', owner: ZORA_OWNER });
|
|
163
|
+
entries.push({
|
|
164
|
+
path: `${mode}.selections.${target.id}`,
|
|
165
|
+
origin: 'derived',
|
|
166
|
+
owner: COLOR_THEORY_OWNER,
|
|
167
|
+
inputs: [`${mode}.generated.swatches.${target.swatch}`, targetPath],
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return entries;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/*** Compare deterministic owner result records for divergence diagnostics. */
|
|
175
|
+
function outputsAgree(left: unknown, right: unknown): boolean {
|
|
176
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
177
|
+
}
|
|
@@ -4,6 +4,7 @@ import type { ThemeConfig } from '@ankhorage/contracts';
|
|
|
4
4
|
import type { ZoraTheme } from './types';
|
|
5
5
|
import { zoraDefaultTheme } from './zoraDefaultTheme';
|
|
6
6
|
|
|
7
|
+
/*** Convert the concise ZORA design seed into canonical persisted theme source. */
|
|
7
8
|
export function createZoraThemeConfig(theme: ZoraTheme = zoraDefaultTheme): ThemeConfig {
|
|
8
9
|
const primaryColor = parseHexColorOrThrow(theme.primaryColor);
|
|
9
10
|
|
package/src/theme/index.ts
CHANGED
|
@@ -1,17 +1,9 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * from './public';
|
|
2
2
|
export { ThemeModeToggle } from './ThemeModeToggle';
|
|
3
3
|
export type { ThemeModeToggleProps } from './ThemeModeToggleProps';
|
|
4
|
-
export type {
|
|
5
|
-
ZoraComputedTheme,
|
|
6
|
-
ZoraComputedThemeMode,
|
|
7
|
-
ZoraTheme,
|
|
8
|
-
ZoraThemeId,
|
|
9
|
-
ZoraThemeMode,
|
|
10
|
-
} from './types';
|
|
11
4
|
export { useZoraTheme } from './useZoraTheme';
|
|
12
5
|
export { withZoraThemeScope } from './withZoraThemeScope';
|
|
13
6
|
export type { ZoraBaseProps } from './ZoraBaseProps';
|
|
14
|
-
export { zoraDefaultTheme } from './zoraDefaultTheme';
|
|
15
7
|
export type { ZoraProviderProps } from './ZoraProvider';
|
|
16
8
|
export { ZoraProvider } from './ZoraProvider';
|
|
17
9
|
export type { ZoraThemeScopeProps } from './ZoraThemeScope';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { compileZoraTheme } from './compileZoraTheme';
|
|
2
|
+
export { createZoraThemeConfig } from './createZoraThemeConfig';
|
|
3
|
+
export type {
|
|
4
|
+
ZoraComputedTheme,
|
|
5
|
+
ZoraComputedThemeMode,
|
|
6
|
+
ZoraTheme,
|
|
7
|
+
ZoraThemeCompilationDiagnostic,
|
|
8
|
+
ZoraThemeCompilationDiagnosticCode,
|
|
9
|
+
ZoraThemeCompilationOptions,
|
|
10
|
+
ZoraThemeId,
|
|
11
|
+
ZoraThemeMode,
|
|
12
|
+
ZoraThemeProvenanceEntry,
|
|
13
|
+
ZoraThemeSelectionResult,
|
|
14
|
+
ZoraThemeSelectionTarget,
|
|
15
|
+
ZoraThemeSwatchRole,
|
|
16
|
+
ZoraThemeValueOrigin,
|
|
17
|
+
} from './types';
|
|
18
|
+
export { zoraDefaultTheme } from './zoraDefaultTheme';
|
package/src/theme/types.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
ColorContrastContext,
|
|
2
3
|
ColorHarmony,
|
|
4
|
+
ColorSelectionTarget,
|
|
5
|
+
ColorSwatchSelectionResult,
|
|
6
|
+
ColorSwatchTiePolicy,
|
|
3
7
|
GeneratedThemeModeColors,
|
|
4
8
|
GeneratedThemeSwatches,
|
|
5
|
-
SemanticColorToken,
|
|
6
9
|
} from '@ankhorage/color-theory';
|
|
7
10
|
import type { AppCategory, ThemeConfig } from '@ankhorage/contracts';
|
|
8
|
-
import type { SurfaceTheme } from '@ankhorage/surface';
|
|
11
|
+
import type { SurfaceTheme } from '@ankhorage/surface/theme';
|
|
9
12
|
|
|
10
13
|
export type ZoraThemeId = string;
|
|
11
14
|
|
|
@@ -19,19 +22,59 @@ export interface ZoraTheme {
|
|
|
19
22
|
harmony: ColorHarmony;
|
|
20
23
|
}
|
|
21
24
|
|
|
25
|
+
export type ZoraThemeSwatchRole = keyof GeneratedThemeSwatches;
|
|
26
|
+
|
|
27
|
+
/** A measurable request delegated to Color Theory's canonical swatch selector. */
|
|
28
|
+
export interface ZoraThemeSelectionTarget {
|
|
29
|
+
readonly id: string;
|
|
30
|
+
readonly swatch: ZoraThemeSwatchRole;
|
|
31
|
+
readonly target: ColorSelectionTarget;
|
|
32
|
+
readonly contexts: readonly ColorContrastContext[];
|
|
33
|
+
readonly tiePolicy: ColorSwatchTiePolicy;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface ZoraThemeCompilationOptions {
|
|
37
|
+
readonly selectionTargets?: Partial<Record<ZoraThemeMode, readonly ZoraThemeSelectionTarget[]>>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface ZoraThemeSelectionResult {
|
|
41
|
+
readonly request: ZoraThemeSelectionTarget;
|
|
42
|
+
readonly result: ColorSwatchSelectionResult | null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type ZoraThemeCompilationDiagnosticCode =
|
|
46
|
+
'generated-output-divergence' | 'missing-target-swatch' | 'unresolved-selection-target';
|
|
47
|
+
|
|
48
|
+
export interface ZoraThemeCompilationDiagnostic {
|
|
49
|
+
readonly code: ZoraThemeCompilationDiagnosticCode;
|
|
50
|
+
readonly message: string;
|
|
51
|
+
readonly mode: ZoraThemeMode;
|
|
52
|
+
readonly path: string;
|
|
53
|
+
readonly severity: 'error';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type ZoraThemeValueOrigin = 'defaulted' | 'derived' | 'direct';
|
|
57
|
+
|
|
58
|
+
export interface ZoraThemeProvenanceEntry {
|
|
59
|
+
readonly path: string;
|
|
60
|
+
readonly origin: ZoraThemeValueOrigin;
|
|
61
|
+
readonly owner: string;
|
|
62
|
+
readonly inputs?: readonly string[];
|
|
63
|
+
}
|
|
64
|
+
|
|
22
65
|
export interface ZoraComputedThemeMode {
|
|
23
|
-
mode: ZoraThemeMode;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
66
|
+
readonly mode: ZoraThemeMode;
|
|
67
|
+
readonly generated: GeneratedThemeModeColors;
|
|
68
|
+
readonly surfaceTheme: SurfaceTheme;
|
|
69
|
+
readonly selections: readonly ZoraThemeSelectionResult[];
|
|
70
|
+
readonly diagnostics: readonly ZoraThemeCompilationDiagnostic[];
|
|
71
|
+
readonly provenance: readonly ZoraThemeProvenanceEntry[];
|
|
28
72
|
}
|
|
29
73
|
|
|
30
74
|
export interface ZoraComputedTheme {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
dark: ZoraComputedThemeMode;
|
|
75
|
+
readonly themeConfig: ThemeConfig;
|
|
76
|
+
readonly light: ZoraComputedThemeMode;
|
|
77
|
+
readonly dark: ZoraComputedThemeMode;
|
|
78
|
+
readonly diagnostics: readonly ZoraThemeCompilationDiagnostic[];
|
|
79
|
+
readonly provenance: readonly ZoraThemeProvenanceEntry[];
|
|
37
80
|
}
|