@ankhorage/zora 0.15.1 → 0.15.4
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 +18 -0
- package/dist/internal/color/index.d.ts +1 -0
- package/dist/internal/color/index.d.ts.map +1 -1
- package/dist/internal/color/index.js +1 -0
- package/dist/internal/color/index.js.map +1 -1
- package/dist/internal/color/semanticTokens.d.ts +28 -0
- package/dist/internal/color/semanticTokens.d.ts.map +1 -0
- package/dist/internal/color/semanticTokens.js +84 -0
- package/dist/internal/color/semanticTokens.js.map +1 -0
- package/package.json +2 -2
- package/src/internal/color/index.ts +5 -0
- package/src/internal/color/semanticTokens.test.ts +170 -0
- package/src/internal/color/semanticTokens.ts +114 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.15.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 32e7814: chore(release): trigger
|
|
8
|
+
|
|
9
|
+
## 0.15.3
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 4751b68: feat(theme): add internal semantic color token selection from role scales
|
|
14
|
+
|
|
15
|
+
## 0.15.2
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- e91aaf1: update @ankhorage/contracts
|
|
20
|
+
|
|
3
21
|
## 0.15.1
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
|
@@ -5,5 +5,6 @@ export { resolveModePrimaryColor } from './primary';
|
|
|
5
5
|
export { assignZoraHarmonyRoleHues, getZoraHueRoleAssignment, type ZoraComputedHueRoles, type ZoraHueRoleAssignment, type ZoraHueRoleId, } from './roleHues';
|
|
6
6
|
export { createZoraRoleColorScales, getZoraRoleColorScale, ZORA_COLOR_SCALE_ROLE_ORDER, type ZoraColorScaleRoleId, type ZoraComputedRoleColorScales, type ZoraRoleColorScale, } from './roleScales';
|
|
7
7
|
export { createZoraColorScale, type CreateZoraColorScaleOptions, type CreateZoraHueScaleOptions, createZoraNeutralScale, createZoraPrimaryScale, type ZoraHueScaleRoleId, } from './scales';
|
|
8
|
+
export { createZoraSemanticColorTokens, getReadableTextColor, type ZoraSemanticColorTokens, } from './semanticTokens';
|
|
8
9
|
export { ZORA_COLOR_SCALE_STEPS, type ZoraColorScale, type ZoraColorScaleStep } from './types';
|
|
9
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/internal/color/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,gCAAgC,EAChC,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EACxB,KAAK,8BAA8B,GACpC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,kBAAkB,EAClB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,iBAAiB,GACvB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,yBAAyB,EACzB,qBAAqB,EACrB,2BAA2B,EAC3B,KAAK,oBAAoB,EACzB,KAAK,2BAA2B,EAChC,KAAK,kBAAkB,GACxB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,oBAAoB,EACpB,KAAK,2BAA2B,EAChC,KAAK,yBAAyB,EAC9B,sBAAsB,EACtB,sBAAsB,EACtB,KAAK,kBAAkB,GACxB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,sBAAsB,EAAE,KAAK,cAAc,EAAE,KAAK,kBAAkB,EAAE,MAAM,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/internal/color/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,gCAAgC,EAChC,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EACxB,KAAK,8BAA8B,GACpC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,kBAAkB,EAClB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,iBAAiB,GACvB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EACL,yBAAyB,EACzB,wBAAwB,EACxB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,yBAAyB,EACzB,qBAAqB,EACrB,2BAA2B,EAC3B,KAAK,oBAAoB,EACzB,KAAK,2BAA2B,EAChC,KAAK,kBAAkB,GACxB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,oBAAoB,EACpB,KAAK,2BAA2B,EAChC,KAAK,yBAAyB,EAC9B,sBAAsB,EACtB,sBAAsB,EACtB,KAAK,kBAAkB,GACxB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,6BAA6B,EAC7B,oBAAoB,EACpB,KAAK,uBAAuB,GAC7B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,sBAAsB,EAAE,KAAK,cAAc,EAAE,KAAK,kBAAkB,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -5,5 +5,6 @@ export { resolveModePrimaryColor } from './primary';
|
|
|
5
5
|
export { assignZoraHarmonyRoleHues, getZoraHueRoleAssignment, } from './roleHues';
|
|
6
6
|
export { createZoraRoleColorScales, getZoraRoleColorScale, ZORA_COLOR_SCALE_ROLE_ORDER, } from './roleScales';
|
|
7
7
|
export { createZoraColorScale, createZoraNeutralScale, createZoraPrimaryScale, } from './scales';
|
|
8
|
+
export { createZoraSemanticColorTokens, getReadableTextColor, } from './semanticTokens';
|
|
8
9
|
export { ZORA_COLOR_SCALE_STEPS } from './types';
|
|
9
10
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/internal/color/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,gCAAgC,GAIjC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,kBAAkB,GAInB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EACL,yBAAyB,EACzB,wBAAwB,GAIzB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,yBAAyB,EACzB,qBAAqB,EACrB,2BAA2B,GAI5B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,oBAAoB,EAGpB,sBAAsB,EACtB,sBAAsB,GAEvB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,sBAAsB,EAAgD,MAAM,SAAS,CAAC","sourcesContent":["export {\n getZoraColorToneRecipe,\n getZoraColorToneRoleChromaFactor,\n type ZoraColorToneLaneRecipe,\n type ZoraColorToneRecipe,\n type ZoraColorToneRoleChromaFactors,\n} from './colorToneRecipes';\nexport {\n computeZoraHarmony,\n type ZoraComputedHarmony,\n type ZoraHarmonySlot,\n type ZoraHarmonySlotId,\n} from './harmony';\nexport { parseHexToOklch } from './oklch';\nexport { resolveModePrimaryColor } from './primary';\nexport {\n assignZoraHarmonyRoleHues,\n getZoraHueRoleAssignment,\n type ZoraComputedHueRoles,\n type ZoraHueRoleAssignment,\n type ZoraHueRoleId,\n} from './roleHues';\nexport {\n createZoraRoleColorScales,\n getZoraRoleColorScale,\n ZORA_COLOR_SCALE_ROLE_ORDER,\n type ZoraColorScaleRoleId,\n type ZoraComputedRoleColorScales,\n type ZoraRoleColorScale,\n} from './roleScales';\nexport {\n createZoraColorScale,\n type CreateZoraColorScaleOptions,\n type CreateZoraHueScaleOptions,\n createZoraNeutralScale,\n createZoraPrimaryScale,\n type ZoraHueScaleRoleId,\n} from './scales';\nexport { ZORA_COLOR_SCALE_STEPS, type ZoraColorScale, type ZoraColorScaleStep } from './types';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/internal/color/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,gCAAgC,GAIjC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,kBAAkB,GAInB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EACL,yBAAyB,EACzB,wBAAwB,GAIzB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,yBAAyB,EACzB,qBAAqB,EACrB,2BAA2B,GAI5B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,oBAAoB,EAGpB,sBAAsB,EACtB,sBAAsB,GAEvB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,6BAA6B,EAC7B,oBAAoB,GAErB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,sBAAsB,EAAgD,MAAM,SAAS,CAAC","sourcesContent":["export {\n getZoraColorToneRecipe,\n getZoraColorToneRoleChromaFactor,\n type ZoraColorToneLaneRecipe,\n type ZoraColorToneRecipe,\n type ZoraColorToneRoleChromaFactors,\n} from './colorToneRecipes';\nexport {\n computeZoraHarmony,\n type ZoraComputedHarmony,\n type ZoraHarmonySlot,\n type ZoraHarmonySlotId,\n} from './harmony';\nexport { parseHexToOklch } from './oklch';\nexport { resolveModePrimaryColor } from './primary';\nexport {\n assignZoraHarmonyRoleHues,\n getZoraHueRoleAssignment,\n type ZoraComputedHueRoles,\n type ZoraHueRoleAssignment,\n type ZoraHueRoleId,\n} from './roleHues';\nexport {\n createZoraRoleColorScales,\n getZoraRoleColorScale,\n ZORA_COLOR_SCALE_ROLE_ORDER,\n type ZoraColorScaleRoleId,\n type ZoraComputedRoleColorScales,\n type ZoraRoleColorScale,\n} from './roleScales';\nexport {\n createZoraColorScale,\n type CreateZoraColorScaleOptions,\n type CreateZoraHueScaleOptions,\n createZoraNeutralScale,\n createZoraPrimaryScale,\n type ZoraHueScaleRoleId,\n} from './scales';\nexport {\n createZoraSemanticColorTokens,\n getReadableTextColor,\n type ZoraSemanticColorTokens,\n} from './semanticTokens';\nexport { ZORA_COLOR_SCALE_STEPS, type ZoraColorScale, type ZoraColorScaleStep } from './types';\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ZoraColorTone, ZoraHexColor, ZoraThemeMode } from '../../theme/types';
|
|
2
|
+
import { type ZoraComputedRoleColorScales } from './roleScales';
|
|
3
|
+
export interface ZoraSemanticColorTokens {
|
|
4
|
+
background: ZoraHexColor;
|
|
5
|
+
surface: ZoraHexColor;
|
|
6
|
+
surfaceRaised: ZoraHexColor;
|
|
7
|
+
surfaceTint: ZoraHexColor;
|
|
8
|
+
border: ZoraHexColor;
|
|
9
|
+
text: ZoraHexColor;
|
|
10
|
+
textMuted: ZoraHexColor;
|
|
11
|
+
primary: ZoraHexColor;
|
|
12
|
+
secondary: ZoraHexColor;
|
|
13
|
+
accent: ZoraHexColor;
|
|
14
|
+
highlight: ZoraHexColor;
|
|
15
|
+
onPrimary: ZoraHexColor;
|
|
16
|
+
onAccent: ZoraHexColor;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Selects the more readable of two candidate hex colors against a given background,
|
|
20
|
+
* using OKLCH lightness as a simple contrast proxy.
|
|
21
|
+
*/
|
|
22
|
+
export declare function getReadableTextColor(background: ZoraHexColor, candidates: readonly [ZoraHexColor, ZoraHexColor]): ZoraHexColor;
|
|
23
|
+
export declare function createZoraSemanticColorTokens(options: {
|
|
24
|
+
roleScales: ZoraComputedRoleColorScales;
|
|
25
|
+
mode: ZoraThemeMode;
|
|
26
|
+
colorTone: ZoraColorTone;
|
|
27
|
+
}): ZoraSemanticColorTokens;
|
|
28
|
+
//# sourceMappingURL=semanticTokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semanticTokens.d.ts","sourceRoot":"","sources":["../../../src/internal/color/semanticTokens.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEpF,OAAO,EAAyB,KAAK,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAEvF,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,YAAY,CAAC;IACzB,OAAO,EAAE,YAAY,CAAC;IACtB,aAAa,EAAE,YAAY,CAAC;IAC5B,WAAW,EAAE,YAAY,CAAC;IAC1B,MAAM,EAAE,YAAY,CAAC;IACrB,IAAI,EAAE,YAAY,CAAC;IACnB,SAAS,EAAE,YAAY,CAAC;IACxB,OAAO,EAAE,YAAY,CAAC;IACtB,SAAS,EAAE,YAAY,CAAC;IACxB,MAAM,EAAE,YAAY,CAAC;IACrB,SAAS,EAAE,YAAY,CAAC;IACxB,SAAS,EAAE,YAAY,CAAC;IACxB,QAAQ,EAAE,YAAY,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,YAAY,EACxB,UAAU,EAAE,SAAS,CAAC,YAAY,EAAE,YAAY,CAAC,GAChD,YAAY,CAMd;AAED,wBAAgB,6BAA6B,CAAC,OAAO,EAAE;IACrD,UAAU,EAAE,2BAA2B,CAAC;IACxC,IAAI,EAAE,aAAa,CAAC;IACpB,SAAS,EAAE,aAAa,CAAC;CAC1B,GAAG,uBAAuB,CA0E1B"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { parseHexToOklch } from './oklch';
|
|
2
|
+
import { getZoraRoleColorScale } from './roleScales';
|
|
3
|
+
/**
|
|
4
|
+
* Selects the more readable of two candidate hex colors against a given background,
|
|
5
|
+
* using OKLCH lightness as a simple contrast proxy.
|
|
6
|
+
*/
|
|
7
|
+
export function getReadableTextColor(background, candidates) {
|
|
8
|
+
const bgL = parseHexToOklch(background).l;
|
|
9
|
+
const [a, b] = candidates;
|
|
10
|
+
const diffA = Math.abs(parseHexToOklch(a).l - bgL);
|
|
11
|
+
const diffB = Math.abs(parseHexToOklch(b).l - bgL);
|
|
12
|
+
return diffA >= diffB ? a : b;
|
|
13
|
+
}
|
|
14
|
+
export function createZoraSemanticColorTokens(options) {
|
|
15
|
+
// colorTone is accepted now and reserved for future per-tone step overrides
|
|
16
|
+
// (e.g. obsidian/pastel may shift surface selections differently).
|
|
17
|
+
const { roleScales, mode } = options;
|
|
18
|
+
const neutral = getZoraRoleColorScale(roleScales, 'neutral').scale;
|
|
19
|
+
const surfaceTintScale = getZoraRoleColorScale(roleScales, 'surfaceTint').scale;
|
|
20
|
+
const primaryScale = getZoraRoleColorScale(roleScales, 'primary').scale;
|
|
21
|
+
const secondaryScale = getZoraRoleColorScale(roleScales, 'secondary').scale;
|
|
22
|
+
const accentScale = getZoraRoleColorScale(roleScales, 'accent').scale;
|
|
23
|
+
const highlightScale = getZoraRoleColorScale(roleScales, 'highlight').scale;
|
|
24
|
+
if (mode === 'light') {
|
|
25
|
+
const background = neutral[50];
|
|
26
|
+
const surface = neutral[100];
|
|
27
|
+
const surfaceRaised = neutral[50];
|
|
28
|
+
const surfaceTint = surfaceTintScale[100];
|
|
29
|
+
const border = neutral[200];
|
|
30
|
+
const text = neutral[900];
|
|
31
|
+
const textMuted = neutral[700];
|
|
32
|
+
const primary = primaryScale[600];
|
|
33
|
+
const secondary = secondaryScale[600];
|
|
34
|
+
const accent = accentScale[600];
|
|
35
|
+
const highlight = highlightScale[600];
|
|
36
|
+
const onPrimary = getReadableTextColor(primary, [neutral[50], neutral[950]]);
|
|
37
|
+
const onAccent = getReadableTextColor(accent, [neutral[50], neutral[950]]);
|
|
38
|
+
return {
|
|
39
|
+
background,
|
|
40
|
+
surface,
|
|
41
|
+
surfaceRaised,
|
|
42
|
+
surfaceTint,
|
|
43
|
+
border,
|
|
44
|
+
text,
|
|
45
|
+
textMuted,
|
|
46
|
+
primary,
|
|
47
|
+
secondary,
|
|
48
|
+
accent,
|
|
49
|
+
highlight,
|
|
50
|
+
onPrimary,
|
|
51
|
+
onAccent,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
// dark mode
|
|
55
|
+
const background = neutral[950];
|
|
56
|
+
const surface = neutral[900];
|
|
57
|
+
const surfaceRaised = neutral[800];
|
|
58
|
+
const surfaceTint = surfaceTintScale[900];
|
|
59
|
+
const border = neutral[700];
|
|
60
|
+
const text = neutral[50];
|
|
61
|
+
const textMuted = neutral[300];
|
|
62
|
+
const primary = primaryScale[400];
|
|
63
|
+
const secondary = secondaryScale[400];
|
|
64
|
+
const accent = accentScale[400];
|
|
65
|
+
const highlight = highlightScale[400];
|
|
66
|
+
const onPrimary = getReadableTextColor(primary, [neutral[50], neutral[950]]);
|
|
67
|
+
const onAccent = getReadableTextColor(accent, [neutral[50], neutral[950]]);
|
|
68
|
+
return {
|
|
69
|
+
background,
|
|
70
|
+
surface,
|
|
71
|
+
surfaceRaised,
|
|
72
|
+
surfaceTint,
|
|
73
|
+
border,
|
|
74
|
+
text,
|
|
75
|
+
textMuted,
|
|
76
|
+
primary,
|
|
77
|
+
secondary,
|
|
78
|
+
accent,
|
|
79
|
+
highlight,
|
|
80
|
+
onPrimary,
|
|
81
|
+
onAccent,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=semanticTokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semanticTokens.js","sourceRoot":"","sources":["../../../src/internal/color/semanticTokens.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,qBAAqB,EAAoC,MAAM,cAAc,CAAC;AAkBvF;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAClC,UAAwB,EACxB,UAAiD;IAEjD,MAAM,GAAG,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAC1C,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,UAAU,CAAC;IAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;IACnD,OAAO,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,6BAA6B,CAAC,OAI7C;IACC,4EAA4E;IAC5E,mEAAmE;IACnE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;IAErC,MAAM,OAAO,GAAG,qBAAqB,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC,KAAK,CAAC;IACnE,MAAM,gBAAgB,GAAG,qBAAqB,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC,KAAK,CAAC;IAChF,MAAM,YAAY,GAAG,qBAAqB,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC,KAAK,CAAC;IACxE,MAAM,cAAc,GAAG,qBAAqB,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,KAAK,CAAC;IAC5E,MAAM,WAAW,GAAG,qBAAqB,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,KAAK,CAAC;IACtE,MAAM,cAAc,GAAG,qBAAqB,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,KAAK,CAAC;IAE5E,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,MAAM,UAAU,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC;QAC/B,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,aAAa,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC;QAClC,MAAM,WAAW,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC1B,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC/B,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;QAClC,MAAM,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;QACtC,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;QAChC,MAAM,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;QACtC,MAAM,SAAS,GAAG,oBAAoB,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC7E,MAAM,QAAQ,GAAG,oBAAoB,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAE3E,OAAO;YACL,UAAU;YACV,OAAO;YACP,aAAa;YACb,WAAW;YACX,MAAM;YACN,IAAI;YACJ,SAAS;YACT,OAAO;YACP,SAAS;YACT,MAAM;YACN,SAAS;YACT,SAAS;YACT,QAAQ;SACT,CAAC;IACJ,CAAC;IAED,YAAY;IACZ,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAChC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7B,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACnC,MAAM,WAAW,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,IAAI,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC;IACzB,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IAChC,MAAM,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACtC,MAAM,SAAS,GAAG,oBAAoB,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC7E,MAAM,QAAQ,GAAG,oBAAoB,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAE3E,OAAO;QACL,UAAU;QACV,OAAO;QACP,aAAa;QACb,WAAW;QACX,MAAM;QACN,IAAI;QACJ,SAAS;QACT,OAAO;QACP,SAAS;QACT,MAAM;QACN,SAAS;QACT,SAAS;QACT,QAAQ;KACT,CAAC;AACJ,CAAC","sourcesContent":["import type { ZoraColorTone, ZoraHexColor, ZoraThemeMode } from '../../theme/types';\nimport { parseHexToOklch } from './oklch';\nimport { getZoraRoleColorScale, type ZoraComputedRoleColorScales } from './roleScales';\n\nexport interface ZoraSemanticColorTokens {\n background: ZoraHexColor;\n surface: ZoraHexColor;\n surfaceRaised: ZoraHexColor;\n surfaceTint: ZoraHexColor;\n border: ZoraHexColor;\n text: ZoraHexColor;\n textMuted: ZoraHexColor;\n primary: ZoraHexColor;\n secondary: ZoraHexColor;\n accent: ZoraHexColor;\n highlight: ZoraHexColor;\n onPrimary: ZoraHexColor;\n onAccent: ZoraHexColor;\n}\n\n/**\n * Selects the more readable of two candidate hex colors against a given background,\n * using OKLCH lightness as a simple contrast proxy.\n */\nexport function getReadableTextColor(\n background: ZoraHexColor,\n candidates: readonly [ZoraHexColor, ZoraHexColor],\n): ZoraHexColor {\n const bgL = parseHexToOklch(background).l;\n const [a, b] = candidates;\n const diffA = Math.abs(parseHexToOklch(a).l - bgL);\n const diffB = Math.abs(parseHexToOklch(b).l - bgL);\n return diffA >= diffB ? a : b;\n}\n\nexport function createZoraSemanticColorTokens(options: {\n roleScales: ZoraComputedRoleColorScales;\n mode: ZoraThemeMode;\n colorTone: ZoraColorTone;\n}): ZoraSemanticColorTokens {\n // colorTone is accepted now and reserved for future per-tone step overrides\n // (e.g. obsidian/pastel may shift surface selections differently).\n const { roleScales, mode } = options;\n\n const neutral = getZoraRoleColorScale(roleScales, 'neutral').scale;\n const surfaceTintScale = getZoraRoleColorScale(roleScales, 'surfaceTint').scale;\n const primaryScale = getZoraRoleColorScale(roleScales, 'primary').scale;\n const secondaryScale = getZoraRoleColorScale(roleScales, 'secondary').scale;\n const accentScale = getZoraRoleColorScale(roleScales, 'accent').scale;\n const highlightScale = getZoraRoleColorScale(roleScales, 'highlight').scale;\n\n if (mode === 'light') {\n const background = neutral[50];\n const surface = neutral[100];\n const surfaceRaised = neutral[50];\n const surfaceTint = surfaceTintScale[100];\n const border = neutral[200];\n const text = neutral[900];\n const textMuted = neutral[700];\n const primary = primaryScale[600];\n const secondary = secondaryScale[600];\n const accent = accentScale[600];\n const highlight = highlightScale[600];\n const onPrimary = getReadableTextColor(primary, [neutral[50], neutral[950]]);\n const onAccent = getReadableTextColor(accent, [neutral[50], neutral[950]]);\n\n return {\n background,\n surface,\n surfaceRaised,\n surfaceTint,\n border,\n text,\n textMuted,\n primary,\n secondary,\n accent,\n highlight,\n onPrimary,\n onAccent,\n };\n }\n\n // dark mode\n const background = neutral[950];\n const surface = neutral[900];\n const surfaceRaised = neutral[800];\n const surfaceTint = surfaceTintScale[900];\n const border = neutral[700];\n const text = neutral[50];\n const textMuted = neutral[300];\n const primary = primaryScale[400];\n const secondary = secondaryScale[400];\n const accent = accentScale[400];\n const highlight = highlightScale[400];\n const onPrimary = getReadableTextColor(primary, [neutral[50], neutral[950]]);\n const onAccent = getReadableTextColor(accent, [neutral[50], neutral[950]]);\n\n return {\n background,\n surface,\n surfaceRaised,\n surfaceTint,\n border,\n text,\n textMuted,\n primary,\n secondary,\n accent,\n highlight,\n onPrimary,\n onAccent,\n };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ankhorage/zora",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.15.
|
|
4
|
+
"version": "0.15.4",
|
|
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,7 +43,7 @@
|
|
|
43
43
|
}
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@ankhorage/surface": "^0.2.
|
|
46
|
+
"@ankhorage/surface": "^0.2.2",
|
|
47
47
|
"culori": "^4.0.2"
|
|
48
48
|
},
|
|
49
49
|
"files": [
|
|
@@ -36,4 +36,9 @@ export {
|
|
|
36
36
|
createZoraPrimaryScale,
|
|
37
37
|
type ZoraHueScaleRoleId,
|
|
38
38
|
} from './scales';
|
|
39
|
+
export {
|
|
40
|
+
createZoraSemanticColorTokens,
|
|
41
|
+
getReadableTextColor,
|
|
42
|
+
type ZoraSemanticColorTokens,
|
|
43
|
+
} from './semanticTokens';
|
|
39
44
|
export { ZORA_COLOR_SCALE_STEPS, type ZoraColorScale, type ZoraColorScaleStep } from './types';
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
|
|
3
|
+
import type { ZoraColorTone, ZoraHexColor } from '../../theme/types';
|
|
4
|
+
import {
|
|
5
|
+
assignZoraHarmonyRoleHues,
|
|
6
|
+
computeZoraHarmony,
|
|
7
|
+
createZoraRoleColorScales,
|
|
8
|
+
createZoraSemanticColorTokens,
|
|
9
|
+
getReadableTextColor,
|
|
10
|
+
type ZoraComputedRoleColorScales,
|
|
11
|
+
type ZoraSemanticColorTokens,
|
|
12
|
+
} from './index';
|
|
13
|
+
|
|
14
|
+
function isSixDigitLowercaseHex(value: string): value is ZoraHexColor {
|
|
15
|
+
return /^#[0-9a-f]{6}$/.test(value);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getLightness(hex: ZoraHexColor): number {
|
|
19
|
+
// Intentionally a simple RGB average rather than OKLCH — serves as a
|
|
20
|
+
// perceptual proxy for test assertions only. The production implementation
|
|
21
|
+
// in getReadableTextColor uses OKLCH lightness via parseHexToOklch.
|
|
22
|
+
const r = parseInt(hex.slice(1, 3), 16) / 255;
|
|
23
|
+
const g = parseInt(hex.slice(3, 5), 16) / 255;
|
|
24
|
+
const b = parseInt(hex.slice(5, 7), 16) / 255;
|
|
25
|
+
return (r + g + b) / 3;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function buildRoleScales(colorTone: ZoraColorTone = 'jewel'): ZoraComputedRoleColorScales {
|
|
29
|
+
const seed: ZoraHexColor = '#0f766e';
|
|
30
|
+
const harmony = computeZoraHarmony(seed, 'tetradic');
|
|
31
|
+
const hueRoles = assignZoraHarmonyRoleHues(harmony);
|
|
32
|
+
return createZoraRoleColorScales({ colorTone, hueRoles, seed });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function buildLight(colorTone: ZoraColorTone = 'jewel'): ZoraSemanticColorTokens {
|
|
36
|
+
return createZoraSemanticColorTokens({
|
|
37
|
+
roleScales: buildRoleScales(colorTone),
|
|
38
|
+
mode: 'light',
|
|
39
|
+
colorTone,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function buildDark(colorTone: ZoraColorTone = 'jewel'): ZoraSemanticColorTokens {
|
|
44
|
+
return createZoraSemanticColorTokens({
|
|
45
|
+
roleScales: buildRoleScales(colorTone),
|
|
46
|
+
mode: 'dark',
|
|
47
|
+
colorTone,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
describe('createZoraSemanticColorTokens', () => {
|
|
52
|
+
test('all output values are lowercase 6-digit hex', () => {
|
|
53
|
+
const light = buildLight();
|
|
54
|
+
const dark = buildDark();
|
|
55
|
+
|
|
56
|
+
for (const tokens of [light, dark]) {
|
|
57
|
+
for (const key of Object.keys(tokens) as (keyof ZoraSemanticColorTokens)[]) {
|
|
58
|
+
const value = tokens[key];
|
|
59
|
+
expect(isSixDigitLowercaseHex(value), `${key} must be lowercase 6-digit hex`).toBe(true);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('light mode background is lighter than dark mode background', () => {
|
|
65
|
+
const light = buildLight();
|
|
66
|
+
const dark = buildDark();
|
|
67
|
+
|
|
68
|
+
expect(getLightness(light.background)).toBeGreaterThan(getLightness(dark.background));
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('text has opposite lightness direction from background (light mode)', () => {
|
|
72
|
+
const light = buildLight();
|
|
73
|
+
|
|
74
|
+
expect(getLightness(light.text)).toBeLessThan(getLightness(light.background));
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('text has opposite lightness direction from background (dark mode)', () => {
|
|
78
|
+
const dark = buildDark();
|
|
79
|
+
|
|
80
|
+
expect(getLightness(dark.text)).toBeGreaterThan(getLightness(dark.background));
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('primary, accent, and highlight are distinct from background and surface (light)', () => {
|
|
84
|
+
const light = buildLight();
|
|
85
|
+
|
|
86
|
+
for (const token of [light.primary, light.accent, light.highlight] as const) {
|
|
87
|
+
expect(token).not.toBe(light.background);
|
|
88
|
+
expect(token).not.toBe(light.surface);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('primary, accent, and highlight are distinct from background and surface (dark)', () => {
|
|
93
|
+
const dark = buildDark();
|
|
94
|
+
|
|
95
|
+
for (const token of [dark.primary, dark.accent, dark.highlight] as const) {
|
|
96
|
+
expect(token).not.toBe(dark.background);
|
|
97
|
+
expect(token).not.toBe(dark.surface);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test('onPrimary is readable against primary (light mode)', () => {
|
|
102
|
+
const light = buildLight();
|
|
103
|
+
const primaryL = getLightness(light.primary);
|
|
104
|
+
const onPrimaryL = getLightness(light.onPrimary);
|
|
105
|
+
|
|
106
|
+
expect(Math.abs(primaryL - onPrimaryL)).toBeGreaterThan(0.3);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('onPrimary is readable against primary (dark mode)', () => {
|
|
110
|
+
const dark = buildDark();
|
|
111
|
+
const primaryL = getLightness(dark.primary);
|
|
112
|
+
const onPrimaryL = getLightness(dark.onPrimary);
|
|
113
|
+
|
|
114
|
+
expect(Math.abs(primaryL - onPrimaryL)).toBeGreaterThan(0.3);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('onAccent is readable against accent (light mode)', () => {
|
|
118
|
+
const light = buildLight();
|
|
119
|
+
const accentL = getLightness(light.accent);
|
|
120
|
+
const onAccentL = getLightness(light.onAccent);
|
|
121
|
+
|
|
122
|
+
expect(Math.abs(accentL - onAccentL)).toBeGreaterThan(0.3);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test('colorTone changes at least one output token between neutral and fluorescent', () => {
|
|
126
|
+
const lightNeutral = buildLight('neutral');
|
|
127
|
+
const lightFluorescent = buildLight('fluorescent');
|
|
128
|
+
|
|
129
|
+
const tokens: (keyof ZoraSemanticColorTokens)[] = [
|
|
130
|
+
'primary',
|
|
131
|
+
'secondary',
|
|
132
|
+
'accent',
|
|
133
|
+
'highlight',
|
|
134
|
+
];
|
|
135
|
+
|
|
136
|
+
const anyChanged = tokens.some((k) => lightNeutral[k] !== lightFluorescent[k]);
|
|
137
|
+
expect(anyChanged).toBe(true);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('output is deterministic for same input', () => {
|
|
141
|
+
const first = buildLight();
|
|
142
|
+
const second = buildLight();
|
|
143
|
+
|
|
144
|
+
expect(first).toEqual(second);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test('surface is distinct from surfaceRaised in dark mode', () => {
|
|
148
|
+
const dark = buildDark();
|
|
149
|
+
|
|
150
|
+
expect(dark.surface).not.toBe(dark.surfaceRaised);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
describe('getReadableTextColor', () => {
|
|
155
|
+
test('returns the candidate with greater lightness difference from background', () => {
|
|
156
|
+
const background: ZoraHexColor = '#ffffff';
|
|
157
|
+
const dark: ZoraHexColor = '#111111';
|
|
158
|
+
const light: ZoraHexColor = '#eeeeee';
|
|
159
|
+
|
|
160
|
+
expect(getReadableTextColor(background, [dark, light])).toBe(dark);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test('returns the lighter candidate when background is dark', () => {
|
|
164
|
+
const background: ZoraHexColor = '#111111';
|
|
165
|
+
const dark: ZoraHexColor = '#333333';
|
|
166
|
+
const light: ZoraHexColor = '#eeeeee';
|
|
167
|
+
|
|
168
|
+
expect(getReadableTextColor(background, [dark, light])).toBe(light);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { ZoraColorTone, ZoraHexColor, ZoraThemeMode } from '../../theme/types';
|
|
2
|
+
import { parseHexToOklch } from './oklch';
|
|
3
|
+
import { getZoraRoleColorScale, type ZoraComputedRoleColorScales } from './roleScales';
|
|
4
|
+
|
|
5
|
+
export interface ZoraSemanticColorTokens {
|
|
6
|
+
background: ZoraHexColor;
|
|
7
|
+
surface: ZoraHexColor;
|
|
8
|
+
surfaceRaised: ZoraHexColor;
|
|
9
|
+
surfaceTint: ZoraHexColor;
|
|
10
|
+
border: ZoraHexColor;
|
|
11
|
+
text: ZoraHexColor;
|
|
12
|
+
textMuted: ZoraHexColor;
|
|
13
|
+
primary: ZoraHexColor;
|
|
14
|
+
secondary: ZoraHexColor;
|
|
15
|
+
accent: ZoraHexColor;
|
|
16
|
+
highlight: ZoraHexColor;
|
|
17
|
+
onPrimary: ZoraHexColor;
|
|
18
|
+
onAccent: ZoraHexColor;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Selects the more readable of two candidate hex colors against a given background,
|
|
23
|
+
* using OKLCH lightness as a simple contrast proxy.
|
|
24
|
+
*/
|
|
25
|
+
export function getReadableTextColor(
|
|
26
|
+
background: ZoraHexColor,
|
|
27
|
+
candidates: readonly [ZoraHexColor, ZoraHexColor],
|
|
28
|
+
): ZoraHexColor {
|
|
29
|
+
const bgL = parseHexToOklch(background).l;
|
|
30
|
+
const [a, b] = candidates;
|
|
31
|
+
const diffA = Math.abs(parseHexToOklch(a).l - bgL);
|
|
32
|
+
const diffB = Math.abs(parseHexToOklch(b).l - bgL);
|
|
33
|
+
return diffA >= diffB ? a : b;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function createZoraSemanticColorTokens(options: {
|
|
37
|
+
roleScales: ZoraComputedRoleColorScales;
|
|
38
|
+
mode: ZoraThemeMode;
|
|
39
|
+
colorTone: ZoraColorTone;
|
|
40
|
+
}): ZoraSemanticColorTokens {
|
|
41
|
+
// colorTone is accepted now and reserved for future per-tone step overrides
|
|
42
|
+
// (e.g. obsidian/pastel may shift surface selections differently).
|
|
43
|
+
const { roleScales, mode } = options;
|
|
44
|
+
|
|
45
|
+
const neutral = getZoraRoleColorScale(roleScales, 'neutral').scale;
|
|
46
|
+
const surfaceTintScale = getZoraRoleColorScale(roleScales, 'surfaceTint').scale;
|
|
47
|
+
const primaryScale = getZoraRoleColorScale(roleScales, 'primary').scale;
|
|
48
|
+
const secondaryScale = getZoraRoleColorScale(roleScales, 'secondary').scale;
|
|
49
|
+
const accentScale = getZoraRoleColorScale(roleScales, 'accent').scale;
|
|
50
|
+
const highlightScale = getZoraRoleColorScale(roleScales, 'highlight').scale;
|
|
51
|
+
|
|
52
|
+
if (mode === 'light') {
|
|
53
|
+
const background = neutral[50];
|
|
54
|
+
const surface = neutral[100];
|
|
55
|
+
const surfaceRaised = neutral[50];
|
|
56
|
+
const surfaceTint = surfaceTintScale[100];
|
|
57
|
+
const border = neutral[200];
|
|
58
|
+
const text = neutral[900];
|
|
59
|
+
const textMuted = neutral[700];
|
|
60
|
+
const primary = primaryScale[600];
|
|
61
|
+
const secondary = secondaryScale[600];
|
|
62
|
+
const accent = accentScale[600];
|
|
63
|
+
const highlight = highlightScale[600];
|
|
64
|
+
const onPrimary = getReadableTextColor(primary, [neutral[50], neutral[950]]);
|
|
65
|
+
const onAccent = getReadableTextColor(accent, [neutral[50], neutral[950]]);
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
background,
|
|
69
|
+
surface,
|
|
70
|
+
surfaceRaised,
|
|
71
|
+
surfaceTint,
|
|
72
|
+
border,
|
|
73
|
+
text,
|
|
74
|
+
textMuted,
|
|
75
|
+
primary,
|
|
76
|
+
secondary,
|
|
77
|
+
accent,
|
|
78
|
+
highlight,
|
|
79
|
+
onPrimary,
|
|
80
|
+
onAccent,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// dark mode
|
|
85
|
+
const background = neutral[950];
|
|
86
|
+
const surface = neutral[900];
|
|
87
|
+
const surfaceRaised = neutral[800];
|
|
88
|
+
const surfaceTint = surfaceTintScale[900];
|
|
89
|
+
const border = neutral[700];
|
|
90
|
+
const text = neutral[50];
|
|
91
|
+
const textMuted = neutral[300];
|
|
92
|
+
const primary = primaryScale[400];
|
|
93
|
+
const secondary = secondaryScale[400];
|
|
94
|
+
const accent = accentScale[400];
|
|
95
|
+
const highlight = highlightScale[400];
|
|
96
|
+
const onPrimary = getReadableTextColor(primary, [neutral[50], neutral[950]]);
|
|
97
|
+
const onAccent = getReadableTextColor(accent, [neutral[50], neutral[950]]);
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
background,
|
|
101
|
+
surface,
|
|
102
|
+
surfaceRaised,
|
|
103
|
+
surfaceTint,
|
|
104
|
+
border,
|
|
105
|
+
text,
|
|
106
|
+
textMuted,
|
|
107
|
+
primary,
|
|
108
|
+
secondary,
|
|
109
|
+
accent,
|
|
110
|
+
highlight,
|
|
111
|
+
onPrimary,
|
|
112
|
+
onAccent,
|
|
113
|
+
};
|
|
114
|
+
}
|