@sanity/ui 2.0.0-alpha.34 → 2.0.0-alpha.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/dist/index.cjs.mjs +4 -3
  2. package/dist/index.d.ts +29 -22
  3. package/dist/index.esm.js +397 -518
  4. package/dist/index.esm.js.map +1 -1
  5. package/dist/index.js +413 -534
  6. package/dist/index.js.map +1 -1
  7. package/dist/theme.cjs.mjs +10 -4
  8. package/dist/theme.d.ts +489 -201
  9. package/dist/theme.esm.js +2934 -2748
  10. package/dist/theme.esm.js.map +1 -1
  11. package/dist/theme.js +2943 -2751
  12. package/dist/theme.js.map +1 -1
  13. package/package.json +3 -3
  14. package/src/core/_compat.ts +7 -7
  15. package/src/core/components/dialog/dialog.tsx +5 -5
  16. package/src/core/components/dialog/styles.ts +4 -5
  17. package/src/core/components/hotkeys/hotkeys.tsx +4 -2
  18. package/src/core/components/menu/__workshop__/avatarMenu.tsx +50 -0
  19. package/src/core/components/menu/__workshop__/index.ts +5 -0
  20. package/src/core/components/menu/menuItem.tsx +7 -1
  21. package/src/core/components/skeleton/textSkeleton.tsx +12 -14
  22. package/src/core/components/tree/style.ts +14 -17
  23. package/src/core/hooks/useMediaIndex/useMediaIndex.test.tsx +3 -3
  24. package/src/core/hooks/useMediaIndex/useMediaIndex.ts +2 -3
  25. package/src/core/primitives/_selectable/style.ts +18 -18
  26. package/src/core/primitives/avatar/avatar.tsx +3 -3
  27. package/src/core/primitives/avatar/avatarCounter.tsx +4 -4
  28. package/src/core/primitives/avatar/avatarStack.tsx +2 -2
  29. package/src/core/primitives/avatar/styles.ts +7 -8
  30. package/src/core/primitives/avatar/types.ts +2 -2
  31. package/src/core/primitives/badge/__workshop__/props.tsx +1 -1
  32. package/src/core/primitives/badge/__workshop__/tones.tsx +1 -1
  33. package/src/core/primitives/badge/badge.tsx +1 -1
  34. package/src/core/primitives/badge/styles.ts +5 -6
  35. package/src/core/primitives/button/button.tsx +3 -4
  36. package/src/core/primitives/button/styles.ts +22 -23
  37. package/src/core/primitives/card/__workshop__/selected.tsx +15 -15
  38. package/src/core/primitives/card/card.tsx +1 -1
  39. package/src/core/primitives/card/styles.ts +22 -22
  40. package/src/core/primitives/checkbox/styles.ts +21 -22
  41. package/src/core/primitives/code/styles.ts +4 -1
  42. package/src/core/primitives/container/styles.ts +2 -3
  43. package/src/core/primitives/heading/styles.ts +4 -2
  44. package/src/core/primitives/inline/styles.ts +6 -6
  45. package/src/core/primitives/kbd/kbd.tsx +7 -11
  46. package/src/core/primitives/label/styles.ts +4 -3
  47. package/src/core/primitives/popover/helpers.ts +3 -4
  48. package/src/core/primitives/popover/popover.tsx +8 -4
  49. package/src/core/primitives/popover/types.ts +1 -1
  50. package/src/core/primitives/radio/styles.ts +17 -19
  51. package/src/core/primitives/select/styles.ts +27 -32
  52. package/src/core/primitives/stack/styles.ts +2 -3
  53. package/src/core/primitives/switch/__workshop__/props.tsx +11 -31
  54. package/src/core/primitives/switch/styles.ts +18 -21
  55. package/src/core/primitives/text/__workshop__/colored.tsx +10 -10
  56. package/src/core/primitives/text/styles.ts +9 -4
  57. package/src/core/primitives/tooltip/tooltip.tsx +3 -3
  58. package/src/core/styles/border/borderStyle.ts +6 -11
  59. package/src/core/styles/box/boxStyle.ts +5 -9
  60. package/src/core/styles/card/_cardColorStyle.ts +99 -0
  61. package/src/core/styles/card/index.ts +1 -0
  62. package/src/core/styles/flex/flexItemStyle.ts +2 -3
  63. package/src/core/styles/flex/flexStyle.ts +6 -11
  64. package/src/core/styles/font/responsiveFont.ts +4 -4
  65. package/src/core/styles/font/textAlignStyle.ts +3 -3
  66. package/src/core/styles/grid/gridItemStyle.ts +7 -13
  67. package/src/core/styles/grid/gridStyle.ts +9 -17
  68. package/src/core/styles/helpers.ts +5 -3
  69. package/src/core/styles/input/responsiveInputPaddingStyle.ts +6 -6
  70. package/src/core/styles/input/textInputStyle.ts +36 -40
  71. package/src/core/styles/margin/marginsStyle.test.ts +2 -3
  72. package/src/core/styles/padding/paddingStyle.test.ts +2 -3
  73. package/src/core/styles/radius/radiusStyle.ts +2 -3
  74. package/src/core/styles/shadow/shadowStyle.ts +4 -5
  75. package/src/core/theme/__workshop__/build/Root.tsx +24 -20
  76. package/src/core/theme/__workshop__/build/story.tsx +22 -26
  77. package/src/core/theme/__workshop__/canvas.tsx +21 -40
  78. package/src/core/theme/theme.test.tsx +2 -35
  79. package/src/core/theme/themeColorProvider.tsx +2 -2
  80. package/src/core/theme/themeContext.ts +2 -2
  81. package/src/core/theme/themeProvider.tsx +25 -25
  82. package/src/core/theme/types.ts +3 -2
  83. package/src/core/theme/useRootTheme.ts +1 -9
  84. package/src/core/theme/useTheme.ts +8 -1
  85. package/src/core/types/card.ts +2 -2
  86. package/src/core/utils/arrow/arrow.tsx +3 -3
  87. package/src/core/utils/elementQuery/elementQuery.tsx +3 -3
  88. package/src/core/utils/virtualList/virtualList.tsx +2 -2
  89. package/src/theme/build/_deprecated/color/muted/createMuted.ts +2 -2
  90. package/src/theme/build/buildColorTheme.test.ts +14 -15
  91. package/src/theme/build/buildColorTheme.ts +239 -292
  92. package/src/theme/build/buildTheme.test.ts +9 -7
  93. package/src/theme/build/buildTheme.ts +12 -9
  94. package/src/theme/build/colorToken.ts +3 -1
  95. package/src/theme/build/lib/color-fns/contrastRatio.ts +1 -0
  96. package/src/theme/build/lib/isRecord.ts +3 -0
  97. package/src/theme/build/merge.ts +18 -13
  98. package/src/theme/build/renderColorTheme.ts +205 -213
  99. package/src/theme/build/renderColorValue.ts +15 -8
  100. package/src/theme/build/resolveColorTokens.ts +177 -198
  101. package/src/theme/build/theme.test.ts +27 -0
  102. package/src/theme/config/helpers.ts +16 -8
  103. package/src/theme/config/system.ts +46 -17
  104. package/src/theme/config/tokens/color/types.ts +55 -32
  105. package/src/theme/config/tokens/types.ts +11 -4
  106. package/src/theme/config/types.ts +5 -11
  107. package/src/theme/{build/defaults → defaults}/colorPalette.ts +1 -1
  108. package/src/theme/{build/defaults → defaults}/colorTokens.ts +189 -136
  109. package/src/theme/{build/defaults → defaults}/config.ts +14 -12
  110. package/src/theme/{build/defaults → defaults}/fonts.ts +6 -6
  111. package/src/theme/getScopedTheme.ts +81 -0
  112. package/src/theme/index.ts +9 -1
  113. package/src/theme/system/avatar.ts +1 -1
  114. package/src/theme/system/color/_constants.ts +11 -11
  115. package/src/theme/system/color/_system.ts +10 -71
  116. package/src/theme/system/color/avatar.ts +22 -10
  117. package/src/theme/system/color/badge.ts +18 -7
  118. package/src/theme/system/color/button.ts +17 -23
  119. package/src/theme/system/color/color.ts +34 -0
  120. package/src/theme/system/color/index.ts +3 -10
  121. package/src/theme/system/color/input.ts +13 -11
  122. package/src/theme/system/color/kbd.ts +3 -3
  123. package/src/theme/system/color/selectable.ts +13 -19
  124. package/src/theme/system/color/shadow.ts +7 -0
  125. package/src/theme/system/color/state.ts +37 -0
  126. package/src/theme/system/css.ts +9 -0
  127. package/src/theme/system/focusRing.ts +7 -0
  128. package/src/theme/system/index.ts +5 -1
  129. package/src/theme/system/input.ts +1 -1
  130. package/src/theme/system/layer.ts +1 -2
  131. package/src/theme/system/shadow.ts +1 -0
  132. package/src/theme/system/styles.ts +19 -0
  133. package/src/theme/system/theme.ts +128 -0
  134. package/src/theme/system/{color → v0/color}/_generic.ts +2 -1
  135. package/src/theme/system/v0/color/_system.ts +13 -0
  136. package/src/theme/system/{color → v0/color}/base.ts +2 -0
  137. package/src/theme/system/v0/color/button.ts +42 -0
  138. package/src/theme/system/{color → v0/color}/card.ts +2 -1
  139. package/src/theme/system/v0/color/color.ts +40 -0
  140. package/src/theme/system/v0/color/index.ts +11 -0
  141. package/src/theme/system/v0/color/input.ts +34 -0
  142. package/src/theme/system/{color/_deprecated → v0/color}/muted.ts +1 -1
  143. package/src/theme/system/v0/color/selectable.ts +31 -0
  144. package/src/theme/system/{color/_deprecated → v0/color}/solid.ts +1 -1
  145. package/src/theme/system/v0/color/spot.ts +13 -0
  146. package/src/theme/system/v0/index.ts +1 -0
  147. package/src/theme/versioning/getTheme_v2.ts +38 -0
  148. package/src/theme/versioning/index.ts +5 -0
  149. package/src/theme/versioning/is_v0.ts +6 -0
  150. package/src/theme/versioning/is_v2.ts +6 -0
  151. package/src/theme/versioning/themeColor_v0_v2.ts +217 -0
  152. package/src/theme/versioning/v0_v2.ts +72 -0
  153. package/src/theme/versioning/v2_v0.ts +126 -0
  154. package/src/core/styles/colorVars/colorVarsStyle.ts +0 -43
  155. package/src/core/styles/colorVars/index.ts +0 -1
  156. package/src/core/theme/defaults.ts +0 -7
  157. package/src/theme/system/_system.ts +0 -95
  158. package/src/theme/system/color/_deprecated/spot.ts +0 -13
  159. package/src/theme/themes/studio/config.ts +0 -69
  160. package/src/theme/themes/studio/fonts.ts +0 -219
  161. package/src/theme/themes/studio/index.ts +0 -1
  162. package/src/theme/themes/studio/studioTheme.test.ts +0 -27
  163. package/src/theme/themes/studio/studioTheme.ts +0 -8
