@ankhorage/surface 3.1.3 → 3.3.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 +12 -0
- package/dist/components/navigation/helpers.d.ts.map +1 -1
- package/dist/components/navigation/helpers.js +16 -8
- package/dist/components/navigation/helpers.js.map +1 -1
- package/dist/internal/resolvers/resolveInteractiveColors.d.ts.map +1 -1
- package/dist/internal/resolvers/resolveInteractiveColors.js +19 -13
- package/dist/internal/resolvers/resolveInteractiveColors.js.map +1 -1
- package/dist/internal/resolvers/resolveSelectionControlColors.d.ts.map +1 -1
- package/dist/internal/resolvers/resolveSelectionControlColors.js +19 -10
- package/dist/internal/resolvers/resolveSelectionControlColors.js.map +1 -1
- package/dist/theme/colorEngine.d.ts +4 -3
- package/dist/theme/colorEngine.d.ts.map +1 -1
- package/dist/theme/colorEngine.js +23 -122
- package/dist/theme/colorEngine.js.map +1 -1
- package/dist/theme/createSurfaceColorDiagnosticCollector.d.ts +14 -0
- package/dist/theme/createSurfaceColorDiagnosticCollector.d.ts.map +1 -0
- package/dist/theme/createSurfaceColorDiagnosticCollector.js +53 -0
- package/dist/theme/createSurfaceColorDiagnosticCollector.js.map +1 -0
- package/dist/theme/createSurfaceSemanticPolicy.d.ts +30 -0
- package/dist/theme/createSurfaceSemanticPolicy.d.ts.map +1 -0
- package/dist/theme/createSurfaceSemanticPolicy.js +118 -0
- package/dist/theme/createSurfaceSemanticPolicy.js.map +1 -0
- package/dist/theme/createTheme.d.ts.map +1 -1
- package/dist/theme/createTheme.js +2 -1
- package/dist/theme/createTheme.js.map +1 -1
- package/dist/theme/defaultTokens.d.ts +1 -1
- package/dist/theme/defaultTokens.d.ts.map +1 -1
- package/dist/theme/defaultTokens.js.map +1 -1
- package/dist/theme/index.d.ts +1 -4
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +1 -4
- 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 +5 -0
- package/dist/theme/public.js.map +1 -0
- package/dist/theme/resolveGlobalTokens.d.ts +1 -1
- package/dist/theme/resolveGlobalTokens.d.ts.map +1 -1
- package/dist/theme/resolveGlobalTokens.js.map +1 -1
- package/dist/theme/resolveRoleSemantics.d.ts +19 -0
- package/dist/theme/resolveRoleSemantics.d.ts.map +1 -0
- package/dist/theme/resolveRoleSemantics.js +29 -0
- package/dist/theme/resolveRoleSemantics.js.map +1 -0
- package/dist/theme/resolveSurfaceSemanticModel.d.ts +16 -0
- package/dist/theme/resolveSurfaceSemanticModel.d.ts.map +1 -0
- package/dist/theme/resolveSurfaceSemanticModel.js +128 -0
- package/dist/theme/resolveSurfaceSemanticModel.js.map +1 -0
- package/dist/theme/surfaceColorPolicy.d.ts +8 -0
- package/dist/theme/surfaceColorPolicy.d.ts.map +1 -0
- package/dist/theme/surfaceColorPolicy.js +8 -0
- package/dist/theme/surfaceColorPolicy.js.map +1 -0
- package/dist/theme/types.d.ts +52 -1
- package/dist/theme/types.d.ts.map +1 -1
- package/dist/theme/types.js.map +1 -1
- package/package.json +9 -1
- package/src/components/navigation/helpers.test.ts +4 -4
- package/src/components/navigation/helpers.ts +17 -8
- package/src/index.test.ts +20 -0
- package/src/internal/resolvers/resolveInteractiveColors.test.ts +1 -0
- package/src/internal/resolvers/resolveInteractiveColors.ts +19 -13
- package/src/internal/resolvers/resolveSelectionControlColors.test.ts +3 -2
- package/src/internal/resolvers/resolveSelectionControlColors.ts +19 -10
- package/src/primitives/icon/PortableIcon.web.test.tsx +2 -7
- package/src/surfaceColor.test.ts +8 -10
- package/src/theme/colorEngine.test.ts +85 -34
- package/src/theme/colorEngine.ts +28 -147
- package/src/theme/createSurfaceColorDiagnosticCollector.ts +91 -0
- package/src/theme/createSurfaceSemanticPolicy.ts +174 -0
- package/src/theme/createTheme.ts +2 -1
- package/src/theme/defaultTokens.ts +4 -1
- package/src/theme/index.ts +1 -4
- package/src/theme/public.test.ts +43 -0
- package/src/theme/public.ts +4 -0
- package/src/theme/resolveGlobalTokens.ts +4 -1
- package/src/theme/resolveRoleSemantics.ts +96 -0
- package/src/theme/resolveSurfaceSemanticModel.ts +230 -0
- package/src/theme/surfaceColorPolicy.ts +7 -0
- package/src/theme/types.ts +65 -1
package/dist/theme/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { GeneratedThemeSwatches } from '@ankhorage/color-theory';
|
|
1
|
+
import type { ColorSwatchDiagnostics, ColorSwatchSelectionResult, DefaultSemanticStatusRole, GeneratedThemeModeColors, GeneratedThemeSwatches, HexColor, SemanticColorReferenceMap, ThemeColorMode } from '@ankhorage/color-theory';
|
|
2
2
|
import type { ThemeConfig as ContractsThemeConfig } from '@ankhorage/contracts';
|
|
3
3
|
export type { ColorHarmony, ColorSwatch, ColorSwatchStep, GeneratedColorRole, GeneratedThemeModeColors, GeneratedThemeSwatches, HexColor, SemanticColorReferenceMap, SemanticColorToken, } from '@ankhorage/color-theory';
|
|
4
4
|
export type { ThemeConfig, ThemeModeConfig } from '@ankhorage/contracts';
|
|
@@ -14,6 +14,8 @@ export interface NeutralSemantics {
|
|
|
14
14
|
text: string;
|
|
15
15
|
textMuted: string;
|
|
16
16
|
textSubtle: string;
|
|
17
|
+
disabledBg: string;
|
|
18
|
+
disabledText: string;
|
|
17
19
|
}
|
|
18
20
|
export interface RoleSemantics {
|
|
19
21
|
base: string;
|
|
@@ -23,25 +25,48 @@ export interface RoleSemantics {
|
|
|
23
25
|
softHover: string;
|
|
24
26
|
softActive: string;
|
|
25
27
|
outline: string;
|
|
28
|
+
onSurfaceText: string;
|
|
26
29
|
onSolidText: string;
|
|
30
|
+
onHoverText: string;
|
|
31
|
+
onStrongText: string;
|
|
32
|
+
onSoftText: string;
|
|
33
|
+
onSoftHoverText: string;
|
|
34
|
+
onSoftActiveText: string;
|
|
35
|
+
disabledBg: string;
|
|
36
|
+
onDisabledText: string;
|
|
27
37
|
}
|
|
28
38
|
export interface SurfaceSemantics {
|
|
29
39
|
default: string;
|
|
30
40
|
subtle: string;
|
|
31
41
|
raised: string;
|
|
42
|
+
sunken: string;
|
|
43
|
+
overlay: string;
|
|
44
|
+
scrim: string;
|
|
45
|
+
disabled: string;
|
|
32
46
|
inverse: string;
|
|
33
47
|
}
|
|
34
48
|
export interface ContentSemantics {
|
|
35
49
|
default: string;
|
|
36
50
|
muted: string;
|
|
37
51
|
subtle: string;
|
|
52
|
+
disabled: string;
|
|
53
|
+
icon: string;
|
|
54
|
+
link: string;
|
|
55
|
+
visited: string;
|
|
38
56
|
inverse: string;
|
|
39
57
|
}
|
|
40
58
|
export interface BorderSemantics {
|
|
41
59
|
default: string;
|
|
60
|
+
subtle: string;
|
|
42
61
|
strong: string;
|
|
62
|
+
divider: string;
|
|
43
63
|
focus: string;
|
|
44
64
|
}
|
|
65
|
+
export interface SelectionSemantics {
|
|
66
|
+
background: string;
|
|
67
|
+
content: string;
|
|
68
|
+
border: string;
|
|
69
|
+
}
|
|
45
70
|
export interface ActionSemantics {
|
|
46
71
|
primary: RoleSemantics;
|
|
47
72
|
neutral: RoleSemantics;
|
|
@@ -61,8 +86,32 @@ export interface ThemeSemantics {
|
|
|
61
86
|
surface: SurfaceSemantics;
|
|
62
87
|
content: ContentSemantics;
|
|
63
88
|
border: BorderSemantics;
|
|
89
|
+
selection: SelectionSemantics;
|
|
64
90
|
action: ActionSemantics;
|
|
65
91
|
}
|
|
92
|
+
export interface SurfaceContrastDiagnostic {
|
|
93
|
+
id: string;
|
|
94
|
+
foreground: HexColor;
|
|
95
|
+
background: HexColor;
|
|
96
|
+
contrast: number;
|
|
97
|
+
minimumContrast: number;
|
|
98
|
+
passes: boolean;
|
|
99
|
+
}
|
|
100
|
+
/** A Color Theory swatch selection together with its stable Surface purpose. */
|
|
101
|
+
export interface SurfaceColorSelectionDiagnostic {
|
|
102
|
+
id: string;
|
|
103
|
+
result: ColorSwatchSelectionResult;
|
|
104
|
+
}
|
|
105
|
+
/** Owner-produced color evidence used to explain a resolved Surface theme. */
|
|
106
|
+
export interface SurfaceColorDiagnostics {
|
|
107
|
+
mode: ThemeColorMode;
|
|
108
|
+
generated: GeneratedThemeModeColors;
|
|
109
|
+
semanticReferences: SemanticColorReferenceMap;
|
|
110
|
+
statusSwatches: Record<DefaultSemanticStatusRole, ColorSwatchDiagnostics>;
|
|
111
|
+
selections: readonly SurfaceColorSelectionDiagnostic[];
|
|
112
|
+
contrasts: readonly SurfaceContrastDiagnostic[];
|
|
113
|
+
surfaceSeparation: readonly SurfaceContrastDiagnostic[];
|
|
114
|
+
}
|
|
66
115
|
export type FontWeight = '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900' | 'bold' | 'normal';
|
|
67
116
|
export interface ThemeTokens {
|
|
68
117
|
colors: {
|
|
@@ -85,6 +134,8 @@ export interface ThemeTokens {
|
|
|
85
134
|
};
|
|
86
135
|
swatches: GeneratedThemeSwatches;
|
|
87
136
|
semantics: ThemeSemantics;
|
|
137
|
+
/** Generated owner evidence; this is runtime output and is not persisted in ThemeConfig. */
|
|
138
|
+
colorDiagnostics: SurfaceColorDiagnostics;
|
|
88
139
|
spacing: {
|
|
89
140
|
none: 0;
|
|
90
141
|
xs: number;
|
|
@@ -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":"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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 {
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/theme/types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n ColorSwatchDiagnostics,\n ColorSwatchSelectionResult,\n DefaultSemanticStatusRole,\n GeneratedThemeModeColors,\n GeneratedThemeSwatches,\n HexColor,\n SemanticColorReferenceMap,\n ThemeColorMode,\n} from '@ankhorage/color-theory';\nimport type { ThemeConfig as ContractsThemeConfig } from '@ankhorage/contracts';\n\nexport type {\n ColorHarmony,\n ColorSwatch,\n ColorSwatchStep,\n GeneratedColorRole,\n GeneratedThemeModeColors,\n GeneratedThemeSwatches,\n HexColor,\n SemanticColorReferenceMap,\n SemanticColorToken,\n} from '@ankhorage/color-theory';\nexport type { ThemeConfig, ThemeModeConfig } from '@ankhorage/contracts';\n\nexport interface NeutralSemantics {\n bg: string;\n bgSubtle: string;\n surface: string;\n surfaceHover: string;\n surfaceActive: string;\n border: string;\n borderStrong: string;\n divider: string;\n text: string;\n textMuted: string;\n textSubtle: string;\n disabledBg: string;\n disabledText: string;\n}\n\nexport interface RoleSemantics {\n base: string;\n hover: string;\n strong: string;\n softBg: string;\n softHover: string;\n softActive: string;\n outline: string;\n onSurfaceText: string;\n onSolidText: string;\n onHoverText: string;\n onStrongText: string;\n onSoftText: string;\n onSoftHoverText: string;\n onSoftActiveText: string;\n disabledBg: string;\n onDisabledText: string;\n}\n\nexport interface SurfaceSemantics {\n default: string;\n subtle: string;\n raised: string;\n sunken: string;\n overlay: string;\n scrim: string;\n disabled: string;\n inverse: string;\n}\n\nexport interface ContentSemantics {\n default: string;\n muted: string;\n subtle: string;\n disabled: string;\n icon: string;\n link: string;\n visited: string;\n inverse: string;\n}\n\nexport interface BorderSemantics {\n default: string;\n subtle: string;\n strong: string;\n divider: string;\n focus: string;\n}\n\nexport interface SelectionSemantics {\n background: string;\n content: string;\n border: string;\n}\n\nexport interface ActionSemantics {\n primary: RoleSemantics;\n neutral: RoleSemantics;\n danger: RoleSemantics;\n}\n\nexport interface ThemeSemantics {\n neutral: NeutralSemantics;\n brand: RoleSemantics;\n secondary: RoleSemantics;\n accent: RoleSemantics;\n highlight: RoleSemantics;\n danger: RoleSemantics;\n success: RoleSemantics;\n warning: RoleSemantics;\n error: RoleSemantics;\n info: RoleSemantics;\n surface: SurfaceSemantics;\n content: ContentSemantics;\n border: BorderSemantics;\n selection: SelectionSemantics;\n action: ActionSemantics;\n}\n\nexport interface SurfaceContrastDiagnostic {\n id: string;\n foreground: HexColor;\n background: HexColor;\n contrast: number;\n minimumContrast: number;\n passes: boolean;\n}\n\n/** A Color Theory swatch selection together with its stable Surface purpose. */\nexport interface SurfaceColorSelectionDiagnostic {\n id: string;\n result: ColorSwatchSelectionResult;\n}\n\n/** Owner-produced color evidence used to explain a resolved Surface theme. */\nexport interface SurfaceColorDiagnostics {\n mode: ThemeColorMode;\n generated: GeneratedThemeModeColors;\n semanticReferences: SemanticColorReferenceMap;\n statusSwatches: Record<DefaultSemanticStatusRole, ColorSwatchDiagnostics>;\n selections: readonly SurfaceColorSelectionDiagnostic[];\n contrasts: readonly SurfaceContrastDiagnostic[];\n surfaceSeparation: readonly SurfaceContrastDiagnostic[];\n}\n\nexport type FontWeight =\n '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900' | 'bold' | 'normal';\n\nexport interface ThemeTokens {\n colors: {\n primary: string;\n secondary: string;\n accent: string;\n highlight: string;\n tertiary: string;\n quaternary: string;\n background: string;\n surface: string;\n text: string;\n textSecondary: string;\n border: string;\n error: string;\n success: string;\n warning: string;\n info: string;\n [key: string]: string;\n };\n swatches: GeneratedThemeSwatches;\n semantics: ThemeSemantics;\n /** Generated owner evidence; this is runtime output and is not persisted in ThemeConfig. */\n colorDiagnostics: SurfaceColorDiagnostics;\n spacing: {\n none: 0;\n xs: number;\n s: number;\n m: number;\n l: number;\n xl: number;\n xxl: number;\n [key: string]: number;\n };\n radii: {\n none: 0;\n s: number;\n m: number;\n l: number;\n full: number;\n [key: string]: number;\n };\n typography: {\n headings: Record<\n 1 | 2 | 3 | 4 | 5 | 6,\n {\n size: number;\n lineHeight: number;\n weight: 'regular' | 'medium' | 'semiBold' | 'bold';\n }\n >;\n sizes: {\n xs: number;\n s: number;\n m: number;\n l: number;\n xl: number;\n xxl: number;\n '3xl': number;\n h1: number;\n h2: number;\n h3: number;\n h4: number;\n h5: number;\n h6: number;\n [key: string]: number;\n };\n weights: {\n thin: FontWeight;\n extraLight: FontWeight;\n light: FontWeight;\n regular: FontWeight;\n medium: FontWeight;\n semiBold: FontWeight;\n bold: FontWeight;\n extraBold: FontWeight;\n black: FontWeight;\n };\n fonts: {\n /** Map of weight -> fontFamily name for normal style */\n normal: Record<FontWeight, string | undefined>;\n /** Map of weight -> fontFamily name for italic style */\n italic: Record<FontWeight, string | undefined>;\n };\n };\n shadows: {\n soft: number;\n medium: number;\n hard: number;\n [key: string]: number;\n };\n}\n\nexport interface SurfaceTheme extends ThemeTokens {\n config: ContractsThemeConfig;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ankhorage/surface",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.3.0",
|
|
5
5
|
"description": "Stable UI foundation for React Native and React Native Web.",
|
|
6
6
|
"homepage": "https://github.com/ankhorage/surface#readme",
|
|
7
7
|
"bugs": {
|
|
@@ -56,6 +56,14 @@
|
|
|
56
56
|
"import": "./dist/index.js",
|
|
57
57
|
"default": "./dist/index.js"
|
|
58
58
|
},
|
|
59
|
+
"./theme": {
|
|
60
|
+
"bun": "./src/theme/public.ts",
|
|
61
|
+
"react-native": "./src/theme/public.ts",
|
|
62
|
+
"browser": "./src/theme/public.ts",
|
|
63
|
+
"types": "./dist/theme/public.d.ts",
|
|
64
|
+
"import": "./dist/theme/public.js",
|
|
65
|
+
"default": "./dist/theme/public.js"
|
|
66
|
+
},
|
|
59
67
|
"./package.json": "./package.json"
|
|
60
68
|
},
|
|
61
69
|
"publishConfig": {
|
|
@@ -40,7 +40,7 @@ describe('navigation helpers', () => {
|
|
|
40
40
|
});
|
|
41
41
|
|
|
42
42
|
expect(resolved.backgroundColor).toBe(theme.semantics.action.primary.softHover);
|
|
43
|
-
expect(resolved.contentColor).toBe(theme.semantics.action.primary.
|
|
43
|
+
expect(resolved.contentColor).toBe(theme.semantics.action.primary.onSoftHoverText);
|
|
44
44
|
expect(resolved.opacity).toBe(1);
|
|
45
45
|
});
|
|
46
46
|
|
|
@@ -67,8 +67,8 @@ describe('navigation helpers', () => {
|
|
|
67
67
|
state,
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
-
expect(resolved.backgroundColor).toBe(theme.semantics.
|
|
71
|
-
expect(resolved.contentColor).toBe(theme.semantics.content.
|
|
72
|
-
expect(resolved.opacity).toBe(
|
|
70
|
+
expect(resolved.backgroundColor).toBe(theme.semantics.surface.disabled);
|
|
71
|
+
expect(resolved.contentColor).toBe(theme.semantics.content.disabled);
|
|
72
|
+
expect(resolved.opacity).toBe(1);
|
|
73
73
|
});
|
|
74
74
|
});
|
|
@@ -41,12 +41,21 @@ export function resolveNavigationItemPresentation(
|
|
|
41
41
|
): ResolvedNavigationItemPresentation {
|
|
42
42
|
const interactionState = disabled ? { ...state, hovered: false, pressed: false } : state;
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
if (disabled) {
|
|
45
|
+
return {
|
|
46
|
+
backgroundColor: theme.semantics.surface.disabled,
|
|
47
|
+
contentColor: theme.semantics.content.disabled,
|
|
48
|
+
opacity: 1,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const contentColor = active
|
|
53
|
+
? interactionState.pressed
|
|
54
|
+
? theme.semantics.action.primary.onSoftActiveText
|
|
55
|
+
: interactionState.hovered
|
|
56
|
+
? theme.semantics.action.primary.onSoftHoverText
|
|
57
|
+
: theme.semantics.action.primary.onSoftText
|
|
58
|
+
: theme.semantics.content.default;
|
|
50
59
|
|
|
51
60
|
if (active) {
|
|
52
61
|
return {
|
|
@@ -56,7 +65,7 @@ export function resolveNavigationItemPresentation(
|
|
|
56
65
|
? theme.semantics.action.primary.softHover
|
|
57
66
|
: theme.semantics.action.primary.softBg,
|
|
58
67
|
contentColor,
|
|
59
|
-
opacity,
|
|
68
|
+
opacity: 1,
|
|
60
69
|
};
|
|
61
70
|
}
|
|
62
71
|
|
|
@@ -67,6 +76,6 @@ export function resolveNavigationItemPresentation(
|
|
|
67
76
|
? theme.semantics.neutral.surfaceHover
|
|
68
77
|
: 'transparent',
|
|
69
78
|
contentColor,
|
|
70
|
-
opacity,
|
|
79
|
+
opacity: 1,
|
|
71
80
|
};
|
|
72
81
|
}
|
package/src/index.test.ts
CHANGED
|
@@ -2,6 +2,9 @@ import { readFileSync } from 'node:fs';
|
|
|
2
2
|
|
|
3
3
|
import { describe, expect, it } from 'bun:test';
|
|
4
4
|
|
|
5
|
+
import type { SelectionSemantics, SurfaceColorDiagnostics } from './index';
|
|
6
|
+
import { createTheme } from './theme/createTheme';
|
|
7
|
+
|
|
5
8
|
const indexSource = readFileSync(new URL('./index.ts', import.meta.url), 'utf8');
|
|
6
9
|
const packageJson = JSON.parse(
|
|
7
10
|
readFileSync(new URL('../package.json', import.meta.url), 'utf8'),
|
|
@@ -67,6 +70,14 @@ describe('public package contract', () => {
|
|
|
67
70
|
import: './dist/index.js',
|
|
68
71
|
types: './dist/index.d.ts',
|
|
69
72
|
},
|
|
73
|
+
'./theme': {
|
|
74
|
+
bun: './src/theme/public.ts',
|
|
75
|
+
'react-native': './src/theme/public.ts',
|
|
76
|
+
browser: './src/theme/public.ts',
|
|
77
|
+
default: './dist/theme/public.js',
|
|
78
|
+
import: './dist/theme/public.js',
|
|
79
|
+
types: './dist/theme/public.d.ts',
|
|
80
|
+
},
|
|
70
81
|
'./package.json': './package.json',
|
|
71
82
|
});
|
|
72
83
|
});
|
|
@@ -75,4 +86,13 @@ describe('public package contract', () => {
|
|
|
75
86
|
expect(packageJson.peerDependencies['react-native']).toBe('0.86.x');
|
|
76
87
|
expect(packageJson.devDependencies['react-native']).toBe('0.86.3');
|
|
77
88
|
});
|
|
89
|
+
|
|
90
|
+
it('exports the resolved semantic and diagnostic contracts from the public root', () => {
|
|
91
|
+
const theme = createTheme();
|
|
92
|
+
const selection: SelectionSemantics = theme.semantics.selection;
|
|
93
|
+
const diagnostics: SurfaceColorDiagnostics = theme.colorDiagnostics;
|
|
94
|
+
|
|
95
|
+
expect(selection.background).toBeDefined();
|
|
96
|
+
expect(diagnostics.generated.swatches).toBe(theme.swatches);
|
|
97
|
+
});
|
|
78
98
|
});
|
|
@@ -32,6 +32,7 @@ describe('resolveInteractiveColors', () => {
|
|
|
32
32
|
expect(solid.contentColor).toBe(theme.semantics.action.primary.onSolidText);
|
|
33
33
|
expect(outline.borderColor).toBe(theme.semantics.action.danger.outline);
|
|
34
34
|
expect(outline.backgroundColor).toBe(theme.semantics.action.danger.softHover);
|
|
35
|
+
expect(outline.contentColor).toBe(theme.semantics.action.danger.onSurfaceText);
|
|
35
36
|
});
|
|
36
37
|
|
|
37
38
|
it('derives input colors from interaction and validation state', () => {
|
|
@@ -27,10 +27,9 @@ export function resolveButtonColors(
|
|
|
27
27
|
): ResolvedInteractiveColors {
|
|
28
28
|
if (state.disabled) {
|
|
29
29
|
return {
|
|
30
|
-
backgroundColor: theme.semantics.
|
|
31
|
-
borderColor: theme.semantics.
|
|
32
|
-
contentColor: theme.semantics.content.
|
|
33
|
-
opacity: 0.72,
|
|
30
|
+
backgroundColor: theme.semantics.surface.disabled,
|
|
31
|
+
borderColor: theme.semantics.border.subtle,
|
|
32
|
+
contentColor: theme.semantics.content.disabled,
|
|
34
33
|
};
|
|
35
34
|
}
|
|
36
35
|
|
|
@@ -45,7 +44,7 @@ export function resolveButtonColors(
|
|
|
45
44
|
? semanticTone.softHover
|
|
46
45
|
: 'transparent',
|
|
47
46
|
borderColor: semanticTone.outline,
|
|
48
|
-
contentColor: semanticTone.
|
|
47
|
+
contentColor: semanticTone.onSurfaceText,
|
|
49
48
|
};
|
|
50
49
|
case 'ghost':
|
|
51
50
|
return {
|
|
@@ -55,7 +54,7 @@ export function resolveButtonColors(
|
|
|
55
54
|
? semanticTone.softHover
|
|
56
55
|
: 'transparent',
|
|
57
56
|
borderColor: 'transparent',
|
|
58
|
-
contentColor: semanticTone.
|
|
57
|
+
contentColor: semanticTone.onSurfaceText,
|
|
59
58
|
};
|
|
60
59
|
case 'soft':
|
|
61
60
|
return {
|
|
@@ -65,7 +64,11 @@ export function resolveButtonColors(
|
|
|
65
64
|
? semanticTone.softHover
|
|
66
65
|
: semanticTone.softBg,
|
|
67
66
|
borderColor: 'transparent',
|
|
68
|
-
contentColor:
|
|
67
|
+
contentColor: state.pressed
|
|
68
|
+
? semanticTone.onSoftActiveText
|
|
69
|
+
: state.hovered
|
|
70
|
+
? semanticTone.onSoftHoverText
|
|
71
|
+
: semanticTone.onSoftText,
|
|
69
72
|
};
|
|
70
73
|
case 'solid':
|
|
71
74
|
default:
|
|
@@ -76,7 +79,11 @@ export function resolveButtonColors(
|
|
|
76
79
|
? semanticTone.hover
|
|
77
80
|
: semanticTone.base,
|
|
78
81
|
borderColor: semanticTone.base,
|
|
79
|
-
contentColor:
|
|
82
|
+
contentColor: state.pressed
|
|
83
|
+
? semanticTone.onStrongText
|
|
84
|
+
: state.hovered
|
|
85
|
+
? semanticTone.onHoverText
|
|
86
|
+
: semanticTone.onSolidText,
|
|
80
87
|
};
|
|
81
88
|
}
|
|
82
89
|
}
|
|
@@ -87,11 +94,10 @@ export function resolveInputColors(
|
|
|
87
94
|
): ResolvedInteractiveColors & { placeholderColor: string } {
|
|
88
95
|
if (fieldState.disabled) {
|
|
89
96
|
return {
|
|
90
|
-
backgroundColor: theme.semantics.surface.
|
|
91
|
-
borderColor: theme.semantics.border.
|
|
92
|
-
contentColor: theme.semantics.content.
|
|
93
|
-
placeholderColor: theme.semantics.content.
|
|
94
|
-
opacity: 0.72,
|
|
97
|
+
backgroundColor: theme.semantics.surface.disabled,
|
|
98
|
+
borderColor: theme.semantics.border.subtle,
|
|
99
|
+
contentColor: theme.semantics.content.disabled,
|
|
100
|
+
placeholderColor: theme.semantics.content.disabled,
|
|
95
101
|
};
|
|
96
102
|
}
|
|
97
103
|
|
|
@@ -38,7 +38,8 @@ describe('resolveSelectionControlColors', () => {
|
|
|
38
38
|
color: 'danger',
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
expect(colors.
|
|
42
|
-
expect(colors.
|
|
41
|
+
expect(colors.backgroundColor).toBe(theme.semantics.surface.disabled);
|
|
42
|
+
expect(colors.labelColor).toBe(theme.semantics.content.disabled);
|
|
43
|
+
expect(colors.opacity).toBeUndefined();
|
|
43
44
|
});
|
|
44
45
|
});
|
|
@@ -35,15 +35,12 @@ export function resolveSelectionControlColors(
|
|
|
35
35
|
|
|
36
36
|
if (isMuted) {
|
|
37
37
|
return {
|
|
38
|
-
backgroundColor:
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
labelColor: theme.semantics.content.muted,
|
|
44
|
-
trackColor: checked ? theme.semantics.neutral.surfaceActive : theme.semantics.neutral.border,
|
|
38
|
+
backgroundColor: theme.semantics.surface.disabled,
|
|
39
|
+
borderColor: theme.semantics.border.subtle,
|
|
40
|
+
indicatorColor: checked ? theme.semantics.content.disabled : 'transparent',
|
|
41
|
+
labelColor: theme.semantics.content.disabled,
|
|
42
|
+
trackColor: theme.semantics.surface.disabled,
|
|
45
43
|
thumbColor: theme.semantics.surface.default,
|
|
46
|
-
opacity: 0.72,
|
|
47
44
|
};
|
|
48
45
|
}
|
|
49
46
|
|
|
@@ -71,12 +68,24 @@ export function resolveSelectionControlColors(
|
|
|
71
68
|
return {
|
|
72
69
|
backgroundColor: checked ? checkedBackgroundColor : uncheckedBackgroundColor,
|
|
73
70
|
borderColor: checked ? semanticTone.base : uncheckedBorderColor,
|
|
74
|
-
indicatorColor: checked
|
|
71
|
+
indicatorColor: checked
|
|
72
|
+
? pressed
|
|
73
|
+
? semanticTone.onStrongText
|
|
74
|
+
: hovered
|
|
75
|
+
? semanticTone.onHoverText
|
|
76
|
+
: semanticTone.onSolidText
|
|
77
|
+
: 'transparent',
|
|
75
78
|
labelColor: isInteractiveReadOnly
|
|
76
79
|
? theme.semantics.content.muted
|
|
77
80
|
: theme.semantics.content.default,
|
|
78
81
|
trackColor: checked ? checkedTrackColor : uncheckedBorderColor,
|
|
79
|
-
thumbColor: checked
|
|
82
|
+
thumbColor: checked
|
|
83
|
+
? pressed
|
|
84
|
+
? semanticTone.onStrongText
|
|
85
|
+
: hovered
|
|
86
|
+
? semanticTone.onHoverText
|
|
87
|
+
: semanticTone.onSolidText
|
|
88
|
+
: theme.semantics.surface.default,
|
|
80
89
|
opacity: isInteractiveReadOnly ? 0.88 : 1,
|
|
81
90
|
};
|
|
82
91
|
}
|
|
@@ -60,13 +60,8 @@ describe('portable icon rendering', () => {
|
|
|
60
60
|
|
|
61
61
|
describe('portable icon contract', () => {
|
|
62
62
|
test('publishes the intentional provider inventory', () => {
|
|
63
|
-
expect(SUPPORTED_ICON_PROVIDERS).
|
|
64
|
-
|
|
65
|
-
'FontAwesome',
|
|
66
|
-
'FontAwesome5',
|
|
67
|
-
'FontAwesome6',
|
|
68
|
-
'MaterialDesignIcons',
|
|
69
|
-
]);
|
|
63
|
+
expect(SUPPORTED_ICON_PROVIDERS.length).toBeGreaterThan(0);
|
|
64
|
+
expect(new Set(SUPPORTED_ICON_PROVIDERS).size).toBe(SUPPORTED_ICON_PROVIDERS.length);
|
|
70
65
|
});
|
|
71
66
|
});
|
|
72
67
|
|
package/src/surfaceColor.test.ts
CHANGED
|
@@ -20,16 +20,14 @@ describe('surfaceColor public exports', () => {
|
|
|
20
20
|
const indexSource = readFileSync(new URL('./index.ts', import.meta.url), 'utf8');
|
|
21
21
|
|
|
22
22
|
expect(indexSource).toContain("from './surfaceColor';");
|
|
23
|
-
expect(SURFACE_PALETTE_COLORS).
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
expect(SURFACE_COLORS).toEqual([...SURFACE_PALETTE_COLORS, ...SURFACE_STATUS_COLORS, 'danger']);
|
|
32
|
-
expect(SURFACE_EMPHASES).toEqual(['default', 'muted', 'subtle', 'inverse']);
|
|
23
|
+
expect(SURFACE_PALETTE_COLORS.length).toBeGreaterThan(0);
|
|
24
|
+
expect(SURFACE_STATUS_COLORS.length).toBeGreaterThan(0);
|
|
25
|
+
expect(SURFACE_EMPHASES.length).toBeGreaterThan(0);
|
|
26
|
+
expect(new Set(SURFACE_COLORS).size).toBe(SURFACE_COLORS.length);
|
|
27
|
+
expect(new Set(SURFACE_EMPHASES).size).toBe(SURFACE_EMPHASES.length);
|
|
28
|
+
for (const color of [...SURFACE_PALETTE_COLORS, ...SURFACE_STATUS_COLORS]) {
|
|
29
|
+
expect(SURFACE_COLORS).toContain(color);
|
|
30
|
+
}
|
|
33
31
|
});
|
|
34
32
|
|
|
35
33
|
it('keeps root-imported type contracts aligned with runtime lists', () => {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { SemanticColorToken } from '@ankhorage/color-theory';
|
|
2
2
|
import {
|
|
3
|
+
COLOR_HARMONIES,
|
|
3
4
|
DARK_SEMANTIC_COLOR_REFERENCES,
|
|
5
|
+
DEFAULT_SEMANTIC_STATUS_COLOR_SEEDS,
|
|
4
6
|
LIGHT_SEMANTIC_COLOR_REFERENCES,
|
|
5
7
|
} from '@ankhorage/color-theory';
|
|
6
8
|
import type { ThemeConfig } from '@ankhorage/contracts';
|
|
@@ -22,23 +24,18 @@ const mockConfig: ThemeConfig = {
|
|
|
22
24
|
},
|
|
23
25
|
};
|
|
24
26
|
|
|
25
|
-
function
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
expect(semantics.content.default).toBeDefined();
|
|
38
|
-
expect(semantics.border.default).toBeDefined();
|
|
39
|
-
expect(semantics.action.primary.base).toBeDefined();
|
|
40
|
-
expect(semantics.action.neutral.base).toBeDefined();
|
|
41
|
-
expect(semantics.action.danger.base).toBeDefined();
|
|
27
|
+
function expectCompleteSemanticValues(semantics: ThemeSemantics) {
|
|
28
|
+
visitSemanticValues(semantics);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function visitSemanticValues(value: object): void {
|
|
32
|
+
for (const entry of Object.values(value)) {
|
|
33
|
+
if (typeof entry === 'string') {
|
|
34
|
+
expect(entry.length).toBeGreaterThan(0);
|
|
35
|
+
} else {
|
|
36
|
+
visitSemanticValues(entry as object);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
42
39
|
}
|
|
43
40
|
|
|
44
41
|
describe('colorEngine', () => {
|
|
@@ -74,21 +71,30 @@ describe('colorEngine', () => {
|
|
|
74
71
|
expect(semantics.content.inverse).toBe(swatches.neutral[900]);
|
|
75
72
|
});
|
|
76
73
|
|
|
77
|
-
it('uses the
|
|
78
|
-
const
|
|
79
|
-
|
|
74
|
+
it('uses the Color Theory status owner independently from brand harmony', () => {
|
|
75
|
+
const statusConfig: ThemeConfig = {
|
|
76
|
+
...mockConfig,
|
|
77
|
+
light: { primaryColor: '#dc2626', harmony: 'monochromatic' },
|
|
78
|
+
dark: { primaryColor: '#dc2626', harmony: 'monochromatic' },
|
|
79
|
+
};
|
|
80
|
+
const light = generatePalette(statusConfig, 'light');
|
|
81
|
+
const dark = generatePalette(statusConfig, 'dark');
|
|
80
82
|
|
|
81
83
|
expect(light.semantics.danger.base).toBe('#ef4444');
|
|
82
84
|
expect(light.semantics.success.base).toBe('#22c55e');
|
|
83
85
|
expect(light.semantics.warning.base).toBe('#f59e0b');
|
|
84
86
|
expect(light.semantics.error.base).toBe(light.semantics.danger.base);
|
|
85
|
-
expect(light.semantics.info.base).
|
|
87
|
+
expect(light.semantics.info.base).toBe(DEFAULT_SEMANTIC_STATUS_COLOR_SEEDS.info);
|
|
88
|
+
expect(light.semantics.info.base).not.toBe(light.semantics.brand.base);
|
|
86
89
|
|
|
87
90
|
expect(dark.semantics.danger.base).toBe('#ef4444');
|
|
88
91
|
expect(dark.semantics.success.base).toBe('#22c55e');
|
|
89
92
|
expect(dark.semantics.warning.base).toBe('#f59e0b');
|
|
90
93
|
expect(dark.semantics.error.base).toBe(dark.semantics.danger.base);
|
|
91
|
-
expect(dark.semantics.info.base).
|
|
94
|
+
expect(dark.semantics.info.base).toBe(DEFAULT_SEMANTIC_STATUS_COLOR_SEEDS.info);
|
|
95
|
+
expect(dark.semantics.info.base).not.toBe(dark.semantics.brand.base);
|
|
96
|
+
expect(light.semantics.error).toBe(light.semantics.danger);
|
|
97
|
+
expect(dark.semantics.error).toBe(dark.semantics.danger);
|
|
92
98
|
});
|
|
93
99
|
|
|
94
100
|
it('uses mode-aware role semantics for dark mode soft states', () => {
|
|
@@ -136,16 +142,7 @@ describe('colorEngine', () => {
|
|
|
136
142
|
});
|
|
137
143
|
|
|
138
144
|
it('emits required semantic roles for all harmonies', () => {
|
|
139
|
-
const
|
|
140
|
-
'monochromatic',
|
|
141
|
-
'analogous',
|
|
142
|
-
'complementary',
|
|
143
|
-
'triadic',
|
|
144
|
-
'tetradic',
|
|
145
|
-
'splitComplementary',
|
|
146
|
-
] as const;
|
|
147
|
-
|
|
148
|
-
for (const harmony of harmonies) {
|
|
145
|
+
for (const harmony of COLOR_HARMONIES) {
|
|
149
146
|
const config = {
|
|
150
147
|
...mockConfig,
|
|
151
148
|
light: { ...mockConfig.light, harmony },
|
|
@@ -153,11 +150,65 @@ describe('colorEngine', () => {
|
|
|
153
150
|
};
|
|
154
151
|
const light = generatePalette(config, 'light');
|
|
155
152
|
const dark = generatePalette(config, 'dark');
|
|
156
|
-
|
|
157
|
-
|
|
153
|
+
expectCompleteSemanticValues(light.semantics);
|
|
154
|
+
expectCompleteSemanticValues(dark.semantics);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('resolves canonical semantic references and returns their selected steps', () => {
|
|
159
|
+
const light = generatePalette(mockConfig, 'light');
|
|
160
|
+
const dark = generatePalette(mockConfig, 'dark');
|
|
161
|
+
|
|
162
|
+
expect(light.colorDiagnostics.semanticReferences).toBe(LIGHT_SEMANTIC_COLOR_REFERENCES);
|
|
163
|
+
expect(dark.colorDiagnostics.semanticReferences).toBe(DARK_SEMANTIC_COLOR_REFERENCES);
|
|
164
|
+
expect(light.colorDiagnostics.generated.swatches).toBe(light.swatches);
|
|
165
|
+
expect(dark.colorDiagnostics.generated.swatches).toBe(dark.swatches);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('returns measured selections and contrast diagnostics for both modes', () => {
|
|
169
|
+
for (const mode of ['light', 'dark'] as const) {
|
|
170
|
+
const palette = generatePalette(mockConfig, mode);
|
|
171
|
+
|
|
172
|
+
expect(palette.colorDiagnostics.selections.length).toBeGreaterThan(0);
|
|
173
|
+
expect(
|
|
174
|
+
palette.colorDiagnostics.selections.every(({ result }) => result.selected !== null),
|
|
175
|
+
).toBe(true);
|
|
176
|
+
expect(palette.colorDiagnostics.contrasts.length).toBeGreaterThan(0);
|
|
177
|
+
expect(palette.colorDiagnostics.contrasts.every(({ passes }) => passes)).toBe(true);
|
|
178
|
+
expect(palette.colorDiagnostics.surfaceSeparation.length).toBeGreaterThan(0);
|
|
158
179
|
}
|
|
159
180
|
});
|
|
160
181
|
|
|
182
|
+
it('reports neutral and surface-separation evidence without hiding failed separation', () => {
|
|
183
|
+
const palette = generatePalette(mockConfig, 'light');
|
|
184
|
+
const failedSeparation = palette.colorDiagnostics.surfaceSeparation.find(
|
|
185
|
+
({ passes }) => !passes,
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
expect(palette.colorDiagnostics.generated.neutral.neutralKeyColor).toBe(
|
|
189
|
+
palette.swatches.neutral[500],
|
|
190
|
+
);
|
|
191
|
+
expect(palette.colorDiagnostics.generated.neutral.diagnostics.isUsable).toBeBoolean();
|
|
192
|
+
expect(failedSeparation).toBeDefined();
|
|
193
|
+
expect(failedSeparation?.contrast).toBeLessThan(failedSeparation?.minimumContrast ?? 0);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('preserves owner diagnostics for achromatic and low-chroma inputs', () => {
|
|
197
|
+
const achromaticConfig: ThemeConfig = {
|
|
198
|
+
...mockConfig,
|
|
199
|
+
light: { primaryColor: '#777777', harmony: 'square' },
|
|
200
|
+
};
|
|
201
|
+
const palette = generatePalette(achromaticConfig, 'light');
|
|
202
|
+
|
|
203
|
+
expect(palette.colorDiagnostics.generated.harmonyRoleColors.diagnostics.isHueReliable).toBe(
|
|
204
|
+
false,
|
|
205
|
+
);
|
|
206
|
+
expect(
|
|
207
|
+
palette.colorDiagnostics.generated.harmonyRoleColors.diagnostics.warnings.length,
|
|
208
|
+
).toBeGreaterThan(0);
|
|
209
|
+
expect(palette.colorDiagnostics.generated.neutral.diagnostics).toBeDefined();
|
|
210
|
+
});
|
|
211
|
+
|
|
161
212
|
it('generates ordinal chromatic role swatches (no accent/highlight as swatch keys)', () => {
|
|
162
213
|
const { swatches } = generatePalette(mockConfig, 'light');
|
|
163
214
|
const keys = Object.keys(swatches);
|