@ultimat3/ui 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +108 -0
- package/package.json +45 -0
- package/src/a11y.ts +187 -0
- package/src/components/Alert.module.scss +70 -0
- package/src/components/Alert.tsx +49 -0
- package/src/components/Avatar.module.scss +66 -0
- package/src/components/Avatar.tsx +56 -0
- package/src/components/Badge.module.scss +94 -0
- package/src/components/Badge.tsx +34 -0
- package/src/components/Breadcrumb.module.scss +45 -0
- package/src/components/Breadcrumb.tsx +48 -0
- package/src/components/Button.module.scss +146 -0
- package/src/components/Button.tsx +66 -0
- package/src/components/Card.module.scss +69 -0
- package/src/components/Card.tsx +40 -0
- package/src/components/Checkbox.module.scss +90 -0
- package/src/components/Checkbox.tsx +57 -0
- package/src/components/Container.module.scss +31 -0
- package/src/components/Container.tsx +29 -0
- package/src/components/DataTable.module.scss +33 -0
- package/src/components/DataTable.tsx +149 -0
- package/src/components/DateTime.module.scss +9 -0
- package/src/components/DateTime.tsx +45 -0
- package/src/components/Dialog.module.scss +83 -0
- package/src/components/Dialog.tsx +75 -0
- package/src/components/Divider.module.scss +39 -0
- package/src/components/Divider.tsx +38 -0
- package/src/components/Drawer.module.scss +89 -0
- package/src/components/Drawer.tsx +76 -0
- package/src/components/EmptyState.module.scss +34 -0
- package/src/components/EmptyState.tsx +35 -0
- package/src/components/ErrorState.module.scss +77 -0
- package/src/components/ErrorState.tsx +84 -0
- package/src/components/Field.module.scss +41 -0
- package/src/components/Field.tsx +86 -0
- package/src/components/Form.module.scss +21 -0
- package/src/components/Form.tsx +51 -0
- package/src/components/Grid.module.scss +9 -0
- package/src/components/Grid.tsx +40 -0
- package/src/components/IconButton.module.scss +99 -0
- package/src/components/IconButton.tsx +53 -0
- package/src/components/Image.module.scss +14 -0
- package/src/components/Image.tsx +59 -0
- package/src/components/Input.module.scss +55 -0
- package/src/components/Input.tsx +84 -0
- package/src/components/Link.module.scss +42 -0
- package/src/components/Link.tsx +46 -0
- package/src/components/LocaleSwitcher.module.scss +28 -0
- package/src/components/LocaleSwitcher.tsx +61 -0
- package/src/components/Menu.module.scss +63 -0
- package/src/components/Menu.tsx +115 -0
- package/src/components/Money.module.scss +11 -0
- package/src/components/Money.tsx +49 -0
- package/src/components/Pagination.module.scss +15 -0
- package/src/components/Pagination.tsx +77 -0
- package/src/components/Popover.module.scss +59 -0
- package/src/components/Popover.tsx +76 -0
- package/src/components/Radio.module.scss +87 -0
- package/src/components/Radio.tsx +64 -0
- package/src/components/RelativeTime.module.scss +7 -0
- package/src/components/RelativeTime.tsx +49 -0
- package/src/components/Select.module.scss +51 -0
- package/src/components/Select.tsx +63 -0
- package/src/components/Skeleton.module.scss +41 -0
- package/src/components/Skeleton.tsx +34 -0
- package/src/components/Spinner.module.scss +33 -0
- package/src/components/Spinner.tsx +35 -0
- package/src/components/Stack.module.scss +21 -0
- package/src/components/Stack.tsx +49 -0
- package/src/components/Switch.module.scss +66 -0
- package/src/components/Switch.tsx +44 -0
- package/src/components/Table.module.scss +77 -0
- package/src/components/Table.tsx +42 -0
- package/src/components/Tabs.module.scss +71 -0
- package/src/components/Tabs.tsx +88 -0
- package/src/components/Text.module.scss +37 -0
- package/src/components/Text.tsx +57 -0
- package/src/components/Textarea.module.scss +15 -0
- package/src/components/Textarea.tsx +48 -0
- package/src/components/ThemeToggle.module.scss +23 -0
- package/src/components/ThemeToggle.tsx +93 -0
- package/src/components/Toast.module.scss +92 -0
- package/src/components/Toast.tsx +74 -0
- package/src/components/Tooltip.module.scss +41 -0
- package/src/components/Tooltip.tsx +32 -0
- package/src/components/date-time-view.ts +68 -0
- package/src/components/image-source.ts +150 -0
- package/src/components/money-view.ts +50 -0
- package/src/components/relative-time-view.ts +45 -0
- package/src/components/sort-state.ts +28 -0
- package/src/components/variants.ts +17 -0
- package/src/cx.ts +42 -0
- package/src/errors.ts +72 -0
- package/src/global.scss +5 -0
- package/src/i18n-keys.ts +34 -0
- package/src/index.ts +199 -0
- package/src/scss.d.ts +7 -0
- package/src/theme/context.ts +65 -0
- package/src/theme/inline-script.ts +44 -0
- package/src/theme/provider.tsx +56 -0
- package/src/theme/solid-adapter.ts +51 -0
- package/src/theme/theme.ts +116 -0
- package/src/tokens/_breakpoints.scss +10 -0
- package/src/tokens/_colors.scss +95 -0
- package/src/tokens/_index.scss +13 -0
- package/src/tokens/_mixins.scss +186 -0
- package/src/tokens/_motion.scss +36 -0
- package/src/tokens/_radius.scss +23 -0
- package/src/tokens/_shadow.scss +49 -0
- package/src/tokens/_space.scss +28 -0
- package/src/tokens/_typography.scss +83 -0
- package/src/tokens/_z.scss +27 -0
- package/src/tokens/reset.scss +144 -0
- package/src/tokens/theme.scss +19 -0
- package/src/tokens/tokens.ts +222 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// Modern reset. Assumes `theme.scss` has already run — every value here is a
|
|
2
|
+
// token, so the reset itself flips with the theme.
|
|
3
|
+
|
|
4
|
+
@use 'index' as t;
|
|
5
|
+
|
|
6
|
+
*,
|
|
7
|
+
*::before,
|
|
8
|
+
*::after {
|
|
9
|
+
box-sizing: border-box;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
* {
|
|
13
|
+
margin: 0;
|
|
14
|
+
padding: 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
html {
|
|
18
|
+
-webkit-text-size-adjust: 100%;
|
|
19
|
+
text-size-adjust: 100%;
|
|
20
|
+
tab-size: 4;
|
|
21
|
+
scroll-behavior: smooth;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
body {
|
|
25
|
+
min-block-size: 100dvh;
|
|
26
|
+
background: t.role('bg');
|
|
27
|
+
color: t.role('fg');
|
|
28
|
+
font-family: var(--font-sans);
|
|
29
|
+
font-size: t.text(md);
|
|
30
|
+
line-height: t.leading(normal);
|
|
31
|
+
-webkit-font-smoothing: antialiased;
|
|
32
|
+
text-rendering: optimizeLegibility;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
h1,
|
|
36
|
+
h2,
|
|
37
|
+
h3,
|
|
38
|
+
h4,
|
|
39
|
+
h5,
|
|
40
|
+
h6 {
|
|
41
|
+
color: t.role('fg-strong');
|
|
42
|
+
font-weight: t.weight(semibold);
|
|
43
|
+
line-height: t.leading(tight);
|
|
44
|
+
letter-spacing: var(--tracking-tight);
|
|
45
|
+
text-wrap: balance;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
h1 { font-size: t.text(3xl); }
|
|
49
|
+
h2 { font-size: t.text(2xl); }
|
|
50
|
+
h3 { font-size: t.text(xl); }
|
|
51
|
+
h4 { font-size: t.text(lg); }
|
|
52
|
+
|
|
53
|
+
p,
|
|
54
|
+
li {
|
|
55
|
+
text-wrap: pretty;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
a {
|
|
59
|
+
color: t.role('accent');
|
|
60
|
+
text-decoration-thickness: 1px;
|
|
61
|
+
text-underline-offset: 0.15em;
|
|
62
|
+
|
|
63
|
+
&:hover {
|
|
64
|
+
color: t.role('accent-strong');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Intrinsic ratio preserved and dimensions honoured so CLS stays at 0.
|
|
69
|
+
img,
|
|
70
|
+
picture,
|
|
71
|
+
video,
|
|
72
|
+
canvas,
|
|
73
|
+
svg {
|
|
74
|
+
display: block;
|
|
75
|
+
max-inline-size: 100%;
|
|
76
|
+
block-size: auto;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
input,
|
|
80
|
+
button,
|
|
81
|
+
textarea,
|
|
82
|
+
select {
|
|
83
|
+
font: inherit;
|
|
84
|
+
color: inherit;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
button {
|
|
88
|
+
background: none;
|
|
89
|
+
border: 0;
|
|
90
|
+
cursor: pointer;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
code,
|
|
94
|
+
kbd,
|
|
95
|
+
pre,
|
|
96
|
+
samp {
|
|
97
|
+
font-family: var(--font-mono);
|
|
98
|
+
font-size: 0.9em;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
hr {
|
|
102
|
+
border: 0;
|
|
103
|
+
border-block-start: 1px solid t.role('line');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
table {
|
|
107
|
+
border-collapse: collapse;
|
|
108
|
+
inline-size: 100%;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
:target {
|
|
112
|
+
scroll-margin-block-start: var(--space-12);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Themed keyboard focus for anything the component layer forgot.
|
|
116
|
+
:focus-visible {
|
|
117
|
+
outline: 2px solid t.role('accent');
|
|
118
|
+
outline-offset: 2px;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
:focus:not(:focus-visible) {
|
|
122
|
+
outline: none;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
::selection {
|
|
126
|
+
background: t.role('accent', 0.25);
|
|
127
|
+
color: t.role('fg-strong');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Global motion guard. Placed last so it wins over every component rule.
|
|
131
|
+
@media (prefers-reduced-motion: reduce) {
|
|
132
|
+
html {
|
|
133
|
+
scroll-behavior: auto;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
*,
|
|
137
|
+
*::before,
|
|
138
|
+
*::after {
|
|
139
|
+
animation-duration: 0.01ms !important;
|
|
140
|
+
animation-iteration-count: 1 !important;
|
|
141
|
+
transition-duration: 0.01ms !important;
|
|
142
|
+
scroll-behavior: auto !important;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// The one stylesheet that emits global custom properties. Import it once, in the
|
|
2
|
+
// document shell, before any component CSS. Nothing else in the package emits
|
|
3
|
+
// top-level CSS, so ordering is deterministic and dedupe is trivial.
|
|
4
|
+
|
|
5
|
+
@use 'colors';
|
|
6
|
+
@use 'motion';
|
|
7
|
+
@use 'radius';
|
|
8
|
+
@use 'shadow';
|
|
9
|
+
@use 'space';
|
|
10
|
+
@use 'typography';
|
|
11
|
+
@use 'z';
|
|
12
|
+
|
|
13
|
+
@include colors.emit;
|
|
14
|
+
@include shadow.emit;
|
|
15
|
+
@include space.emit;
|
|
16
|
+
@include radius.emit;
|
|
17
|
+
@include typography.emit;
|
|
18
|
+
@include motion.emit;
|
|
19
|
+
@include z.emit;
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
// Typed mirror of the SCSS token source, for consumers that cannot read CSS:
|
|
2
|
+
// charts, <canvas>, OG-image rendering, transactional email.
|
|
3
|
+
//
|
|
4
|
+
// `src/tokens/*.scss` is CANONICAL. This file is a hand-maintained mirror and
|
|
5
|
+
// `tokens.test.ts` (run by `x verify`) fails if the two ever disagree.
|
|
6
|
+
|
|
7
|
+
import { unknownTokenError } from '../errors';
|
|
8
|
+
|
|
9
|
+
export type Theme = 'light' | 'dark';
|
|
10
|
+
|
|
11
|
+
export const COLOR_ROLES = [
|
|
12
|
+
'bg',
|
|
13
|
+
'bg-soft',
|
|
14
|
+
'surface',
|
|
15
|
+
'surface-raised',
|
|
16
|
+
'fg',
|
|
17
|
+
'fg-strong',
|
|
18
|
+
'fg-muted',
|
|
19
|
+
'line',
|
|
20
|
+
'scrim',
|
|
21
|
+
'accent',
|
|
22
|
+
'accent-strong',
|
|
23
|
+
'accent-fg',
|
|
24
|
+
'success',
|
|
25
|
+
'success-soft',
|
|
26
|
+
'success-fg',
|
|
27
|
+
'warning',
|
|
28
|
+
'warning-soft',
|
|
29
|
+
'warning-fg',
|
|
30
|
+
'danger',
|
|
31
|
+
'danger-soft',
|
|
32
|
+
'danger-fg',
|
|
33
|
+
'info',
|
|
34
|
+
'info-soft',
|
|
35
|
+
'info-fg',
|
|
36
|
+
] as const;
|
|
37
|
+
|
|
38
|
+
export type ColorRole = (typeof COLOR_ROLES)[number];
|
|
39
|
+
|
|
40
|
+
/** Space-separated RGB channels, mirroring `_colors.scss`. */
|
|
41
|
+
export const colorTokens: Readonly<Record<Theme, Readonly<Record<ColorRole, string>>>> = {
|
|
42
|
+
light: {
|
|
43
|
+
bg: '253 246 240',
|
|
44
|
+
'bg-soft': '245 237 230',
|
|
45
|
+
surface: '250 245 241',
|
|
46
|
+
'surface-raised': '255 255 255',
|
|
47
|
+
fg: '38 34 31',
|
|
48
|
+
'fg-strong': '17 15 13',
|
|
49
|
+
'fg-muted': '110 102 94',
|
|
50
|
+
line: '224 216 208',
|
|
51
|
+
scrim: '17 15 13',
|
|
52
|
+
accent: '34 122 197',
|
|
53
|
+
'accent-strong': '21 92 152',
|
|
54
|
+
'accent-fg': '255 255 255',
|
|
55
|
+
success: '22 128 84',
|
|
56
|
+
'success-soft': '222 244 232',
|
|
57
|
+
'success-fg': '255 255 255',
|
|
58
|
+
warning: '176 106 8',
|
|
59
|
+
'warning-soft': '253 240 213',
|
|
60
|
+
'warning-fg': '255 255 255',
|
|
61
|
+
danger: '190 42 42',
|
|
62
|
+
'danger-soft': '253 227 227',
|
|
63
|
+
'danger-fg': '255 255 255',
|
|
64
|
+
info: '34 122 197',
|
|
65
|
+
'info-soft': '224 239 252',
|
|
66
|
+
'info-fg': '255 255 255',
|
|
67
|
+
},
|
|
68
|
+
dark: {
|
|
69
|
+
bg: '18 18 20',
|
|
70
|
+
'bg-soft': '28 28 32',
|
|
71
|
+
surface: '34 34 39',
|
|
72
|
+
'surface-raised': '44 44 50',
|
|
73
|
+
fg: '228 226 222',
|
|
74
|
+
'fg-strong': '248 247 245',
|
|
75
|
+
'fg-muted': '150 146 140',
|
|
76
|
+
line: '54 54 60',
|
|
77
|
+
scrim: '0 0 0',
|
|
78
|
+
accent: '96 170 240',
|
|
79
|
+
'accent-strong': '130 190 248',
|
|
80
|
+
'accent-fg': '16 20 26',
|
|
81
|
+
success: '74 190 130',
|
|
82
|
+
'success-soft': '22 46 34',
|
|
83
|
+
'success-fg': '12 26 18',
|
|
84
|
+
warning: '226 170 66',
|
|
85
|
+
'warning-soft': '52 42 20',
|
|
86
|
+
'warning-fg': '28 20 6',
|
|
87
|
+
danger: '240 110 110',
|
|
88
|
+
'danger-soft': '56 26 26',
|
|
89
|
+
'danger-fg': '30 12 12',
|
|
90
|
+
info: '96 170 240',
|
|
91
|
+
'info-soft': '22 38 56',
|
|
92
|
+
'info-fg': '12 20 30',
|
|
93
|
+
},
|
|
94
|
+
} as const;
|
|
95
|
+
|
|
96
|
+
export const spaceTokens = {
|
|
97
|
+
'0': '0',
|
|
98
|
+
'1': '0.25rem',
|
|
99
|
+
'2': '0.5rem',
|
|
100
|
+
'3': '0.75rem',
|
|
101
|
+
'4': '1rem',
|
|
102
|
+
'5': '1.25rem',
|
|
103
|
+
'6': '1.5rem',
|
|
104
|
+
'8': '2rem',
|
|
105
|
+
'10': '2.5rem',
|
|
106
|
+
'12': '3rem',
|
|
107
|
+
'16': '4rem',
|
|
108
|
+
} as const;
|
|
109
|
+
|
|
110
|
+
export const radiusTokens = {
|
|
111
|
+
none: '0',
|
|
112
|
+
sm: '0.25rem',
|
|
113
|
+
md: '0.5rem',
|
|
114
|
+
lg: '0.75rem',
|
|
115
|
+
xl: '1rem',
|
|
116
|
+
pill: '999px',
|
|
117
|
+
full: '50%',
|
|
118
|
+
} as const;
|
|
119
|
+
|
|
120
|
+
export const zTokens = {
|
|
121
|
+
base: '0',
|
|
122
|
+
raised: '10',
|
|
123
|
+
sticky: '100',
|
|
124
|
+
dropdown: '200',
|
|
125
|
+
drawer: '300',
|
|
126
|
+
dialog: '400',
|
|
127
|
+
popover: '500',
|
|
128
|
+
tooltip: '600',
|
|
129
|
+
toast: '700',
|
|
130
|
+
'skip-nav': '800',
|
|
131
|
+
} as const;
|
|
132
|
+
|
|
133
|
+
export const durationTokens = {
|
|
134
|
+
instant: '0ms',
|
|
135
|
+
fast: '120ms',
|
|
136
|
+
base: '220ms',
|
|
137
|
+
slow: '400ms',
|
|
138
|
+
slower: '640ms',
|
|
139
|
+
} as const;
|
|
140
|
+
|
|
141
|
+
export const easingTokens = {
|
|
142
|
+
out: 'cubic-bezier(0.16, 1, 0.3, 1)',
|
|
143
|
+
in: 'cubic-bezier(0.5, 0, 0.75, 0)',
|
|
144
|
+
'in-out': 'cubic-bezier(0.65, 0, 0.35, 1)',
|
|
145
|
+
spring: 'cubic-bezier(0.34, 1.56, 0.64, 1)',
|
|
146
|
+
} as const;
|
|
147
|
+
|
|
148
|
+
export const shadowTokens: Readonly<Record<Theme, Readonly<Record<string, string>>>> = {
|
|
149
|
+
light: {
|
|
150
|
+
xs: '0 1px 2px rgb(17 15 13 / 0.06)',
|
|
151
|
+
sm: '0 2px 6px rgb(17 15 13 / 0.08)',
|
|
152
|
+
md: '0 4px 14px rgb(17 15 13 / 0.1)',
|
|
153
|
+
lg: '0 12px 32px rgb(17 15 13 / 0.14)',
|
|
154
|
+
xl: '0 24px 56px rgb(17 15 13 / 0.18)',
|
|
155
|
+
},
|
|
156
|
+
dark: {
|
|
157
|
+
xs: '0 1px 2px rgb(0 0 0 / 0.4)',
|
|
158
|
+
sm: '0 2px 8px rgb(0 0 0 / 0.48)',
|
|
159
|
+
md: '0 6px 20px rgb(0 0 0 / 0.56)',
|
|
160
|
+
lg: '0 16px 40px rgb(0 0 0 / 0.64)',
|
|
161
|
+
xl: '0 28px 64px rgb(0 0 0 / 0.72)',
|
|
162
|
+
},
|
|
163
|
+
} as const;
|
|
164
|
+
|
|
165
|
+
export const breakpointTokens = {
|
|
166
|
+
sm: '480px',
|
|
167
|
+
md: '768px',
|
|
168
|
+
lg: '1024px',
|
|
169
|
+
xl: '1280px',
|
|
170
|
+
'2xl': '1536px',
|
|
171
|
+
} as const;
|
|
172
|
+
|
|
173
|
+
export const fontSizeTokens = {
|
|
174
|
+
xs: 'clamp(0.75rem, 0.73rem + 0.1vw, 0.8125rem)',
|
|
175
|
+
sm: 'clamp(0.875rem, 0.85rem + 0.15vw, 0.9375rem)',
|
|
176
|
+
md: 'clamp(1rem, 0.96rem + 0.2vw, 1.0625rem)',
|
|
177
|
+
lg: 'clamp(1.125rem, 1.05rem + 0.35vw, 1.25rem)',
|
|
178
|
+
xl: 'clamp(1.375rem, 1.2rem + 0.7vw, 1.75rem)',
|
|
179
|
+
'2xl': 'clamp(1.75rem, 1.4rem + 1.4vw, 2.5rem)',
|
|
180
|
+
'3xl': 'clamp(2.25rem, 1.6rem + 2.6vw, 3.5rem)',
|
|
181
|
+
} as const;
|
|
182
|
+
|
|
183
|
+
export const fontWeightTokens = {
|
|
184
|
+
normal: '400',
|
|
185
|
+
medium: '500',
|
|
186
|
+
semibold: '600',
|
|
187
|
+
bold: '700',
|
|
188
|
+
} as const;
|
|
189
|
+
|
|
190
|
+
export const lineHeightTokens = {
|
|
191
|
+
tight: '1.2',
|
|
192
|
+
snug: '1.35',
|
|
193
|
+
normal: '1.55',
|
|
194
|
+
loose: '1.75',
|
|
195
|
+
} as const;
|
|
196
|
+
|
|
197
|
+
/** `var(--color-accent)` — the channel list, for use inside `rgb()`. */
|
|
198
|
+
export function colorVar(role: ColorRole): string {
|
|
199
|
+
assertColorRole(role);
|
|
200
|
+
return `var(--color-${role})`;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** `rgb(var(--color-accent) / 0.5)` — a CSS-ready colour. */
|
|
204
|
+
export function color(role: ColorRole, alpha = 1): string {
|
|
205
|
+
return `rgb(${colorVar(role)} / ${alpha})`;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Resolved `rgb(...)` for a theme, with no custom-property indirection — the
|
|
210
|
+
* only form <canvas>, chart libraries, and email clients can consume.
|
|
211
|
+
*/
|
|
212
|
+
export function colorRgb(theme: Theme, role: ColorRole, alpha = 1): string {
|
|
213
|
+
assertColorRole(role);
|
|
214
|
+
const channels = colorTokens[theme][role];
|
|
215
|
+
return alpha === 1 ? `rgb(${channels})` : `rgb(${channels} / ${alpha})`;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function assertColorRole(role: string): asserts role is ColorRole {
|
|
219
|
+
if (!(COLOR_ROLES as readonly string[]).includes(role)) {
|
|
220
|
+
throw unknownTokenError('color', role, COLOR_ROLES);
|
|
221
|
+
}
|
|
222
|
+
}
|