@@ -13,6 +13,10 @@ export interface RenderColorValueOptions {
13
13
  export function renderColorValue(str: string, options: RenderColorValueOptions): string {
14
14
  const {bg, blendMode, colorPalette} = options
15
15
 
16
+ if (bg === 'white') {
17
+ throw new Error('Cannot blend with white background')
18
+ }
19
+
16
20
  const node = parseTokenValue(str)
17
21
 
18
22
  if (!node || node.type !== 'color') {
@@ -39,17 +43,20 @@ export function renderColorValue(str: string, options: RenderColorValueOptions):
39
43
 
40
44
  const hexBeforeMix = hex
41
45
 
46
+ const mixOptions = {
47
+ blendMode,
48
+ bg,
49
+ black: renderColorHex(colorPalette.black),
50
+ // opacity: node.opacity,
51
+ white: renderColorHex(colorPalette.white),
52
+ }
53
+
42
54
  try {
43
- hex = mixThemeColor(hex, {
44
- blendMode,
45
- bg,
46
- black: renderColorHex(colorPalette.black),
47
- // opacity: node.opacity,
48
- white: renderColorHex(colorPalette.white),
49
- })
55
+ hex = mixThemeColor(hex, mixOptions)
50
56
  } catch (err) {
51
57
  // eslint-disable-next-line no-console
52
- console.log('could not blend', hex)
58
+ console.log('could not blend', hex, mixOptions)
59
+ throw err
53
60
  }
54
61
 
55
62
  if (hex === '#aN') {
@@ -1,118 +1,52 @@
1
1
  import {ColorHueKey} from '@sanity/color'
2
2
  import {
3
- ColorConfigAvatarColor,
4
- ColorConfigBaseTone,
3
+ ColorConfigCardTone,
5
4
  ColorConfigInputMode,
6
5
  ColorConfigInputState,
7
- ColorConfigState,
8
6
  ColorConfigStateTone,
9
- ThemeColorAvatarTokens,
10
- ThemeColorBadgeTokens,
11
7
  ThemeColorBaseTokens,
12
8
  ThemeColorButtonTokens,
13
9
  ThemeColorInputStateTokens,
14
10
  ThemeColorInputTokens,
15
11
  ThemeColorStateTokens,
16
12
  ThemeColorStatesTokens,
17
- ThemeColorTokenValue,
18
13
  ThemeColorTokens,
19
14
  } from '../config'
15
+ import {defaultColorTokens} from '../defaults/colorTokens'
20
16
  import {
21
- THEME_COLOR_AVATAR_COLORS,
22
- THEME_COLOR_BASE_TONES,
17
+ THEME_COLOR_CARD_TONES,
23
18
  THEME_COLOR_BUTTON_MODES,
24
19
  THEME_COLOR_INPUT_MODES,
25
20
  THEME_COLOR_INPUT_STATES,
26
21
  THEME_COLOR_STATES,
27
22
  THEME_COLOR_STATE_TONES,
28
- ThemeColorAvatarColorKey,
29
- ThemeColorBaseToneKey,
23
+ ThemeColorCardToneKey,
30
24
  ThemeColorButtonModeKey,
31
25
  ThemeColorStateKey,
32
26
  ThemeColorStateToneKey,
33
- ThemeColorSyntax,
34
27
  } from '../system'
35
- import {defaultColorTokens} from './defaults/colorTokens'
36
28
  import {merge} from './merge'
37
29
 
38
30
  /**
39
31
  * Convert a tree of color tokens from a sparse format to a dense format.
40
32
  */
41
33
  export function resolveColorTokens(inputTokens?: ThemeColorTokens): ThemeColorTokens {
42
- const sparseTokens = merge(
43
- defaultColorTokens as unknown as Record<string, unknown>,
44
- (inputTokens ?? {}) as unknown as Record<string, unknown>,
45
- )
46
-
47
- return {
48
- avatar: resolveAvatarColorTokens(sparseTokens),
49
- badge: resolveBadgeColorTokens(sparseTokens),
50
- base: resolveBaseColorTokens(sparseTokens),
51
- button: resolveButtonColorTokens(sparseTokens),
52
- card: resolveCardColorTokens(sparseTokens),
53
- input: resolveInputColorTokens(sparseTokens),
54
- kbd: resolveKBDColorTokens(sparseTokens),
55
- selectable: resolveSelectableColorTokens(sparseTokens),
56
- spot: resolveSpotColorTokens(sparseTokens),
57
- syntax: resolveSyntaxColorTokens(sparseTokens),
58
- }
59
- }
60
-
61
- function resolveKBDColorTokens(sparseTokens: ThemeColorTokens) {
62
- return sparseTokens?.kbd
63
- }
64
-
65
- function resolveAvatarColorTokens(sparseTokens: ThemeColorTokens) {
66
- const tokens: Partial<Record<ColorConfigAvatarColor, ThemeColorAvatarTokens>> = {}
67
-
68
- for (const color of THEME_COLOR_AVATAR_COLORS) {
69
- tokens[color] = _resolveAvatarColorTokens(sparseTokens, color)
70
- }
71
-
72
- return tokens
73
- }
74
-
75
- function _resolveAvatarColorTokens(
76
- sparseTokens: ThemeColorTokens,
77
- color: ThemeColorAvatarColorKey,
78
- ): ThemeColorAvatarTokens {
79
- const spec0 = sparseTokens?.avatar?.[color]
80
- const spec1 = sparseTokens?.avatar?.['*']
34
+ const tokens = merge(defaultColorTokens, inputTokens)
81
35
 
82
36
  return {
83
- ...spec1,
84
- ...spec0,
85
- }
86
- }
87
-
88
- function resolveBadgeColorTokens(sparseTokens: ThemeColorTokens) {
89
- const tokens: Partial<Record<ColorConfigStateTone, ThemeColorAvatarTokens>> = {}
90
-
91
- for (const tone of THEME_COLOR_STATE_TONES) {
92
- tokens[tone] = _resolveBadgeColorTokens(sparseTokens, tone)
93
- }
94
-
95
- return tokens
96
- }
97
-
98
- function _resolveBadgeColorTokens(
99
- sparseTokens: ThemeColorTokens,
100
- tone: ThemeColorStateToneKey,
101
- ): ThemeColorBadgeTokens {
102
- const spec0 = sparseTokens?.badge?.[tone]
103
- const spec1 = sparseTokens?.badge?.['*']
104
-
105
- return {
106
- ...spec1,
107
- ...spec0,
37
+ base: resolveBaseColorTokens(tokens),
38
+ button: resolveButtonColorTokens(tokens),
39
+ input: resolveInputColorTokens(tokens),
40
+ selectable: resolveSelectableColorTokens(tokens),
41
+ syntax: tokens.syntax,
108
42
  }
109
43
  }
110
44
 
111
45
  function resolveBaseColorTokens(sparseTokens: ThemeColorTokens) {
112
- const tokens: Partial<Record<ColorConfigBaseTone, ThemeColorBaseTokens>> = {}
46
+ const tokens: Partial<Record<ColorConfigCardTone, ThemeColorBaseTokens>> = {}
113
47
 
114
48
  // base tones
115
- for (const tone of THEME_COLOR_BASE_TONES) {
49
+ for (const tone of THEME_COLOR_CARD_TONES) {
116
50
  tokens[tone] = resolveBaseColorTones(sparseTokens, tone)
117
51
  }
118
52
 
@@ -120,233 +54,278 @@ function resolveBaseColorTokens(sparseTokens: ThemeColorTokens) {
120
54
  }
121
55
 
122
56
  function resolveBaseColorTones(
123
- sparseTokens: ThemeColorTokens,
124
- tone: ThemeColorBaseToneKey,
57
+ inputTokens: ThemeColorTokens,
58
+ tone: ThemeColorCardToneKey,
125
59
  ): ThemeColorBaseTokens {
126
- const spec0 = sparseTokens?.base?.[tone]
127
- const spec1 = sparseTokens?.base?.['*']
60
+ const spec = merge(inputTokens?.base?.['*'], inputTokens?.base?.[tone])
61
+
62
+ const hue = spec._hue || inputTokens?.base?.[tone]?._hue || 'gray'
128
63
 
129
64
  return {
130
- ...spec1,
131
- ...spec0,
132
- shadow: {...spec1?.shadow, ...spec0?.shadow},
133
- skeleton: {...spec1?.skeleton, ...spec0?.skeleton},
65
+ ...spec,
66
+ _hue: hue,
67
+ avatar: {
68
+ gray: merge({_hue: 'gray'}, spec.avatar?.['*'], spec.avatar?.gray),
69
+ blue: merge({_hue: 'blue'}, spec.avatar?.['*'], spec.avatar?.blue),
70
+ purple: merge({_hue: 'purple'}, spec.avatar?.['*'], spec.avatar?.purple),
71
+ magenta: merge({_hue: 'magenta'}, spec.avatar?.['*'], spec.avatar?.magenta),
72
+ red: merge({_hue: 'red'}, spec.avatar?.['*'], spec.avatar?.red),
73
+ orange: merge({_hue: 'orange'}, spec.avatar?.['*'], spec.avatar?.orange),
74
+ yellow: merge({_hue: 'yellow'}, spec.avatar?.['*'], spec.avatar?.yellow),
75
+ green: merge({_hue: 'green'}, spec.avatar?.['*'], spec.avatar?.green),
76
+ cyan: merge({_hue: 'cyan'}, spec.avatar?.['*'], spec.avatar?.cyan),
77
+ },
78
+ badge: {
79
+ default: {
80
+ _hue: inputTokens?.base?.default?._hue || hue,
81
+ ...spec.badge?.['*'],
82
+ ...spec.badge?.default,
83
+ },
84
+ primary: {
85
+ _hue: inputTokens?.base?.primary?._hue || hue,
86
+ ...spec.badge?.['*'],
87
+ ...spec.badge?.primary,
88
+ },
89
+ positive: {
90
+ _hue: inputTokens?.base?.positive?._hue || hue,
91
+ ...spec.badge?.['*'],
92
+ ...spec.badge?.positive,
93
+ },
94
+ caution: {
95
+ _hue: inputTokens?.base?.caution?._hue || hue,
96
+ ...spec.badge?.['*'],
97
+ ...spec.badge?.caution,
98
+ },
99
+ critical: {
100
+ _hue: inputTokens?.base?.critical?._hue || hue,
101
+ ...spec.badge?.['*'],
102
+ ...spec.badge?.critical,
103
+ },
104
+ },
134
105
  }
135
106
  }
136
107
 
137
108
  function resolveButtonColorTokens(
138
- sparseTokens: ThemeColorTokens,
109
+ inputTokens: ThemeColorTokens,
139
110
  ): Partial<Record<ThemeColorButtonModeKey, ThemeColorButtonTokens>> {
140
111
  const tokens: Partial<Record<ThemeColorButtonModeKey, ThemeColorButtonTokens>> = {}
141
112
 
142
113
  for (const mode of THEME_COLOR_BUTTON_MODES) {
143
- tokens[mode] = resolveButtonToneColorTokens(sparseTokens, mode)
114
+ tokens[mode] = resolveButtonToneColorTokens(inputTokens, mode)
144
115
  }
145
116
 
146
117
  return tokens
147
118
  }
148
119
 
149
120
  function resolveButtonToneColorTokens(
150
- sparseTokens: ThemeColorTokens,
121
+ inputTokens: ThemeColorTokens,
151
122
  mode: ThemeColorButtonModeKey,
152
123
  ): ThemeColorButtonTokens {
153
124
  const tokens: ThemeColorButtonTokens = {}
154
125
 
155
126
  for (const tone of THEME_COLOR_STATE_TONES) {
156
- tokens[tone] = resolveButtonModeColorTokens(sparseTokens, mode, tone)
127
+ tokens[tone] = resolveButtonModeColorTokens(inputTokens, mode, tone)
157
128
  }
158
129
 
159
130
  return tokens
160
131
  }
161
132
 
162
133
  function resolveButtonModeColorTokens(
163
- sparseTokens: ThemeColorTokens,
134
+ inputTokens: ThemeColorTokens,
164
135
  mode: ThemeColorButtonModeKey,
165
136
  tone: ThemeColorStateToneKey,
166
137
  ): ThemeColorStatesTokens {
167
- const spec0 = sparseTokens.button?.[mode]?.[tone]
168
- const spec1 = sparseTokens.button?.[mode]?.['*']
169
- const tokens = {...spec1, ...spec0}
138
+ const tokens: ThemeColorStatesTokens = {}
170
139
 
171
140
  for (const state of THEME_COLOR_STATES) {
172
- tokens[state] = resolveButtonStateColorTokens(sparseTokens, tone, mode, state)
141
+ tokens[state] = resolveButtonStateColorTokens(inputTokens, tone, mode, state)
173
142
  }
174
143
 
175
144
  return tokens
176
145
  }
177
146
 
178
147
  function resolveButtonStateColorTokens(
179
- tokens: ThemeColorTokens,
148
+ inputTokens: ThemeColorTokens,
180
149
  tone: ThemeColorStateToneKey,
181
150
  mode: ThemeColorButtonModeKey,
182
151
  state: ThemeColorStateKey,
183
152
  ): ThemeColorStateTokens {
184
- const spec0 = tokens?.button?.[mode]?.[tone]?.[state]
185
- const spec1 = tokens?.button?.[mode]?.['*']?.[state]
186
- const spec2 = tokens?.button?.[mode]?.[tone]?.['*']
187
- const spec3 = tokens?.button?.[mode]?.['*']?.['*']
188
-
189
- const hue = spec0?._hue || spec1?._hue || spec2?._hue || spec3?._hue || tokens?.base?.[tone]?._hue
190
-
191
- return {
192
- ...spec3,
193
- ...spec2,
194
- ...spec1,
195
- ...spec0,
196
- _hue: hue,
197
- muted: {...spec3?.muted, ...spec2?.muted, ...spec1?.muted, ...spec0?.muted},
198
- accent: {...spec3?.accent, ...spec2?.accent, ...spec1?.accent, ...spec0?.accent},
199
- link: {...spec3?.link, ...spec2?.link, ...spec1?.link, ...spec0?.link},
200
- code: {...spec3?.code, ...spec2?.code, ...spec1?.code, ...spec0?.code},
201
- skeleton: {...spec3?.skeleton, ...spec2?.skeleton, ...spec1?.skeleton, ...spec0?.skeleton},
202
- }
203
- }
204
-
205
- function resolveCardColorTokens(
206
- sparseTokens: ThemeColorTokens,
207
- ): Partial<Record<ColorConfigState, ThemeColorStateTokens>> {
208
- const tokens: Partial<Record<ColorConfigState, ThemeColorStateTokens>> = {}
209
-
210
- for (const state of THEME_COLOR_STATES) {
211
- tokens[state] = resolveCardStateColorTokens(sparseTokens, state)
212
- }
213
-
214
- return tokens
215
- }
216
-
217
- function resolveCardStateColorTokens(
218
- tokens: ThemeColorTokens,
219
- state: ThemeColorStateKey,
220
- ): ThemeColorStateTokens {
221
- const spec0 = tokens?.card?.[state]
222
- const spec1 = tokens?.card?.['*']
153
+ const spec = merge(
154
+ inputTokens?.button?.[mode]?.['*']?.['*'],
155
+ inputTokens?.button?.[mode]?.[tone]?.['*'],
156
+ inputTokens?.button?.[mode]?.['*']?.[state],
157
+ inputTokens?.button?.[mode]?.[tone]?.[state],
158
+ )
223
159
 
224
- const hue = spec0?._hue || spec1?._hue
160
+ const hue = spec._hue || inputTokens?.base?.[tone]?._hue
225
161
 
226
162
  return {
227
- ...spec1,
228
- ...spec0,
163
+ ...spec,
229
164
  _hue: hue,
230
- muted: {...spec1?.muted, ...spec0?.muted},
231
- accent: {...spec1?.accent, ...spec0?.accent},
232
- link: {...spec1?.link, ...spec0?.link},
233
- code: {...spec1?.code, ...spec0?.code},
234
- skeleton: {...spec1?.skeleton, ...spec0?.skeleton},
165
+ avatar: {
166
+ gray: merge({_hue: 'gray'}, spec.avatar?.['*'], spec.avatar?.gray),
167
+ blue: merge({_hue: 'blue'}, spec.avatar?.['*'], spec.avatar?.blue),
168
+ purple: merge({_hue: 'purple'}, spec.avatar?.['*'], spec.avatar?.purple),
169
+ magenta: merge({_hue: 'magenta'}, spec.avatar?.['*'], spec.avatar?.magenta),
170
+ red: merge({_hue: 'red'}, spec.avatar?.['*'], spec.avatar?.red),
171
+ orange: merge({_hue: 'orange'}, spec.avatar?.['*'], spec.avatar?.orange),
172
+ yellow: merge({_hue: 'yellow'}, spec.avatar?.['*'], spec.avatar?.yellow),
173
+ green: merge({_hue: 'green'}, spec.avatar?.['*'], spec.avatar?.green),
174
+ cyan: merge({_hue: 'cyan'}, spec.avatar?.['*'], spec.avatar?.cyan),
175
+ },
176
+ badge: {
177
+ default: {
178
+ _hue: inputTokens?.base?.default?._hue,
179
+ ...spec.badge?.['*'],
180
+ ...spec.badge?.default,
181
+ },
182
+ primary: {
183
+ _hue: inputTokens?.base?.primary?._hue,
184
+ ...spec.badge?.['*'],
185
+ ...spec.badge?.primary,
186
+ },
187
+ positive: {
188
+ _hue: inputTokens?.base?.positive?._hue,
189
+ ...spec.badge?.['*'],
190
+ ...spec.badge?.positive,
191
+ },
192
+ caution: {
193
+ _hue: inputTokens?.base?.caution?._hue,
194
+ ...spec.badge?.['*'],
195
+ ...spec.badge?.caution,
196
+ },
197
+ critical: {
198
+ _hue: inputTokens?.base?.critical?._hue,
199
+ ...spec.badge?.['*'],
200
+ ...spec.badge?.critical,
201
+ },
202
+ },
235
203
  }
236
204
  }
237
205
 
238
206
  function resolveInputColorTokens(
239
- sparseTokens: ThemeColorTokens,
207
+ inputTokens: ThemeColorTokens,
240
208
  ): Partial<Record<ColorConfigInputMode, ThemeColorInputTokens>> {
241
209
  const tokens: Partial<Record<ColorConfigInputMode, ThemeColorInputTokens>> = {}
242
210
 
243
211
  for (const mode of THEME_COLOR_INPUT_MODES) {
244
- tokens[mode] = resolveInputModeColorTokens(sparseTokens, mode)
212
+ tokens[mode] = resolveInputModeColorTokens(inputTokens, mode)
245
213
  }
246
214
 
247
215
  return tokens
248
216
  }
249
217
 
250
218
  function resolveInputModeColorTokens(
251
- sparseTokens: ThemeColorTokens,
219
+ inputTokens: ThemeColorTokens,
252
220
  mode: ColorConfigInputMode,
253
221
  ): ThemeColorInputTokens {
254
222
  const states: ThemeColorInputTokens = {}
255
223
 
256
224
  for (const state of THEME_COLOR_INPUT_STATES) {
257
- states[state] = resolveInputStateColorTokens(sparseTokens, mode, state)
225
+ states[state] = resolveInputStateColorTokens(inputTokens, mode, state)
258
226
  }
259
227
 
260
228
  return states
261
229
  }
262
230
 
263
231
  function resolveInputStateColorTokens(
264
- tokens: ThemeColorTokens,
232
+ inputTokens: ThemeColorTokens,
265
233
  mode: ColorConfigInputMode,
266
234
  state: ColorConfigInputState,
267
235
  ): ThemeColorInputStateTokens {
268
- const spec0 = tokens?.input?.[mode]?.[state]
269
- const spec1 = tokens?.input?.['*']?.[state]
270
- const spec2 = tokens?.input?.[mode]?.['*']
271
- const spec3 = tokens?.input?.['*']?.['*']
236
+ const spec = merge(
237
+ inputTokens?.input?.['*']?.['*'],
238
+ inputTokens?.input?.[mode]?.['*'],
239
+ inputTokens?.input?.['*']?.[state],
240
+ inputTokens?.input?.[mode]?.[state],
241
+ )
272
242
 
273
- const hue =
274
- spec0?._hue || spec1?._hue || spec2?._hue || spec3?._hue || tokens?.input?.[mode]?._hue
243
+ const hue = spec._hue || inputTokens?.input?.[mode]?._hue
275
244
 
276
- return {
277
- ...spec3,
278
- ...spec2,
279
- ...spec1,
280
- ...spec0,
281
- _hue: hue,
282
- }
245
+ return {...spec, _hue: hue}
283
246
  }
284
247
 
285
248
  function resolveSelectableColorTokens(
286
- sparseTokens: ThemeColorTokens,
249
+ inputTokens: ThemeColorTokens,
287
250
  ): Partial<Record<ColorConfigStateTone, {_hue?: ColorHueKey} & ThemeColorStatesTokens>> {
288
251
  const tokens: ThemeColorButtonTokens = {}
289
252
 
290
253
  for (const tone of THEME_COLOR_STATE_TONES) {
291
- tokens[tone] = resolveSelectableToneColorTokens(sparseTokens, tone)
254
+ tokens[tone] = resolveSelectableToneColorTokens(inputTokens, tone)
292
255
  }
293
256
 
294
257
  return tokens
295
258
  }
296
259
 
297
260
  function resolveSelectableToneColorTokens(
298
- sparseTokens: ThemeColorTokens,
261
+ inputTokens: ThemeColorTokens,
299
262
  tone: ThemeColorStateToneKey,
300
263
  ): {_hue?: ColorHueKey} & ThemeColorStatesTokens {
301
264
  const states: {_hue?: ColorHueKey} & ThemeColorStatesTokens = {
302
- _hue: sparseTokens?.selectable?.[tone]?._hue || sparseTokens?.base?.[tone]?._hue,
265
+ _hue: inputTokens?.selectable?.[tone]?._hue || inputTokens?.base?.[tone]?._hue,
303
266
  }
304
267
 
305
268
  for (const state of THEME_COLOR_STATES) {
306
- states[state] = resolveSelectableStateColorTokens(sparseTokens, state, tone)
269
+ states[state] = resolveSelectableStateColorTokens(inputTokens, tone, state)
307
270
  }
308
271
 
309
272
  return states
310
273
  }
311
274
 
312
275
  function resolveSelectableStateColorTokens(
313
- tokens: ThemeColorTokens,
314
- state: ThemeColorStateKey,
276
+ inputTokens: ThemeColorTokens,
315
277
  tone: ThemeColorStateToneKey,
278
+ state: ThemeColorStateKey,
316
279
  ) {
317
- const spec0 = tokens?.selectable?.[tone]?.[state]
318
- const spec1 = tokens?.selectable?.['*']?.[state]
319
- const spec2 = tokens?.selectable?.[tone]?.['*']
320
- const spec3 = tokens?.selectable?.['*']?.['*']
280
+ const spec = merge(
281
+ inputTokens?.selectable?.['*']?.['*'],
282
+ inputTokens?.selectable?.[tone]?.['*'],
283
+ inputTokens?.selectable?.['*']?.[state],
284
+ inputTokens?.selectable?.[tone]?.[state],
285
+ )
321
286
 
322
- const hue = spec0?._hue || spec1?._hue || spec2?._hue || spec3?._hue || tokens?.base?.[tone]?._hue
287
+ const hue = spec._hue || inputTokens?.base?.[tone]?._hue
323
288
 
324
289
  return {
325
- ...spec3,
326
- ...spec2,
327
- ...spec1,
328
- ...spec0,
290
+ ...spec,
329
291
  _hue: hue,
330
- muted: {...spec3?.muted, ...spec2?.muted, ...spec1?.muted, ...spec0?.muted},
331
- accent: {...spec3?.accent, ...spec2?.accent, ...spec1?.accent, ...spec0?.accent},
332
- link: {...spec3?.link, ...spec2?.link, ...spec1?.link, ...spec0?.link},
333
- code: {...spec3?.code, ...spec2?.code, ...spec1?.code, ...spec0?.code},
334
- skeleton: {...spec3?.skeleton, ...spec2?.skeleton, ...spec1?.skeleton, ...spec0?.skeleton},
335
- }
336
- }
337
-
338
- function resolveSpotColorTokens(
339
- sparseTokens: ThemeColorTokens,
340
- ): Partial<Record<ColorHueKey, ThemeColorTokenValue>> {
341
- return {
342
- ...sparseTokens.spot,
343
- }
344
- }
345
-
346
- function resolveSyntaxColorTokens(
347
- sparseTokens: ThemeColorTokens,
348
- ): Partial<Record<keyof ThemeColorSyntax, ThemeColorTokenValue>> {
349
- return {
350
- ...sparseTokens.syntax,
292
+ avatar: {
293
+ gray: merge({_hue: 'gray'}, spec.avatar?.['*'], spec.avatar?.gray),
294
+ blue: merge({_hue: 'blue'}, spec.avatar?.['*'], spec.avatar?.blue),
295
+ purple: merge({_hue: 'purple'}, spec.avatar?.['*'], spec.avatar?.purple),
296
+ magenta: merge({_hue: 'magenta'}, spec.avatar?.['*'], spec.avatar?.magenta),
297
+ red: merge({_hue: 'red'}, spec.avatar?.['*'], spec.avatar?.red),
298
+ orange: merge({_hue: 'orange'}, spec.avatar?.['*'], spec.avatar?.orange),
299
+ yellow: merge({_hue: 'yellow'}, spec.avatar?.['*'], spec.avatar?.yellow),
300
+ green: merge({_hue: 'green'}, spec.avatar?.['*'], spec.avatar?.green),
301
+ cyan: merge({_hue: 'cyan'}, spec.avatar?.['*'], spec.avatar?.cyan),
302
+ },
303
+ badge: {
304
+ default: {
305
+ _hue: inputTokens?.base?.default?._hue,
306
+ ...spec.badge?.['*'],
307
+ ...spec.badge?.default,
308
+ },
309
+ primary: {
310
+ _hue: inputTokens?.base?.primary?._hue,
311
+ ...spec.badge?.['*'],
312
+ ...spec.badge?.primary,
313
+ },
314
+ positive: {
315
+ _hue: inputTokens?.base?.positive?._hue,
316
+ ...spec.badge?.['*'],
317
+ ...spec.badge?.positive,
318
+ },
319
+ caution: {
320
+ _hue: inputTokens?.base?.caution?._hue,
321
+ ...spec.badge?.['*'],
322
+ ...spec.badge?.caution,
323
+ },
324
+ critical: {
325
+ _hue: inputTokens?.base?.critical?._hue,
326
+ ...spec.badge?.['*'],
327
+ ...spec.badge?.critical,
328
+ },
329
+ },
351
330
  }
352
331
  }
@@ -0,0 +1,27 @@
1
+ import {buildTheme} from './buildTheme'
2
+ import {getContrastRatio} from './lib/color-fns'
3
+
4
+ const AA_MIN = 4.5
5
+
6
+ it('should have sufficient contrast', () => {
7
+ const base = buildTheme().v2!.color.light.default
8
+ const selectable = base.selectable.default
9
+
10
+ const contrasts = {
11
+ base: {
12
+ fg: getContrastRatio(base.bg, base.fg),
13
+ },
14
+ selectable: {
15
+ enabled: {
16
+ fg: getContrastRatio(selectable.enabled.bg, selectable.enabled.fg),
17
+ muted: {
18
+ fg: getContrastRatio(selectable.enabled.bg, selectable.enabled.muted.fg),
19
+ },
20
+ },
21
+ },
22
+ }
23
+
24
+ expect(contrasts.base.fg).toBeGreaterThan(AA_MIN)
25
+ expect(contrasts.selectable.enabled.fg).toBeGreaterThan(AA_MIN)
26
+ expect(contrasts.selectable.enabled.muted.fg).toBeGreaterThan(AA_MIN)
27
+ })
@@ -1,11 +1,11 @@
1
1
  import {
2
- COLOR_CONFIG_BASE_KEYS,
3
- COLOR_CONFIG_BASE_TONES,
2
+ COLOR_CONFIG_CARD_KEYS,
3
+ COLOR_CONFIG_CARD_TONES,
4
4
  COLOR_CONFIG_BLEND_KEYS,
5
5
  COLOR_CONFIG_STATE_KEYS,
6
6
  COLOR_CONFIG_STATE_TONES,
7
- ColorConfigBaseKey,
8
- ColorConfigBaseTone,
7
+ ColorConfigCardKey,
8
+ ColorConfigCardTone,
9
9
  ColorConfigBlendKey,
10
10
  ColorConfigOpacityValue,
11
11
  ColorConfigStateKey,
@@ -14,34 +14,42 @@ import {
14
14
  } from './system'
15
15
  import {parseTokenValue} from './tokens'
16
16
 
17
- export function isColorConfigBaseTone(str: string): str is ColorConfigBaseTone {
18
- return COLOR_CONFIG_BASE_TONES.includes(str as ColorConfigBaseTone)
17
+ /** @internal */
18
+ export function isColorConfigBaseTone(str: string): str is ColorConfigCardTone {
19
+ return COLOR_CONFIG_CARD_TONES.includes(str as ColorConfigCardTone)
19
20
  }
20
21
 
21
- export function isColorConfigBaseKey(str: string): str is ColorConfigBaseKey {
22
- return COLOR_CONFIG_BASE_KEYS.includes(str as ColorConfigBaseKey)
22
+ /** @internal */
23
+ export function isColorConfigBaseKey(str: string): str is ColorConfigCardKey {
24
+ return COLOR_CONFIG_CARD_KEYS.includes(str as ColorConfigCardKey)
23
25
  }
24
26
 
27
+ /** @internal */
25
28
  export function isColorConfigStateKey(str: string): str is ColorConfigStateKey {
26
29
  return COLOR_CONFIG_STATE_KEYS.includes(str as ColorConfigStateKey)
27
30
  }
28
31
 
32
+ /** @internal */
29
33
  export function isColorConfigStateTone(str: string): str is ColorConfigStateTone {
30
34
  return COLOR_CONFIG_STATE_TONES.includes(str as ColorConfigStateTone)
31
35
  }
32
36
 
37
+ /** @internal */
33
38
  export function isColorConfigBlendKey(str: string): str is ColorConfigBlendKey {
34
39
  return COLOR_CONFIG_BLEND_KEYS.includes(str as ColorConfigBlendKey)
35
40
  }
36
41
 
42
+ /** @internal */
37
43
  export function isColorTokenValue(str: string): str is ColorConfigValue {
38
44
  return parseTokenValue(str)?.type === 'color' || parseTokenValue(str)?.type === 'hue'
39
45
  }
40
46
 
47
+ /** @internal */
41
48
  export function isColorValue(str: string): str is 'black' | 'white' {
42
49
  return str === 'black' || str === 'white'
43
50
  }
44
51
 
52
+ /** @internal */
45
53
  export function isColorOpacityValue(str: string): str is ColorConfigOpacityValue {
46
54
  return str === '0' || /^0\.[0-9]+$/.test(str) || str === '1'
47
55
  }