@titan-design/react-ui 0.2.6 → 0.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/dist/bodymap-BhG55xHM.d.mts +318 -0
- package/dist/bodymap-BhG55xHM.d.ts +318 -0
- package/dist/bodymap.d.mts +3 -0
- package/dist/bodymap.d.ts +3 -0
- package/dist/bodymap.js +2670 -0
- package/dist/bodymap.js.map +1 -0
- package/dist/bodymap.mjs +1101 -0
- package/dist/bodymap.mjs.map +1 -0
- package/dist/{chunk-UXVRPOME.mjs → chunk-7XPB4NAO.mjs} +125 -785
- package/dist/chunk-7XPB4NAO.mjs.map +1 -0
- package/dist/chunk-P7TSQUN6.mjs +846 -0
- package/dist/chunk-P7TSQUN6.mjs.map +1 -0
- package/dist/chunk-TOD2WQBG.mjs +1048 -0
- package/dist/chunk-TOD2WQBG.mjs.map +1 -0
- package/dist/index.d.mts +1131 -4
- package/dist/index.d.ts +1131 -4
- package/dist/index.js +6396 -225
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5572 -641
- package/dist/index.mjs.map +1 -1
- package/dist/theme/index.d.mts +317 -1
- package/dist/theme/index.d.ts +317 -1
- package/dist/theme/index.js +185 -2
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/index.mjs +2 -1
- package/dist/theme/tokens-css.d.mts +20 -0
- package/dist/theme/tokens-css.d.ts +20 -0
- package/dist/theme/tokens-css.js +598 -0
- package/dist/theme/tokens-css.js.map +1 -0
- package/dist/theme/tokens-css.mjs +24 -0
- package/dist/theme/tokens-css.mjs.map +1 -0
- package/package.json +25 -5
- package/src/bodymap.ts +37 -0
- package/src/components/custom/DateTime/DateTime.stories.tsx +1 -1
- package/src/components/custom/Gauge/Gauge.stories.tsx +45 -0
- package/src/components/custom/Gauge/Gauge.test.tsx +71 -0
- package/src/components/custom/Gauge/Gauge.tsx +167 -0
- package/src/components/custom/Gauge/index.ts +2 -0
- package/src/components/custom/Scatter/Scatter.stories.tsx +85 -0
- package/src/components/custom/Scatter/Scatter.test.tsx +96 -0
- package/src/components/custom/Scatter/Scatter.tsx +260 -0
- package/src/components/custom/Scatter/index.ts +2 -0
- package/src/components/custom/Sidebar/Sidebar.tsx +1 -1
- package/src/components/custom/Table/Table.test.tsx +27 -0
- package/src/components/custom/Table/Table.tsx +33 -17
- package/src/components/custom/Treemap/Treemap.stories.tsx +60 -0
- package/src/components/custom/Treemap/Treemap.test.tsx +67 -0
- package/src/components/custom/Treemap/Treemap.tsx +192 -0
- package/src/components/custom/Treemap/index.ts +2 -0
- package/src/components/custom/Workout/ActiveWorkoutPage.stories.tsx +118 -0
- package/src/components/custom/Workout/ActiveWorkoutPage.test.tsx +171 -0
- package/src/components/custom/Workout/ActiveWorkoutPage.tsx +447 -0
- package/src/components/custom/Workout/BaseBadge.stories.tsx +75 -0
- package/src/components/custom/Workout/BaseBadge.test.tsx +102 -0
- package/src/components/custom/Workout/BaseBadge.tsx +113 -0
- package/src/components/custom/Workout/BodyMap.stories.tsx +121 -0
- package/src/components/custom/Workout/BodyMap.test.tsx +124 -0
- package/src/components/custom/Workout/BodyMap.tsx +347 -0
- package/src/components/custom/Workout/BodyMapDetailPanel.stories.tsx +171 -0
- package/src/components/custom/Workout/BodyMapDetailPanel.test.tsx +225 -0
- package/src/components/custom/Workout/BodyMapDetailPanel.tsx +477 -0
- package/src/components/custom/Workout/CapacityBandChart.stories.tsx +125 -0
- package/src/components/custom/Workout/CapacityBandChart.test.tsx +193 -0
- package/src/components/custom/Workout/CapacityBandChart.tsx +493 -0
- package/src/components/custom/Workout/DeviationBar.stories.tsx +58 -0
- package/src/components/custom/Workout/DeviationBar.test.tsx +86 -0
- package/src/components/custom/Workout/DeviationBar.tsx +78 -0
- package/src/components/custom/Workout/ExerciseCard.stories.tsx +186 -0
- package/src/components/custom/Workout/ExerciseCard.test.tsx +343 -0
- package/src/components/custom/Workout/ExerciseCard.tsx +442 -0
- package/src/components/custom/Workout/ExerciseDetailPage.stories.tsx +225 -0
- package/src/components/custom/Workout/ExerciseDetailPage.test.tsx +128 -0
- package/src/components/custom/Workout/ExerciseDetailPage.tsx +630 -0
- package/src/components/custom/Workout/InputBar.stories.tsx +75 -0
- package/src/components/custom/Workout/InputBar.test.tsx +116 -0
- package/src/components/custom/Workout/InputBar.tsx +162 -0
- package/src/components/custom/Workout/IntensityBar.stories.tsx +60 -0
- package/src/components/custom/Workout/IntensityBar.test.tsx +113 -0
- package/src/components/custom/Workout/IntensityBar.tsx +166 -0
- package/src/components/custom/Workout/MesoCard.stories.tsx +152 -0
- package/src/components/custom/Workout/MesoCard.test.tsx +170 -0
- package/src/components/custom/Workout/MesoCard.tsx +237 -0
- package/src/components/custom/Workout/MesoProgressBar.stories.tsx +85 -0
- package/src/components/custom/Workout/MesoProgressBar.test.tsx +153 -0
- package/src/components/custom/Workout/MesoProgressBar.tsx +165 -0
- package/src/components/custom/Workout/MesoStatusCard.stories.tsx +126 -0
- package/src/components/custom/Workout/MesoStatusCard.test.tsx +209 -0
- package/src/components/custom/Workout/MesoStatusCard.tsx +456 -0
- package/src/components/custom/Workout/MuscleGroupChip.stories.tsx +118 -0
- package/src/components/custom/Workout/MuscleGroupChip.test.tsx +91 -0
- package/src/components/custom/Workout/MuscleGroupChip.tsx +92 -0
- package/src/components/custom/Workout/PlaceholderStrip.stories.tsx +94 -0
- package/src/components/custom/Workout/PlaceholderStrip.test.tsx +83 -0
- package/src/components/custom/Workout/PlaceholderStrip.tsx +90 -0
- package/src/components/custom/Workout/PrBadge.stories.tsx +105 -0
- package/src/components/custom/Workout/PrBadge.test.tsx +111 -0
- package/src/components/custom/Workout/PrBadge.tsx +103 -0
- package/src/components/custom/Workout/PrHistoryModal.stories.tsx +116 -0
- package/src/components/custom/Workout/PrHistoryModal.test.tsx +161 -0
- package/src/components/custom/Workout/PrHistoryModal.tsx +244 -0
- package/src/components/custom/Workout/ProgramPlanningPage.stories.tsx +257 -0
- package/src/components/custom/Workout/ProgramPlanningPage.test.tsx +142 -0
- package/src/components/custom/Workout/ProgramPlanningPage.tsx +422 -0
- package/src/components/custom/Workout/ReadinessCheck.stories.tsx +126 -0
- package/src/components/custom/Workout/ReadinessCheck.test.tsx +181 -0
- package/src/components/custom/Workout/ReadinessCheck.tsx +265 -0
- package/src/components/custom/Workout/RestTimer.test.tsx +10 -0
- package/src/components/custom/Workout/RestTimer.tsx +15 -11
- package/src/components/custom/Workout/SetRow.stories.tsx +165 -0
- package/src/components/custom/Workout/SetRow.test.tsx +222 -0
- package/src/components/custom/Workout/SetRow.tsx +247 -0
- package/src/components/custom/Workout/Sparkline.stories.tsx +125 -0
- package/src/components/custom/Workout/Sparkline.test.tsx +113 -0
- package/src/components/custom/Workout/Sparkline.tsx +188 -0
- package/src/components/custom/Workout/StatusDot.stories.tsx +150 -0
- package/src/components/custom/Workout/StatusDot.test.tsx +165 -0
- package/src/components/custom/Workout/StatusDot.tsx +160 -0
- package/src/components/custom/Workout/StrengthTrendChart.stories.tsx +130 -0
- package/src/components/custom/Workout/StrengthTrendChart.test.tsx +217 -0
- package/src/components/custom/Workout/StrengthTrendChart.tsx +628 -0
- package/src/components/custom/Workout/SupersetWrapper.tsx +1 -2
- package/src/components/custom/Workout/TempoDisplay.stories.tsx +66 -0
- package/src/components/custom/Workout/TempoDisplay.test.tsx +90 -0
- package/src/components/custom/Workout/TempoDisplay.tsx +209 -0
- package/src/components/custom/Workout/TrainingStatusPage.stories.tsx +212 -0
- package/src/components/custom/Workout/TrainingStatusPage.test.tsx +131 -0
- package/src/components/custom/Workout/TrainingStatusPage.tsx +321 -0
- package/src/components/custom/Workout/VelocityStrip.stories.tsx +125 -0
- package/src/components/custom/Workout/VelocityStrip.test.tsx +244 -0
- package/src/components/custom/Workout/VelocityStrip.tsx +288 -0
- package/src/components/custom/Workout/WeekRow.stories.tsx +121 -0
- package/src/components/custom/Workout/WeekRow.test.tsx +117 -0
- package/src/components/custom/Workout/WeekRow.tsx +140 -0
- package/src/components/custom/Workout/WeightBadge.stories.tsx +87 -0
- package/src/components/custom/Workout/WeightBadge.test.tsx +164 -0
- package/src/components/custom/Workout/WeightBadge.tsx +113 -0
- package/src/components/custom/Workout/WorkoutCard.stories.tsx +135 -0
- package/src/components/custom/Workout/WorkoutCard.test.tsx +172 -0
- package/src/components/custom/Workout/WorkoutCard.tsx +228 -0
- package/src/components/custom/Workout/WorkoutPill.stories.tsx +63 -0
- package/src/components/custom/Workout/WorkoutPill.test.tsx +153 -0
- package/src/components/custom/Workout/WorkoutPill.tsx +177 -0
- package/src/components/custom/Workout/index.ts +152 -0
- package/src/components/custom/Workout/muscleTaxonomy.ts +252 -0
- package/src/components/custom/index.ts +3 -0
- package/src/components/custom/stepper/Stepper.stories.tsx +1 -1
- package/src/components/custom/stepper/Stepper.tsx +2 -2
- package/src/components/ui/autocomplete/Autocomplete.stories.tsx +1 -1
- package/src/components/ui/autocomplete/Autocomplete.tsx +1 -1
- package/src/components/ui/card/Card.tsx +1 -1
- package/src/components/ui/checkbox/Checkbox.tsx +1 -1
- package/src/components/ui/collapse/Collapse.tsx +1 -1
- package/src/components/ui/data-row/DataRow.stories.tsx +1 -1
- package/src/components/ui/drawer/Drawer.tsx +3 -3
- package/src/components/ui/form-field/FormField.tsx +1 -1
- package/src/components/ui/help-tip/HelpTip.tsx +1 -1
- package/src/components/ui/menu/Menu.tsx +2 -2
- package/src/components/ui/pill/Pill.tsx +1 -1
- package/src/components/ui/popover/Popover.stories.tsx +2 -2
- package/src/components/ui/popover/Popover.tsx +1 -1
- package/src/components/ui/radio/Radio.stories.tsx +1 -1
- package/src/components/ui/section/Section.stories.tsx +4 -4
- package/src/components/ui/select/Select.tsx +1 -1
- package/src/components/ui/stack/Stack.stories.tsx +4 -4
- package/src/components/ui/tabs/Tabs.tsx +2 -2
- package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +1 -1
- package/src/components/ui/toolbar-button/ToolbarButton.tsx +1 -1
- package/src/examples/react-hook-form/ControlledFields.tsx +171 -0
- package/src/examples/react-hook-form/ReactHookForm.stories.tsx +51 -0
- package/src/examples/react-hook-form/ReactHookForm.test.tsx +61 -0
- package/src/examples/react-hook-form/RhfContactForm.tsx +99 -0
- package/src/stories/PresetShowcase.stories.tsx +15 -15
- package/src/theme/config.completeness.test.ts +77 -0
- package/src/theme/config.ts +151 -0
- package/src/theme/index.ts +2 -1
- package/src/theme/manifest/css-property-manifest.example.json +79 -0
- package/src/theme/manifest/css-property-manifest.schema.json +93 -0
- package/src/theme/manifest/css-property-manifest.test.ts +81 -0
- package/src/theme/manifest/css-property-manifest.types.ts +49 -0
- package/src/theme/manifest/css-property-manifest.validate.ts +84 -0
- package/src/theme/manifest/design-freeze.test.ts +155 -0
- package/src/theme/manifest/extract-css-properties.fixture.html +29 -0
- package/src/theme/manifest/extract-css-properties.test.ts +112 -0
- package/src/theme/manifest/index.ts +11 -0
- package/src/theme/tokens-css.test.ts +28 -0
- package/src/theme/tokens-css.ts +36 -0
- package/src/theme/workout-tokens.ts +59 -0
- package/src/utils/index.ts +10 -0
- package/src/utils/workout-format.test.ts +81 -0
- package/src/utils/workout-format.ts +83 -0
- package/dist/chunk-UXVRPOME.mjs.map +0 -1
package/src/theme/config.ts
CHANGED
|
@@ -7,6 +7,35 @@
|
|
|
7
7
|
|
|
8
8
|
import { semanticColorsLight, semanticColorsDark, type ThemeMode } from './tokens/semantic'
|
|
9
9
|
|
|
10
|
+
// Theme-independent custom properties: identical in light and dark in
|
|
11
|
+
// global.css (RGB glow decompositions, font families, animation tokens). Kept
|
|
12
|
+
// as literals since they are not part of the semantic COLOR maps, and spread
|
|
13
|
+
// into both theme maps so codegen mirrors global.css exactly.
|
|
14
|
+
const themeIndependentCSSVars = {
|
|
15
|
+
// RGB decomposed values for glow shadows
|
|
16
|
+
'--color-brand-primary-rgb': '255, 121, 0',
|
|
17
|
+
'--color-brand-secondary-rgb': '64, 109, 135',
|
|
18
|
+
'--color-status-success-rgb': '20, 184, 166',
|
|
19
|
+
'--color-status-error-rgb': '209, 67, 67',
|
|
20
|
+
'--color-status-warning-rgb': '255, 176, 32',
|
|
21
|
+
'--color-status-info-rgb': '33, 150, 243',
|
|
22
|
+
'--color-background-base-rgb': '16, 16, 16',
|
|
23
|
+
|
|
24
|
+
// Font families
|
|
25
|
+
'--font-family-sans': "'Inter'",
|
|
26
|
+
'--font-family-body': "'Nunito Sans'",
|
|
27
|
+
'--font-family-heading': "'Space Grotesk'",
|
|
28
|
+
'--font-family-mono': "'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace",
|
|
29
|
+
|
|
30
|
+
// Animation tokens
|
|
31
|
+
'--ease-out': 'cubic-bezier(0.22, 1, 0.36, 1)',
|
|
32
|
+
'--ease-in-out': 'cubic-bezier(0.65, 0, 0.35, 1)',
|
|
33
|
+
'--ease-spring': 'cubic-bezier(0.34, 1.56, 0.64, 1)',
|
|
34
|
+
'--duration-fast': '150ms',
|
|
35
|
+
'--duration-normal': '250ms',
|
|
36
|
+
'--duration-slow': '400ms',
|
|
37
|
+
} as const
|
|
38
|
+
|
|
10
39
|
// CSS custom properties for light mode
|
|
11
40
|
export const lightThemeCSSVars = {
|
|
12
41
|
'--color-brand-primary': semanticColorsLight['brand-primary'],
|
|
@@ -57,6 +86,67 @@ export const lightThemeCSSVars = {
|
|
|
57
86
|
'--color-interactive-disabled': semanticColorsLight['interactive-disabled'],
|
|
58
87
|
|
|
59
88
|
'--color-divider': semanticColorsLight['divider'],
|
|
89
|
+
|
|
90
|
+
'--color-brand-primary-hover': semanticColorsLight['brand-primary-hover'],
|
|
91
|
+
'--color-brand-primary-active': semanticColorsLight['brand-primary-active'],
|
|
92
|
+
'--color-brand-secondary-hover': semanticColorsLight['brand-secondary-hover'],
|
|
93
|
+
'--color-brand-secondary-active': semanticColorsLight['brand-secondary-active'],
|
|
94
|
+
|
|
95
|
+
'--color-status-success-light': semanticColorsLight['status-success-light'],
|
|
96
|
+
'--color-status-success-dark': semanticColorsLight['status-success-dark'],
|
|
97
|
+
'--color-status-error-light': semanticColorsLight['status-error-light'],
|
|
98
|
+
'--color-status-error-dark': semanticColorsLight['status-error-dark'],
|
|
99
|
+
'--color-status-warning-light': semanticColorsLight['status-warning-light'],
|
|
100
|
+
'--color-status-warning-dark': semanticColorsLight['status-warning-dark'],
|
|
101
|
+
'--color-status-info-light': semanticColorsLight['status-info-light'],
|
|
102
|
+
'--color-status-info-dark': semanticColorsLight['status-info-dark'],
|
|
103
|
+
|
|
104
|
+
'--color-on-status-success': semanticColorsLight['on-status-success'],
|
|
105
|
+
'--color-on-status-error': semanticColorsLight['on-status-error'],
|
|
106
|
+
'--color-on-status-warning': semanticColorsLight['on-status-warning'],
|
|
107
|
+
'--color-on-status-info': semanticColorsLight['on-status-info'],
|
|
108
|
+
|
|
109
|
+
'--color-result-improve': semanticColorsLight['result-improve'],
|
|
110
|
+
'--color-result-improve-light': semanticColorsLight['result-improve-light'],
|
|
111
|
+
'--color-result-improve-dark': semanticColorsLight['result-improve-dark'],
|
|
112
|
+
'--color-result-degrade': semanticColorsLight['result-degrade'],
|
|
113
|
+
'--color-result-degrade-light': semanticColorsLight['result-degrade-light'],
|
|
114
|
+
'--color-result-degrade-dark': semanticColorsLight['result-degrade-dark'],
|
|
115
|
+
'--color-result-inconclusive': semanticColorsLight['result-inconclusive'],
|
|
116
|
+
'--color-result-inconclusive-light': semanticColorsLight['result-inconclusive-light'],
|
|
117
|
+
'--color-result-neutral': semanticColorsLight['result-neutral'],
|
|
118
|
+
|
|
119
|
+
'--color-on-result-improve': semanticColorsLight['on-result-improve'],
|
|
120
|
+
'--color-on-result-degrade': semanticColorsLight['on-result-degrade'],
|
|
121
|
+
'--color-on-result-inconclusive': semanticColorsLight['on-result-inconclusive'],
|
|
122
|
+
|
|
123
|
+
'--color-data-1': semanticColorsLight['data-1'],
|
|
124
|
+
'--color-data-2': semanticColorsLight['data-2'],
|
|
125
|
+
'--color-data-3': semanticColorsLight['data-3'],
|
|
126
|
+
'--color-data-4': semanticColorsLight['data-4'],
|
|
127
|
+
'--color-data-5': semanticColorsLight['data-5'],
|
|
128
|
+
'--color-data-6': semanticColorsLight['data-6'],
|
|
129
|
+
'--color-data-7': semanticColorsLight['data-7'],
|
|
130
|
+
'--color-data-8': semanticColorsLight['data-8'],
|
|
131
|
+
'--color-data-9': semanticColorsLight['data-9'],
|
|
132
|
+
'--color-data-10': semanticColorsLight['data-10'],
|
|
133
|
+
|
|
134
|
+
'--color-text-link-hover': semanticColorsLight['text-link-hover'],
|
|
135
|
+
|
|
136
|
+
'--color-surface-overlay': semanticColorsLight['surface-overlay'],
|
|
137
|
+
'--color-surface-input': semanticColorsLight['surface-input'],
|
|
138
|
+
|
|
139
|
+
'--color-border-input': semanticColorsLight['border-input'],
|
|
140
|
+
'--color-border-input-hover': semanticColorsLight['border-input-hover'],
|
|
141
|
+
'--color-border-input-focus': semanticColorsLight['border-input-focus'],
|
|
142
|
+
'--color-border-input-error': semanticColorsLight['border-input-error'],
|
|
143
|
+
|
|
144
|
+
'--color-interactive-disabled-text': semanticColorsLight['interactive-disabled-text'],
|
|
145
|
+
|
|
146
|
+
'--color-avatar-background': semanticColorsLight['avatar-background'],
|
|
147
|
+
'--color-avatar-text': semanticColorsLight['avatar-text'],
|
|
148
|
+
|
|
149
|
+
...themeIndependentCSSVars,
|
|
60
150
|
} as const
|
|
61
151
|
|
|
62
152
|
// CSS custom properties for dark mode (default)
|
|
@@ -109,6 +199,67 @@ export const darkThemeCSSVars = {
|
|
|
109
199
|
'--color-interactive-disabled': semanticColorsDark['interactive-disabled'],
|
|
110
200
|
|
|
111
201
|
'--color-divider': semanticColorsDark['divider'],
|
|
202
|
+
|
|
203
|
+
'--color-brand-primary-hover': semanticColorsDark['brand-primary-hover'],
|
|
204
|
+
'--color-brand-primary-active': semanticColorsDark['brand-primary-active'],
|
|
205
|
+
'--color-brand-secondary-hover': semanticColorsDark['brand-secondary-hover'],
|
|
206
|
+
'--color-brand-secondary-active': semanticColorsDark['brand-secondary-active'],
|
|
207
|
+
|
|
208
|
+
'--color-status-success-light': semanticColorsDark['status-success-light'],
|
|
209
|
+
'--color-status-success-dark': semanticColorsDark['status-success-dark'],
|
|
210
|
+
'--color-status-error-light': semanticColorsDark['status-error-light'],
|
|
211
|
+
'--color-status-error-dark': semanticColorsDark['status-error-dark'],
|
|
212
|
+
'--color-status-warning-light': semanticColorsDark['status-warning-light'],
|
|
213
|
+
'--color-status-warning-dark': semanticColorsDark['status-warning-dark'],
|
|
214
|
+
'--color-status-info-light': semanticColorsDark['status-info-light'],
|
|
215
|
+
'--color-status-info-dark': semanticColorsDark['status-info-dark'],
|
|
216
|
+
|
|
217
|
+
'--color-on-status-success': semanticColorsDark['on-status-success'],
|
|
218
|
+
'--color-on-status-error': semanticColorsDark['on-status-error'],
|
|
219
|
+
'--color-on-status-warning': semanticColorsDark['on-status-warning'],
|
|
220
|
+
'--color-on-status-info': semanticColorsDark['on-status-info'],
|
|
221
|
+
|
|
222
|
+
'--color-result-improve': semanticColorsDark['result-improve'],
|
|
223
|
+
'--color-result-improve-light': semanticColorsDark['result-improve-light'],
|
|
224
|
+
'--color-result-improve-dark': semanticColorsDark['result-improve-dark'],
|
|
225
|
+
'--color-result-degrade': semanticColorsDark['result-degrade'],
|
|
226
|
+
'--color-result-degrade-light': semanticColorsDark['result-degrade-light'],
|
|
227
|
+
'--color-result-degrade-dark': semanticColorsDark['result-degrade-dark'],
|
|
228
|
+
'--color-result-inconclusive': semanticColorsDark['result-inconclusive'],
|
|
229
|
+
'--color-result-inconclusive-light': semanticColorsDark['result-inconclusive-light'],
|
|
230
|
+
'--color-result-neutral': semanticColorsDark['result-neutral'],
|
|
231
|
+
|
|
232
|
+
'--color-on-result-improve': semanticColorsDark['on-result-improve'],
|
|
233
|
+
'--color-on-result-degrade': semanticColorsDark['on-result-degrade'],
|
|
234
|
+
'--color-on-result-inconclusive': semanticColorsDark['on-result-inconclusive'],
|
|
235
|
+
|
|
236
|
+
'--color-data-1': semanticColorsDark['data-1'],
|
|
237
|
+
'--color-data-2': semanticColorsDark['data-2'],
|
|
238
|
+
'--color-data-3': semanticColorsDark['data-3'],
|
|
239
|
+
'--color-data-4': semanticColorsDark['data-4'],
|
|
240
|
+
'--color-data-5': semanticColorsDark['data-5'],
|
|
241
|
+
'--color-data-6': semanticColorsDark['data-6'],
|
|
242
|
+
'--color-data-7': semanticColorsDark['data-7'],
|
|
243
|
+
'--color-data-8': semanticColorsDark['data-8'],
|
|
244
|
+
'--color-data-9': semanticColorsDark['data-9'],
|
|
245
|
+
'--color-data-10': semanticColorsDark['data-10'],
|
|
246
|
+
|
|
247
|
+
'--color-text-link-hover': semanticColorsDark['text-link-hover'],
|
|
248
|
+
|
|
249
|
+
'--color-surface-overlay': semanticColorsDark['surface-overlay'],
|
|
250
|
+
'--color-surface-input': semanticColorsDark['surface-input'],
|
|
251
|
+
|
|
252
|
+
'--color-border-input': semanticColorsDark['border-input'],
|
|
253
|
+
'--color-border-input-hover': semanticColorsDark['border-input-hover'],
|
|
254
|
+
'--color-border-input-focus': semanticColorsDark['border-input-focus'],
|
|
255
|
+
'--color-border-input-error': semanticColorsDark['border-input-error'],
|
|
256
|
+
|
|
257
|
+
'--color-interactive-disabled-text': semanticColorsDark['interactive-disabled-text'],
|
|
258
|
+
|
|
259
|
+
'--color-avatar-background': semanticColorsDark['avatar-background'],
|
|
260
|
+
'--color-avatar-text': semanticColorsDark['avatar-text'],
|
|
261
|
+
|
|
262
|
+
...themeIndependentCSSVars,
|
|
112
263
|
} as const
|
|
113
264
|
|
|
114
265
|
// Helper to get CSS vars for a theme mode
|
package/src/theme/index.ts
CHANGED
|
@@ -3,11 +3,12 @@ export * from './tokens'
|
|
|
3
3
|
export * from './config'
|
|
4
4
|
export * from './shadows'
|
|
5
5
|
export * from './elevation'
|
|
6
|
+
export * from './manifest'
|
|
6
7
|
|
|
7
8
|
// Re-export commonly used items
|
|
8
9
|
export { getSemanticColors, type ThemeMode } from './tokens/semantic'
|
|
9
10
|
export { getThemeCSSVars, gluestackConfig } from './config'
|
|
10
|
-
export {
|
|
11
|
+
export {
|
|
11
12
|
neumorphicShadows,
|
|
12
13
|
// Color math utilities (HSV-based)
|
|
13
14
|
lighten,
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"component": "button",
|
|
3
|
+
"description": "Illustrative example conforming to css-property-manifest.schema.json. Values are derived from Button.tsx's variant/color/size style maps and the semantic token definitions they reference, not measured via getComputedStyle — a follow-up ticket generates verified manifests from the rendered Storybook DOM.",
|
|
4
|
+
"baseVariant": { "variant": "solid", "color": "primary", "size": "md" },
|
|
5
|
+
"properties": [
|
|
6
|
+
{
|
|
7
|
+
"property": "backgroundColor",
|
|
8
|
+
"token": "brand-primary",
|
|
9
|
+
"cssVariable": "--color-brand-primary",
|
|
10
|
+
"resolvedValue": "rgb(255, 121, 0)",
|
|
11
|
+
"variantOverrides": [
|
|
12
|
+
{
|
|
13
|
+
"variant": { "variant": "outline" },
|
|
14
|
+
"token": null,
|
|
15
|
+
"cssVariable": null,
|
|
16
|
+
"resolvedValue": "rgba(0, 0, 0, 0)"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"variant": { "variant": "solid", "color": "error" },
|
|
20
|
+
"token": "status-error",
|
|
21
|
+
"cssVariable": "--color-status-error",
|
|
22
|
+
"resolvedValue": "rgb(209, 67, 67)"
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"property": "color",
|
|
28
|
+
"token": null,
|
|
29
|
+
"resolvedValue": "rgb(255, 255, 255)",
|
|
30
|
+
"variantOverrides": [
|
|
31
|
+
{
|
|
32
|
+
"variant": { "variant": "outline" },
|
|
33
|
+
"token": "brand-primary",
|
|
34
|
+
"cssVariable": "--color-brand-primary",
|
|
35
|
+
"resolvedValue": "rgb(255, 121, 0)"
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"property": "borderColor",
|
|
41
|
+
"token": null,
|
|
42
|
+
"resolvedValue": "rgba(0, 0, 0, 0)",
|
|
43
|
+
"variantOverrides": [
|
|
44
|
+
{
|
|
45
|
+
"variant": { "variant": "outline" },
|
|
46
|
+
"token": "brand-primary",
|
|
47
|
+
"cssVariable": "--color-brand-primary",
|
|
48
|
+
"resolvedValue": "rgb(255, 121, 0)"
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"property": "borderWidth",
|
|
54
|
+
"token": null,
|
|
55
|
+
"resolvedValue": "0px",
|
|
56
|
+
"variantOverrides": [
|
|
57
|
+
{
|
|
58
|
+
"variant": { "variant": "outline" },
|
|
59
|
+
"token": null,
|
|
60
|
+
"resolvedValue": "2px"
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"property": "borderRadius",
|
|
66
|
+
"token": null,
|
|
67
|
+
"resolvedValue": "6px"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"property": "paddingLeft",
|
|
71
|
+
"token": null,
|
|
72
|
+
"resolvedValue": "20px",
|
|
73
|
+
"variantOverrides": [
|
|
74
|
+
{ "variant": { "size": "sm" }, "token": null, "resolvedValue": "16px" },
|
|
75
|
+
{ "variant": { "size": "lg" }, "token": null, "resolvedValue": "24px" }
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://titan-design.dev/schemas/css-property-manifest.schema.json",
|
|
4
|
+
"title": "Component CSS Property Manifest",
|
|
5
|
+
"description": "Per-component record of visual CSS properties, the design token each one resolves from, the expected computed style value, and any per-variant overrides. Generated manifests serve as fixtures for Playwright computed-style assertions (see packages/ui/tests/visual).",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["component", "properties"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"component": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"pattern": "^[a-z][a-z0-9-]*$",
|
|
13
|
+
"description": "Component identifier matching its src/components/ui/{component} directory and Storybook id (e.g. \"button\")."
|
|
14
|
+
},
|
|
15
|
+
"description": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"description": "Optional human-readable note about what this manifest covers."
|
|
18
|
+
},
|
|
19
|
+
"baseVariant": {
|
|
20
|
+
"$ref": "#/$defs/variantAxes",
|
|
21
|
+
"description": "The variant axis values (e.g. { \"variant\": \"solid\", \"color\": \"primary\", \"size\": \"md\" }) that each property's top-level resolvedValue was captured against. Axis names must match the component's own prop names."
|
|
22
|
+
},
|
|
23
|
+
"properties": {
|
|
24
|
+
"type": "array",
|
|
25
|
+
"minItems": 1,
|
|
26
|
+
"items": { "$ref": "#/$defs/cssPropertyEntry" },
|
|
27
|
+
"description": "One entry per visual CSS property tracked for this component."
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"$defs": {
|
|
31
|
+
"variantAxes": {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"minProperties": 1,
|
|
34
|
+
"additionalProperties": { "type": "string" },
|
|
35
|
+
"description": "A set of variant axis name/value pairs (e.g. variant, color, size, state) as accepted by the component's props."
|
|
36
|
+
},
|
|
37
|
+
"cssPropertyEntry": {
|
|
38
|
+
"type": "object",
|
|
39
|
+
"required": ["property", "token", "resolvedValue"],
|
|
40
|
+
"additionalProperties": false,
|
|
41
|
+
"properties": {
|
|
42
|
+
"property": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"pattern": "^[a-zA-Z]+$",
|
|
45
|
+
"description": "CSS property name in camelCase as returned by window.getComputedStyle (e.g. backgroundColor, borderColor, fontSize)."
|
|
46
|
+
},
|
|
47
|
+
"token": {
|
|
48
|
+
"type": ["string", "null"],
|
|
49
|
+
"description": "Semantic design token key this property is sourced from (e.g. \"brand-primary\"), matching a key in packages/ui/src/theme/tokens/semantic.ts. Use null when the value is a literal not backed by a token."
|
|
50
|
+
},
|
|
51
|
+
"cssVariable": {
|
|
52
|
+
"type": ["string", "null"],
|
|
53
|
+
"description": "CSS custom property backing the token (e.g. \"--color-brand-primary\"), as declared in packages/ui/src/theme/global.css. Omit or null when no CSS variable is involved."
|
|
54
|
+
},
|
|
55
|
+
"resolvedValue": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"minLength": 1,
|
|
58
|
+
"description": "Expected computed style value for baseVariant, in the format getComputedStyle returns it (e.g. \"rgb(255, 121, 0)\", \"8px\")."
|
|
59
|
+
},
|
|
60
|
+
"variantOverrides": {
|
|
61
|
+
"type": "array",
|
|
62
|
+
"default": [],
|
|
63
|
+
"items": { "$ref": "#/$defs/variantOverride" },
|
|
64
|
+
"description": "Overrides of token/resolvedValue for specific variant axis combinations, merged onto baseVariant."
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"variantOverride": {
|
|
69
|
+
"type": "object",
|
|
70
|
+
"required": ["variant", "resolvedValue"],
|
|
71
|
+
"additionalProperties": false,
|
|
72
|
+
"properties": {
|
|
73
|
+
"variant": {
|
|
74
|
+
"$ref": "#/$defs/variantAxes",
|
|
75
|
+
"description": "Partial variant axis values that trigger this override (e.g. { \"variant\": \"outline\" } or { \"color\": \"error\", \"state\": \"hover\" })."
|
|
76
|
+
},
|
|
77
|
+
"token": {
|
|
78
|
+
"type": ["string", "null"],
|
|
79
|
+
"description": "Semantic token key for this override, or null for a literal value."
|
|
80
|
+
},
|
|
81
|
+
"cssVariable": {
|
|
82
|
+
"type": ["string", "null"],
|
|
83
|
+
"description": "CSS custom property backing the token for this override."
|
|
84
|
+
},
|
|
85
|
+
"resolvedValue": {
|
|
86
|
+
"type": "string",
|
|
87
|
+
"minLength": 1,
|
|
88
|
+
"description": "Expected computed style value when the variant axes match."
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { readFileSync } from 'fs'
|
|
3
|
+
import { join } from 'path'
|
|
4
|
+
import {
|
|
5
|
+
isCssPropertyManifest,
|
|
6
|
+
validateCssPropertyManifest,
|
|
7
|
+
} from './css-property-manifest.validate'
|
|
8
|
+
|
|
9
|
+
const readJson = (fileName: string): unknown =>
|
|
10
|
+
JSON.parse(readFileSync(join(__dirname, fileName), 'utf-8'))
|
|
11
|
+
|
|
12
|
+
describe('css-property-manifest.schema.json', () => {
|
|
13
|
+
it('is well-formed JSON Schema with the documented top-level shape', () => {
|
|
14
|
+
const schema = readJson('css-property-manifest.schema.json') as Record<string, unknown>
|
|
15
|
+
|
|
16
|
+
expect(schema.$schema).toBe('https://json-schema.org/draft/2020-12/schema')
|
|
17
|
+
expect(schema.required).toEqual(['component', 'properties'])
|
|
18
|
+
expect(schema.$defs).toHaveProperty('cssPropertyEntry')
|
|
19
|
+
expect(schema.$defs).toHaveProperty('variantOverride')
|
|
20
|
+
})
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
describe('validateCssPropertyManifest', () => {
|
|
24
|
+
it('accepts the button example manifest', () => {
|
|
25
|
+
const example = readJson('css-property-manifest.example.json')
|
|
26
|
+
|
|
27
|
+
const result = validateCssPropertyManifest(example)
|
|
28
|
+
|
|
29
|
+
expect(result).toEqual({ valid: true, errors: [] })
|
|
30
|
+
expect(isCssPropertyManifest(example)).toBe(true)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('rejects a manifest missing required top-level fields', () => {
|
|
34
|
+
const result = validateCssPropertyManifest({ component: 'button' })
|
|
35
|
+
|
|
36
|
+
expect(result.valid).toBe(false)
|
|
37
|
+
expect(result.errors).toContain('properties must be a non-empty array')
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('rejects a property entry missing a resolvedValue', () => {
|
|
41
|
+
const candidate = {
|
|
42
|
+
component: 'button',
|
|
43
|
+
properties: [{ property: 'backgroundColor', token: 'brand-primary' }],
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const result = validateCssPropertyManifest(candidate)
|
|
47
|
+
|
|
48
|
+
expect(result.valid).toBe(false)
|
|
49
|
+
expect(result.errors).toContain('properties[0].resolvedValue must be a non-empty string')
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('rejects a variantOverride missing variant axes', () => {
|
|
53
|
+
const candidate = {
|
|
54
|
+
component: 'button',
|
|
55
|
+
properties: [
|
|
56
|
+
{
|
|
57
|
+
property: 'backgroundColor',
|
|
58
|
+
token: 'brand-primary',
|
|
59
|
+
resolvedValue: 'rgb(255, 121, 0)',
|
|
60
|
+
variantOverrides: [{ resolvedValue: 'rgba(0, 0, 0, 0)' }],
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const result = validateCssPropertyManifest(candidate)
|
|
66
|
+
|
|
67
|
+
expect(result.valid).toBe(false)
|
|
68
|
+
expect(result.errors).toContain(
|
|
69
|
+
'properties[0].variantOverrides[0].variant must be a non-empty string-keyed object'
|
|
70
|
+
)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('allows token to be null for literal (non-tokenized) values', () => {
|
|
74
|
+
const candidate = {
|
|
75
|
+
component: 'button',
|
|
76
|
+
properties: [{ property: 'borderRadius', token: null, resolvedValue: '6px' }],
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
expect(validateCssPropertyManifest(candidate)).toEqual({ valid: true, errors: [] })
|
|
80
|
+
})
|
|
81
|
+
})
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript mirror of css-property-manifest.schema.json.
|
|
3
|
+
*
|
|
4
|
+
* A manifest records, per component, which visual CSS properties are tracked,
|
|
5
|
+
* which design token each one resolves from, the expected computed style
|
|
6
|
+
* value, and any per-variant overrides. Manifests are the fixtures consumed
|
|
7
|
+
* by Playwright computed-style assertions (see packages/ui/tests/visual).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** Variant axis name/value pairs, e.g. { variant: 'solid', color: 'primary', size: 'md' }. */
|
|
11
|
+
export type VariantAxes = Record<string, string>
|
|
12
|
+
|
|
13
|
+
/** A resolvedValue/token override that applies when the given variant axes match. */
|
|
14
|
+
export interface VariantOverride {
|
|
15
|
+
/** Partial variant axis values that trigger this override, merged onto baseVariant. */
|
|
16
|
+
variant: VariantAxes
|
|
17
|
+
/** Semantic token key for this override, or null for a literal (non-tokenized) value. */
|
|
18
|
+
token?: string | null
|
|
19
|
+
/** CSS custom property backing the token for this override, if any. */
|
|
20
|
+
cssVariable?: string | null
|
|
21
|
+
/** Expected getComputedStyle value when the variant axes match. */
|
|
22
|
+
resolvedValue: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** A single tracked CSS property for a component. */
|
|
26
|
+
export interface CssPropertyEntry {
|
|
27
|
+
/** CSS property name in camelCase, as returned by window.getComputedStyle (e.g. "backgroundColor"). */
|
|
28
|
+
property: string
|
|
29
|
+
/** Semantic token key this property is sourced from, or null if not backed by a token. */
|
|
30
|
+
token: string | null
|
|
31
|
+
/** CSS custom property backing the token (e.g. "--color-brand-primary"), if any. */
|
|
32
|
+
cssVariable?: string | null
|
|
33
|
+
/** Expected computed style value for baseVariant. */
|
|
34
|
+
resolvedValue: string
|
|
35
|
+
/** Overrides of token/resolvedValue for specific variant axis combinations. */
|
|
36
|
+
variantOverrides?: VariantOverride[]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** The full per-component CSS property manifest. */
|
|
40
|
+
export interface CssPropertyManifest {
|
|
41
|
+
/** Component identifier matching its src/components/ui/{component} directory and Storybook id. */
|
|
42
|
+
component: string
|
|
43
|
+
/** Optional human-readable note about what this manifest covers. */
|
|
44
|
+
description?: string
|
|
45
|
+
/** Variant axis values each property's top-level resolvedValue was captured against. */
|
|
46
|
+
baseVariant?: VariantAxes
|
|
47
|
+
/** Tracked CSS properties for this component. */
|
|
48
|
+
properties: CssPropertyEntry[]
|
|
49
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { CssPropertyManifest, VariantAxes } from './css-property-manifest.types'
|
|
2
|
+
|
|
3
|
+
/** Result of validating a candidate manifest: valid iff errors is empty. */
|
|
4
|
+
export interface ManifestValidationResult {
|
|
5
|
+
valid: boolean
|
|
6
|
+
errors: string[]
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const isPlainObject = (value: unknown): value is Record<string, unknown> =>
|
|
10
|
+
typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
11
|
+
|
|
12
|
+
const isVariantAxes = (value: unknown): value is VariantAxes =>
|
|
13
|
+
isPlainObject(value) &&
|
|
14
|
+
Object.keys(value).length > 0 &&
|
|
15
|
+
Object.values(value).every((axisValue) => typeof axisValue === 'string')
|
|
16
|
+
|
|
17
|
+
function collectVariantOverrideErrors(override: unknown, path: string): string[] {
|
|
18
|
+
if (!isPlainObject(override)) return [`${path} must be an object`]
|
|
19
|
+
const errors: string[] = []
|
|
20
|
+
if (!isVariantAxes(override.variant))
|
|
21
|
+
errors.push(`${path}.variant must be a non-empty string-keyed object`)
|
|
22
|
+
if (
|
|
23
|
+
override.token !== undefined &&
|
|
24
|
+
override.token !== null &&
|
|
25
|
+
typeof override.token !== 'string'
|
|
26
|
+
) {
|
|
27
|
+
errors.push(`${path}.token must be a string or null`)
|
|
28
|
+
}
|
|
29
|
+
if (typeof override.resolvedValue !== 'string' || override.resolvedValue.length === 0) {
|
|
30
|
+
errors.push(`${path}.resolvedValue must be a non-empty string`)
|
|
31
|
+
}
|
|
32
|
+
return errors
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function collectPropertyErrors(entry: unknown, path: string): string[] {
|
|
36
|
+
if (!isPlainObject(entry)) return [`${path} must be an object`]
|
|
37
|
+
const errors: string[] = []
|
|
38
|
+
if (typeof entry.property !== 'string' || entry.property.length === 0) {
|
|
39
|
+
errors.push(`${path}.property must be a non-empty string`)
|
|
40
|
+
}
|
|
41
|
+
if (entry.token !== null && typeof entry.token !== 'string') {
|
|
42
|
+
errors.push(`${path}.token must be a string or null`)
|
|
43
|
+
}
|
|
44
|
+
if (typeof entry.resolvedValue !== 'string' || entry.resolvedValue.length === 0) {
|
|
45
|
+
errors.push(`${path}.resolvedValue must be a non-empty string`)
|
|
46
|
+
}
|
|
47
|
+
if (entry.variantOverrides !== undefined) {
|
|
48
|
+
if (!Array.isArray(entry.variantOverrides)) {
|
|
49
|
+
errors.push(`${path}.variantOverrides must be an array`)
|
|
50
|
+
} else {
|
|
51
|
+
entry.variantOverrides.forEach((override, i) =>
|
|
52
|
+
errors.push(...collectVariantOverrideErrors(override, `${path}.variantOverrides[${i}]`))
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return errors
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Structurally validates a candidate value against the CssPropertyManifest shape. */
|
|
60
|
+
export function validateCssPropertyManifest(candidate: unknown): ManifestValidationResult {
|
|
61
|
+
if (!isPlainObject(candidate)) return { valid: false, errors: ['manifest must be an object'] }
|
|
62
|
+
|
|
63
|
+
const errors: string[] = []
|
|
64
|
+
if (typeof candidate.component !== 'string' || candidate.component.length === 0) {
|
|
65
|
+
errors.push('component must be a non-empty string')
|
|
66
|
+
}
|
|
67
|
+
if (candidate.baseVariant !== undefined && !isVariantAxes(candidate.baseVariant)) {
|
|
68
|
+
errors.push('baseVariant must be a non-empty string-keyed object')
|
|
69
|
+
}
|
|
70
|
+
if (!Array.isArray(candidate.properties) || candidate.properties.length === 0) {
|
|
71
|
+
errors.push('properties must be a non-empty array')
|
|
72
|
+
} else {
|
|
73
|
+
candidate.properties.forEach((entry, i) =>
|
|
74
|
+
errors.push(...collectPropertyErrors(entry, `properties[${i}]`))
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return { valid: errors.length === 0, errors }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Type guard variant of validateCssPropertyManifest for use in narrowing contexts. */
|
|
82
|
+
export function isCssPropertyManifest(candidate: unknown): candidate is CssPropertyManifest {
|
|
83
|
+
return validateCssPropertyManifest(candidate).valid
|
|
84
|
+
}
|