@neuravision/construct 2.0.3 → 2.1.2
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 +318 -0
- package/README.md +158 -149
- package/behaviors/combobox.js +486 -0
- package/behaviors/drawer.js +18 -0
- package/behaviors/dropdown.js +310 -0
- package/behaviors/index.d.ts +335 -0
- package/behaviors/index.js +9 -0
- package/behaviors/internal/dialog-controller.js +421 -0
- package/behaviors/internal/dom.js +408 -0
- package/behaviors/internal/overlay.js +241 -0
- package/behaviors/internal/popup-layer.js +190 -0
- package/behaviors/modal.js +17 -0
- package/behaviors/popover.js +278 -0
- package/behaviors/select-menu.js +344 -0
- package/behaviors/tabs.js +241 -0
- package/behaviors/toggle-group.js +266 -0
- package/behaviors/tooltip.js +257 -0
- package/components/README.md +319 -83
- package/components/_shared.css +22 -10
- package/components/_shell-shared.css +20 -4
- package/components/accordion.css +8 -0
- package/components/app-shell-v2.css +28 -14
- package/components/app-shell.css +30 -14
- package/components/badge.css +15 -0
- package/components/banner.css +32 -1
- package/components/button.css +103 -15
- package/components/card.css +67 -8
- package/components/chart.css +26 -0
- package/components/checkbox.css +35 -2
- package/components/chip.css +30 -2
- package/components/combobox.css +21 -0
- package/components/datepicker.css +58 -9
- package/components/datum.css +277 -0
- package/components/dropdown.css +23 -3
- package/components/field.css +6 -4
- package/components/file-upload.css +1 -1
- package/components/index.css +5 -0
- package/components/list.css +28 -2
- package/components/meter.css +182 -0
- package/components/navbar.css +102 -4
- package/components/pagination.css +18 -0
- package/components/progress-bar.css +22 -1
- package/components/select-menu.css +8 -0
- package/components/select.css +8 -0
- package/components/sidebar.css +15 -2
- package/components/skip-link.css +6 -2
- package/components/slider.css +7 -1
- package/components/status.css +107 -0
- package/components/switch.css +66 -5
- package/components/table.css +53 -4
- package/components/tabs.css +55 -3
- package/components/toast.css +10 -1
- package/components/toggle-group.css +16 -5
- package/components/toolbar.css +17 -1
- package/components/tooltip.css +4 -0
- package/components/tree.css +30 -0
- package/docs/architecture.md +79 -0
- package/docs/behaviors.md +122 -0
- package/docs/fonts.md +70 -0
- package/docs/governance.md +85 -0
- package/docs/guidelines.md +72 -25
- package/examples/react/README.md +20 -0
- package/examples/react/SettingsModal.jsx +66 -0
- package/examples/react/construct.css +5 -0
- package/fonts.css +10 -0
- package/foundations.css +14 -1
- package/package.json +76 -21
- package/schemas/components.schema.json +122 -0
- package/schemas/examples/theme.example.json +27 -0
- package/schemas/primitives.schema.json +135 -0
- package/schemas/semantic.schema.json +224 -0
- package/schemas/theme.schema.json +63 -0
- package/scripts/build-tokens.mjs +1499 -0
- package/scripts/construct-theme.mjs +43 -0
- package/scripts/validate-tokens.mjs +25 -0
- package/standalone/accordion.css +3 -0
- package/standalone/alert.css +3 -0
- package/standalone/app-shell-v2.css +4 -0
- package/standalone/app-shell.css +4 -0
- package/standalone/avatar.css +3 -0
- package/standalone/badge.css +3 -0
- package/standalone/banner.css +3 -0
- package/standalone/breadcrumbs.css +3 -0
- package/standalone/button.css +3 -0
- package/standalone/card.css +3 -0
- package/standalone/chart.css +3 -0
- package/standalone/checkbox.css +3 -0
- package/standalone/chip.css +3 -0
- package/standalone/combobox.css +3 -0
- package/standalone/core.css +4 -0
- package/standalone/data-table.css +3 -0
- package/standalone/datepicker.css +3 -0
- package/standalone/datum.css +3 -0
- package/standalone/divider.css +3 -0
- package/standalone/drawer.css +3 -0
- package/standalone/dropdown.css +3 -0
- package/standalone/empty-state.css +3 -0
- package/standalone/field.css +3 -0
- package/standalone/file-upload.css +3 -0
- package/standalone/icon.css +3 -0
- package/standalone/input.css +3 -0
- package/standalone/list.css +3 -0
- package/standalone/meter.css +3 -0
- package/standalone/modal.css +3 -0
- package/standalone/navbar.css +3 -0
- package/standalone/pagination.css +3 -0
- package/standalone/popover.css +3 -0
- package/standalone/progress-bar.css +3 -0
- package/standalone/select-menu.css +3 -0
- package/standalone/select.css +3 -0
- package/standalone/sidebar.css +3 -0
- package/standalone/skeleton.css +3 -0
- package/standalone/skip-link.css +3 -0
- package/standalone/slider.css +3 -0
- package/standalone/spinner.css +3 -0
- package/standalone/status.css +3 -0
- package/standalone/switch.css +3 -0
- package/standalone/table.css +3 -0
- package/standalone/tabs.css +3 -0
- package/standalone/textarea.css +3 -0
- package/standalone/toast.css +3 -0
- package/standalone/toggle-group.css +3 -0
- package/standalone/toolbar.css +3 -0
- package/standalone/tooltip.css +3 -0
- package/standalone/tree.css +3 -0
- package/themes/README.md +37 -0
- package/themes/walnut.theme.json +106 -0
- package/tokens/README.md +135 -123
- package/tokens/components.json +64 -0
- package/tokens/primitives.json +268 -0
- package/tokens/semantic.dark.json +94 -0
- package/tokens/semantic.high-contrast-dark.json +94 -0
- package/tokens/semantic.high-contrast.json +94 -0
- package/tokens/semantic.light.json +179 -0
- package/tokens/tokens.css +960 -58
- package/tokens/tokens.js +729 -120
- package/tokens/tokens.json +1158 -116
- package/tokens/tokens.ts +729 -120
package/components/chart.css
CHANGED
|
@@ -86,6 +86,27 @@
|
|
|
86
86
|
fill: var(--color-text-secondary);
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
/* ── Datum reference line ──
|
|
90
|
+
Construct's signature applied to data: an orange reference line marks
|
|
91
|
+
a threshold, target, or "today". The dash pattern separates the
|
|
92
|
+
reference from data series without relying on hue, and the value it
|
|
93
|
+
encodes must also appear as text — in the label and in the chart's
|
|
94
|
+
data-table fallback — so the line itself stays supplementary. */
|
|
95
|
+
.ct-chart__datum-line {
|
|
96
|
+
stroke: var(--color-brand-accent-strong);
|
|
97
|
+
stroke-width: var(--ct-chart-line-width);
|
|
98
|
+
stroke-dasharray: 6 4;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.ct-chart__datum-label {
|
|
102
|
+
fill: var(--color-text-secondary);
|
|
103
|
+
font-family: var(--font-family-mono);
|
|
104
|
+
font-size: var(--font-size-xs);
|
|
105
|
+
font-weight: var(--font-weight-semibold);
|
|
106
|
+
letter-spacing: 0.08em;
|
|
107
|
+
text-transform: uppercase;
|
|
108
|
+
}
|
|
109
|
+
|
|
89
110
|
/* ── Line & area ── */
|
|
90
111
|
.ct-chart__line {
|
|
91
112
|
fill: none;
|
|
@@ -366,4 +387,9 @@
|
|
|
366
387
|
.ct-chart__axis-line {
|
|
367
388
|
stroke: GrayText;
|
|
368
389
|
}
|
|
390
|
+
|
|
391
|
+
/* The datum reference line stays distinct from the dimmed chrome. */
|
|
392
|
+
.ct-chart__datum-line {
|
|
393
|
+
stroke: CanvasText;
|
|
394
|
+
}
|
|
369
395
|
}
|
package/components/checkbox.css
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
appearance: none;
|
|
14
14
|
width: var(--space-6);
|
|
15
15
|
height: var(--space-6);
|
|
16
|
-
border: var(--border-thin) solid var(--
|
|
16
|
+
border: var(--border-thin) solid var(--ct-control-border);
|
|
17
17
|
background: var(--color-bg-elevated);
|
|
18
18
|
cursor: pointer;
|
|
19
19
|
display: grid;
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
transform var(--duration-fast) var(--easing-standard);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
/*
|
|
27
|
+
/* Construct 44×44 target policy: meets the size in WCAG 2.2 SC 2.5.5
|
|
28
|
+
(AAA) and exceeds the AA minimum in SC 2.5.8. */
|
|
28
29
|
@media (pointer: coarse) {
|
|
29
30
|
.ct-check__input::after,
|
|
30
31
|
.ct-radio__input::after {
|
|
@@ -97,6 +98,9 @@
|
|
|
97
98
|
background: var(--color-bg-muted);
|
|
98
99
|
border-color: var(--color-border-subtle);
|
|
99
100
|
cursor: not-allowed;
|
|
101
|
+
/* Disabled is fully colour-encoded above; cancel foundations' global
|
|
102
|
+
:disabled opacity so the control is not double-dimmed. */
|
|
103
|
+
opacity: 1;
|
|
100
104
|
}
|
|
101
105
|
|
|
102
106
|
.ct-check__input:focus-visible,
|
|
@@ -118,3 +122,32 @@
|
|
|
118
122
|
border-inline-end: 0;
|
|
119
123
|
transform: scale(1);
|
|
120
124
|
}
|
|
125
|
+
|
|
126
|
+
@media (prefers-reduced-motion: reduce) {
|
|
127
|
+
.ct-check__input,
|
|
128
|
+
.ct-radio__input,
|
|
129
|
+
.ct-check__input::before,
|
|
130
|
+
.ct-radio__input::before {
|
|
131
|
+
transition: none;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@media (forced-colors: active) {
|
|
136
|
+
.ct-check__input:checked,
|
|
137
|
+
.ct-check__input:indeterminate,
|
|
138
|
+
.ct-radio__input:checked {
|
|
139
|
+
border-color: ButtonText;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* The tick and indeterminate dash are border-drawn and survive on their
|
|
143
|
+
own; the radio dot is background-drawn and would be forced to Canvas. */
|
|
144
|
+
.ct-radio__input:checked::before {
|
|
145
|
+
background: SelectedItem;
|
|
146
|
+
forced-color-adjust: none;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.ct-check__input:disabled,
|
|
150
|
+
.ct-radio__input:disabled {
|
|
151
|
+
border-color: GrayText;
|
|
152
|
+
}
|
|
153
|
+
}
|
package/components/chip.css
CHANGED
|
@@ -35,7 +35,9 @@
|
|
|
35
35
|
--ct-chip-padding-x: var(--space-2);
|
|
36
36
|
--ct-chip-font-size: var(--font-size-xs);
|
|
37
37
|
--ct-chip-line-height: var(--line-height-xs);
|
|
38
|
-
|
|
38
|
+
/* WCAG 2.2 SC 2.5.8: the remove button stays at least 24px in every
|
|
39
|
+
pointer context, so the small chip cannot shrink it below that. */
|
|
40
|
+
--ct-chip-remove-size: var(--icon-lg);
|
|
39
41
|
--ct-chip-gap: var(--space-1);
|
|
40
42
|
}
|
|
41
43
|
|
|
@@ -340,12 +342,23 @@
|
|
|
340
342
|
outline-offset: 2px;
|
|
341
343
|
}
|
|
342
344
|
|
|
343
|
-
/* ──
|
|
345
|
+
/* ── Construct 44×44 target policy (WCAG 2.2 SC 2.5.5 is AAA) ─── */
|
|
344
346
|
|
|
345
347
|
@media (pointer: coarse) {
|
|
346
348
|
.ct-chip {
|
|
347
349
|
min-height: 44px;
|
|
348
350
|
}
|
|
351
|
+
|
|
352
|
+
/* Hit area may extend past the chip edge, mirroring checkbox.css. */
|
|
353
|
+
.ct-chip__remove {
|
|
354
|
+
position: relative;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.ct-chip__remove::before {
|
|
358
|
+
content: '';
|
|
359
|
+
position: absolute;
|
|
360
|
+
inset: calc((var(--ct-chip-remove-size) - 44px) / 2);
|
|
361
|
+
}
|
|
349
362
|
}
|
|
350
363
|
|
|
351
364
|
/* ── Reduced motion ──────────────────────────────────────────── */
|
|
@@ -361,3 +374,18 @@
|
|
|
361
374
|
transform: none;
|
|
362
375
|
}
|
|
363
376
|
}
|
|
377
|
+
|
|
378
|
+
/* ── Forced colors ───────────────────────────────────────────── */
|
|
379
|
+
|
|
380
|
+
@media (forced-colors: active) {
|
|
381
|
+
/* Mask-drawn glyphs and the status dot paint via background-color,
|
|
382
|
+
which forced colors resets — repaint them with system ink. The
|
|
383
|
+
selected checkmark is border-drawn and survives on its own. */
|
|
384
|
+
.ct-chip__icon--info,
|
|
385
|
+
.ct-chip__icon--success,
|
|
386
|
+
.ct-chip__icon--warning,
|
|
387
|
+
.ct-chip__icon--danger,
|
|
388
|
+
.ct-chip__dot {
|
|
389
|
+
background-color: CanvasText;
|
|
390
|
+
}
|
|
391
|
+
}
|
package/components/combobox.css
CHANGED
|
@@ -374,6 +374,27 @@
|
|
|
374
374
|
border-color: transparent;
|
|
375
375
|
}
|
|
376
376
|
|
|
377
|
+
/* Replacement indicator for the removed input outline (mirrors the --multi pattern) */
|
|
378
|
+
.ct-combobox--inline:focus-within {
|
|
379
|
+
outline: 2px solid var(--color-focus-ring);
|
|
380
|
+
outline-offset: 2px;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/* ── Construct 44×44 target policy (WCAG 2.2 SC 2.5.5 is AAA) ── */
|
|
384
|
+
|
|
385
|
+
@media (pointer: coarse) {
|
|
386
|
+
.ct-combobox__option {
|
|
387
|
+
min-block-size: 44px;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/* Hit area extends past the visual 24px trigger, mirroring checkbox.css. */
|
|
391
|
+
.ct-combobox__trigger::before {
|
|
392
|
+
content: '';
|
|
393
|
+
position: absolute;
|
|
394
|
+
inset: calc((var(--icon-lg) - 44px) / 2);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
377
398
|
/* ── Reduced motion ──────────────────────────────────────────── */
|
|
378
399
|
|
|
379
400
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/* ─── Datepicker ────────────────────────────────────────────────── */
|
|
2
2
|
|
|
3
3
|
.ct-datepicker {
|
|
4
|
+
--ct-datepicker-selected-surface: color-mix(in srgb, var(--color-brand-primary) 12%, transparent);
|
|
5
|
+
|
|
4
6
|
position: relative;
|
|
5
7
|
display: inline-block;
|
|
6
8
|
}
|
|
@@ -77,10 +79,22 @@
|
|
|
77
79
|
justify-items: center;
|
|
78
80
|
}
|
|
79
81
|
|
|
82
|
+
/* Rows keep grid placement on the shared 7-column grid. display: contents
|
|
83
|
+
once dropped element semantics in older engines; current Chromium/Firefox/
|
|
84
|
+
Safari expose role="row" on display: contents elements correctly. */
|
|
80
85
|
.ct-datepicker__row {
|
|
81
86
|
display: contents;
|
|
82
87
|
}
|
|
83
88
|
|
|
89
|
+
/* Optional gridcell wrapper (APG date-picker dialog markup): the cell is the
|
|
90
|
+
grid item and carries role="gridcell" + aria-selected; the day button
|
|
91
|
+
inside keeps its native button role. */
|
|
92
|
+
.ct-datepicker__cell {
|
|
93
|
+
display: flex;
|
|
94
|
+
width: 100%;
|
|
95
|
+
justify-content: center;
|
|
96
|
+
}
|
|
97
|
+
|
|
84
98
|
.ct-datepicker__weekday {
|
|
85
99
|
font-size: var(--font-size-xs);
|
|
86
100
|
color: var(--color-text-muted);
|
|
@@ -113,9 +127,10 @@
|
|
|
113
127
|
outline-offset: 2px;
|
|
114
128
|
}
|
|
115
129
|
|
|
116
|
-
.ct-datepicker__day[aria-selected='true']
|
|
130
|
+
.ct-datepicker__day[aria-selected='true'],
|
|
131
|
+
.ct-datepicker__cell[aria-selected='true'] .ct-datepicker__day {
|
|
117
132
|
background: var(--color-brand-primary);
|
|
118
|
-
color: var(--color-text-
|
|
133
|
+
color: var(--color-text-on-primary);
|
|
119
134
|
}
|
|
120
135
|
|
|
121
136
|
.ct-datepicker__day[data-outside='true'] {
|
|
@@ -150,19 +165,19 @@
|
|
|
150
165
|
/* ── Range selection ────────────────────────────────────────────── */
|
|
151
166
|
|
|
152
167
|
.ct-datepicker__day[data-in-range] {
|
|
153
|
-
background: var(--
|
|
168
|
+
background: var(--ct-datepicker-selected-surface);
|
|
154
169
|
border-radius: 0;
|
|
155
170
|
}
|
|
156
171
|
|
|
157
172
|
.ct-datepicker__day[data-range-start] {
|
|
158
173
|
background: var(--color-brand-primary);
|
|
159
|
-
color: var(--color-text-
|
|
174
|
+
color: var(--color-text-on-primary);
|
|
160
175
|
border-radius: var(--radius-sm) 0 0 var(--radius-sm);
|
|
161
176
|
}
|
|
162
177
|
|
|
163
178
|
.ct-datepicker__day[data-range-end] {
|
|
164
179
|
background: var(--color-brand-primary);
|
|
165
|
-
color: var(--color-text-
|
|
180
|
+
color: var(--color-text-on-primary);
|
|
166
181
|
border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
|
|
167
182
|
}
|
|
168
183
|
|
|
@@ -170,6 +185,38 @@
|
|
|
170
185
|
border-radius: var(--radius-sm);
|
|
171
186
|
}
|
|
172
187
|
|
|
188
|
+
/* ── Coarse pointers ────────────────────────────────────────────── */
|
|
189
|
+
|
|
190
|
+
@media (pointer: coarse) {
|
|
191
|
+
/* Day cells may compress inside the min(90vw, …) popover; tap size
|
|
192
|
+
wins over the shared column width (44×44 policy). */
|
|
193
|
+
.ct-datepicker__day {
|
|
194
|
+
min-width: var(--control-height-calendar-day);
|
|
195
|
+
min-height: var(--control-height-calendar-day);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* ── Forced colors ──────────────────────────────────────────────── */
|
|
200
|
+
|
|
201
|
+
@media (forced-colors: active) {
|
|
202
|
+
/* Selection fills are background-only and would be stripped to Canvas —
|
|
203
|
+
re-encode with system selection colors (data-today's border and the
|
|
204
|
+
highlight outline survive on their own). */
|
|
205
|
+
.ct-datepicker__day[aria-selected='true'],
|
|
206
|
+
.ct-datepicker__cell[aria-selected='true'] .ct-datepicker__day,
|
|
207
|
+
.ct-datepicker__cell[aria-selected='true'] .ct-datepicker__month,
|
|
208
|
+
.ct-datepicker__cell[aria-selected='true'] .ct-datepicker__year,
|
|
209
|
+
.ct-datepicker__month[aria-selected='true'],
|
|
210
|
+
.ct-datepicker__year[aria-selected='true'],
|
|
211
|
+
.ct-datepicker__day[data-in-range],
|
|
212
|
+
.ct-datepicker__day[data-range-start],
|
|
213
|
+
.ct-datepicker__day[data-range-end] {
|
|
214
|
+
background: SelectedItem;
|
|
215
|
+
color: SelectedItemText;
|
|
216
|
+
forced-color-adjust: none;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
173
220
|
/* ── Month grid (3×4) ───────────────────────────────────────────── */
|
|
174
221
|
|
|
175
222
|
.ct-datepicker__month-grid {
|
|
@@ -203,9 +250,10 @@
|
|
|
203
250
|
outline-offset: 2px;
|
|
204
251
|
}
|
|
205
252
|
|
|
206
|
-
.ct-datepicker__month[aria-selected='true']
|
|
253
|
+
.ct-datepicker__month[aria-selected='true'],
|
|
254
|
+
.ct-datepicker__cell[aria-selected='true'] .ct-datepicker__month {
|
|
207
255
|
background: var(--color-brand-primary);
|
|
208
|
-
color: var(--color-text-
|
|
256
|
+
color: var(--color-text-on-primary);
|
|
209
257
|
}
|
|
210
258
|
|
|
211
259
|
.ct-datepicker__month[data-highlighted] {
|
|
@@ -252,9 +300,10 @@
|
|
|
252
300
|
outline-offset: 2px;
|
|
253
301
|
}
|
|
254
302
|
|
|
255
|
-
.ct-datepicker__year[aria-selected='true']
|
|
303
|
+
.ct-datepicker__year[aria-selected='true'],
|
|
304
|
+
.ct-datepicker__cell[aria-selected='true'] .ct-datepicker__year {
|
|
256
305
|
background: var(--color-brand-primary);
|
|
257
|
-
color: var(--color-text-
|
|
306
|
+
color: var(--color-text-on-primary);
|
|
258
307
|
}
|
|
259
308
|
|
|
260
309
|
.ct-datepicker__year[data-highlighted] {
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/* ============================================================
|
|
2
|
+
Datum — the orange reference-line grammar.
|
|
3
|
+
|
|
4
|
+
Construct's signature is one orange reference line ("the datum")
|
|
5
|
+
that marks what is focused / active / current. These primitives
|
|
6
|
+
promote that signature from focus rings and the intro page into
|
|
7
|
+
reusable structural vocabulary every surface can speak.
|
|
8
|
+
|
|
9
|
+
Accessibility contract (must hold across all built-in themes):
|
|
10
|
+
· orange as TEXT → never; use --color-text-* neutrals (orange fails
|
|
11
|
+
4.5:1 as small text on several theme surfaces)
|
|
12
|
+
· orange as FILL vs muted tracks → --color-brand-accent-strong
|
|
13
|
+
(validated ≥3:1 against bg.muted/canvas/surface per theme)
|
|
14
|
+
· orange as EDGE/RULE (decorative, on canvas/surface) → --color-brand-accent
|
|
15
|
+
============================================================ */
|
|
16
|
+
|
|
17
|
+
/* ── Eyebrow ──
|
|
18
|
+
A mono kicker with a leading datum tick. Sits above a heading to
|
|
19
|
+
label a section in the engineered "data annotation" voice. */
|
|
20
|
+
.ct-eyebrow {
|
|
21
|
+
display: inline-flex;
|
|
22
|
+
align-items: center;
|
|
23
|
+
gap: var(--space-4);
|
|
24
|
+
margin: 0 0 var(--space-5);
|
|
25
|
+
font-family: var(--font-family-mono);
|
|
26
|
+
font-size: var(--font-size-xs);
|
|
27
|
+
font-weight: var(--font-weight-semibold);
|
|
28
|
+
text-transform: uppercase;
|
|
29
|
+
letter-spacing: 0.16em;
|
|
30
|
+
/* Text stays a theme-safe neutral (AA on every surface in all three
|
|
31
|
+
themes); the orange datum lives in the non-text tick below. Orange
|
|
32
|
+
as small text fails contrast on dark/elevated and high-contrast. */
|
|
33
|
+
color: var(--color-text-secondary);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.ct-eyebrow::before {
|
|
37
|
+
content: '';
|
|
38
|
+
width: var(--component-datum-tick-width);
|
|
39
|
+
height: var(--component-datum-tick-height);
|
|
40
|
+
border-radius: var(--radius-xs);
|
|
41
|
+
background: var(--color-brand-accent);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* ── Numbered eyebrow ──
|
|
45
|
+
Sheet numbers, like an engineering drawing set: `--numbered` inserts
|
|
46
|
+
an auto-incremented, zero-padded counter between tick and label.
|
|
47
|
+
Scope numbering with a `.ct-eyebrow-set` ancestor (or any element
|
|
48
|
+
that declares `counter-reset: ct-eyebrow`). */
|
|
49
|
+
.ct-eyebrow-set {
|
|
50
|
+
counter-reset: ct-eyebrow;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.ct-eyebrow--numbered {
|
|
54
|
+
counter-increment: ct-eyebrow;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.ct-eyebrow--numbered::before {
|
|
58
|
+
order: -2;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.ct-eyebrow--numbered::after {
|
|
62
|
+
content: counter(ct-eyebrow, decimal-leading-zero);
|
|
63
|
+
order: -1;
|
|
64
|
+
color: var(--color-text-muted);
|
|
65
|
+
font-variant-numeric: tabular-nums;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* ── Datum scale ──
|
|
69
|
+
A measured reference line: a hairline baseline with hanging ruler
|
|
70
|
+
ticks (minor every 12px, major every 60px) and an orange "origin"
|
|
71
|
+
segment marking zero. Decorative — mark it role="presentation". */
|
|
72
|
+
.ct-datum-scale {
|
|
73
|
+
--ct-datum-scale-accent: var(--color-brand-accent);
|
|
74
|
+
--ct-datum-scale-origin: var(--component-datum-origin-length);
|
|
75
|
+
|
|
76
|
+
height: var(--component-datum-scale-height);
|
|
77
|
+
background:
|
|
78
|
+
/* orange origin segment — the datum's zero */
|
|
79
|
+
linear-gradient(var(--ct-datum-scale-accent), var(--ct-datum-scale-accent)) 0 0 / var(--ct-datum-scale-origin) 2px no-repeat,
|
|
80
|
+
/* full-width baseline hairline */
|
|
81
|
+
linear-gradient(var(--color-border-strong), var(--color-border-strong)) 0 0 / 100% 1px no-repeat,
|
|
82
|
+
/* major ticks */
|
|
83
|
+
repeating-linear-gradient(90deg, var(--color-border-strong) 0 1px, transparent 1px var(--component-datum-scale-major-step)) 0 0 / 100% 8px no-repeat,
|
|
84
|
+
/* minor ticks */
|
|
85
|
+
repeating-linear-gradient(90deg, var(--color-border-default) 0 1px, transparent 1px var(--component-datum-scale-minor-step)) 0 0 / 100% 5px no-repeat;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* RTL: the origin and tick phase anchor to the inline start. */
|
|
89
|
+
.ct-datum-scale:is([dir='rtl'] *, :dir(rtl)) {
|
|
90
|
+
background:
|
|
91
|
+
linear-gradient(var(--ct-datum-scale-accent), var(--ct-datum-scale-accent)) 100% 0 / var(--ct-datum-scale-origin) 2px no-repeat,
|
|
92
|
+
linear-gradient(var(--color-border-strong), var(--color-border-strong)) 0 0 / 100% 1px no-repeat,
|
|
93
|
+
repeating-linear-gradient(270deg, var(--color-border-strong) 0 1px, transparent 1px var(--component-datum-scale-major-step)) 0 0 / 100% 8px no-repeat,
|
|
94
|
+
repeating-linear-gradient(270deg, var(--color-border-default) 0 1px, transparent 1px var(--component-datum-scale-minor-step)) 0 0 / 100% 5px no-repeat;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* ── Progress variant ──
|
|
98
|
+
The origin segment becomes meaning-bearing: its length reports
|
|
99
|
+
progress. Set `--ct-datum-scale-progress` (a percentage) on the
|
|
100
|
+
element and give it `role="progressbar"` with the matching
|
|
101
|
+
`aria-valuenow/min/max`. Meaning-bearing fill uses accent-strong,
|
|
102
|
+
validated ≥3:1 against muted/canvas/surface tracks per theme. */
|
|
103
|
+
.ct-datum-scale--progress {
|
|
104
|
+
--ct-datum-scale-accent: var(--color-brand-accent-strong);
|
|
105
|
+
--ct-datum-scale-origin: var(--ct-datum-scale-progress, 0%);
|
|
106
|
+
|
|
107
|
+
transition: background-size var(--duration-normal) var(--easing-standard);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/* ── Spec datasheet ──
|
|
111
|
+
Engineering datasheet for key figures — hairline-gridded cells
|
|
112
|
+
instead of free-floating big numbers. Mark the lead cell with
|
|
113
|
+
`--origin` to carry the datum. */
|
|
114
|
+
:is(.ct-metrics, .ct-specs) {
|
|
115
|
+
--ct-metric-bg: var(--component-metric-background);
|
|
116
|
+
--ct-metric-border: var(--component-metric-border);
|
|
117
|
+
--ct-metric-radius: var(--component-metric-radius);
|
|
118
|
+
--ct-metric-padding: var(--component-metric-padding);
|
|
119
|
+
--ct-metric-value-font-size: var(--component-metric-value-font-size);
|
|
120
|
+
--ct-metric-label-font-size: var(--component-metric-label-font-size);
|
|
121
|
+
|
|
122
|
+
display: grid;
|
|
123
|
+
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
124
|
+
gap: var(--border-thin);
|
|
125
|
+
border: var(--border-thin) solid var(--ct-metric-border);
|
|
126
|
+
border-radius: var(--ct-metric-radius);
|
|
127
|
+
background: var(--ct-metric-border);
|
|
128
|
+
overflow: hidden;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* Compatibility profile: retain the original datasheet scale while the
|
|
132
|
+
canonical metric pattern uses the denser component-token defaults. */
|
|
133
|
+
.ct-specs {
|
|
134
|
+
--ct-metric-radius: var(--radius-lg);
|
|
135
|
+
--ct-metric-padding: var(--space-7);
|
|
136
|
+
--ct-metric-value-font-size: var(--font-size-4xl);
|
|
137
|
+
--ct-metric-label-font-size: var(--font-size-xs);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
:is(.ct-metric, .ct-spec) {
|
|
141
|
+
--ct-metric-accent: transparent;
|
|
142
|
+
--ct-metric-status-color: var(--color-text-secondary);
|
|
143
|
+
|
|
144
|
+
position: relative;
|
|
145
|
+
min-width: 0;
|
|
146
|
+
margin: 0;
|
|
147
|
+
padding: var(--ct-metric-padding);
|
|
148
|
+
background: var(--ct-metric-bg);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.ct-metric {
|
|
152
|
+
display: grid;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
:is(.ct-metric--active, .ct-metric[data-state='active'], .ct-spec--origin) {
|
|
156
|
+
--ct-metric-accent: var(--color-brand-accent);
|
|
157
|
+
|
|
158
|
+
box-shadow: inset 0 var(--component-datum-edge-width) 0 0 var(--ct-metric-accent);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
:is(.ct-metric__value, .ct-spec__value) {
|
|
162
|
+
margin: 0;
|
|
163
|
+
font-family: var(--font-family-brand);
|
|
164
|
+
font-size: var(--ct-metric-value-font-size);
|
|
165
|
+
font-weight: var(--font-weight-extrabold);
|
|
166
|
+
line-height: 1;
|
|
167
|
+
color: var(--color-text-primary);
|
|
168
|
+
font-variant-numeric: tabular-nums lining-nums;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.ct-metric__value {
|
|
172
|
+
order: 1;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
:is(.ct-metric__value, .ct-spec__value) small {
|
|
176
|
+
font-size: var(--font-size-xl);
|
|
177
|
+
font-weight: var(--font-weight-bold);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
:is(.ct-metric__label, .ct-spec__label) {
|
|
181
|
+
display: block;
|
|
182
|
+
margin-top: var(--space-4);
|
|
183
|
+
font-family: var(--font-family-mono);
|
|
184
|
+
font-size: var(--ct-metric-label-font-size);
|
|
185
|
+
font-weight: var(--font-weight-semibold);
|
|
186
|
+
text-transform: uppercase;
|
|
187
|
+
letter-spacing: 0.14em;
|
|
188
|
+
color: var(--color-text-secondary);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.ct-metric__label {
|
|
192
|
+
order: 2;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.ct-metric__meta {
|
|
196
|
+
display: flex;
|
|
197
|
+
align-items: center;
|
|
198
|
+
justify-content: space-between;
|
|
199
|
+
gap: var(--space-3);
|
|
200
|
+
margin-top: var(--space-4);
|
|
201
|
+
color: var(--color-text-secondary);
|
|
202
|
+
font-size: var(--font-size-xs);
|
|
203
|
+
font-family: var(--font-family-body);
|
|
204
|
+
font-weight: var(--font-weight-regular);
|
|
205
|
+
line-height: var(--line-height-xs);
|
|
206
|
+
text-transform: none;
|
|
207
|
+
letter-spacing: normal;
|
|
208
|
+
order: 3;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.ct-metric__status {
|
|
212
|
+
color: var(--ct-metric-status-color);
|
|
213
|
+
font-weight: var(--font-weight-semibold);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/* Status wording stays neutral and AA-safe on every metric surface;
|
|
217
|
+
semantic colour is carried by the non-text datum edge. */
|
|
218
|
+
|
|
219
|
+
.ct-metric[data-status='success'] {
|
|
220
|
+
--ct-metric-accent: var(--color-state-success);
|
|
221
|
+
--ct-metric-status-color: var(--color-text-primary);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.ct-metric[data-status='info'] {
|
|
225
|
+
--ct-metric-accent: var(--color-state-info);
|
|
226
|
+
--ct-metric-status-color: var(--color-text-primary);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.ct-metric[data-status='warning'] {
|
|
230
|
+
--ct-metric-accent: var(--color-state-warning);
|
|
231
|
+
--ct-metric-status-color: var(--color-text-primary);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.ct-metric:is([data-status='danger'], [data-status='error'], [data-state='error']) {
|
|
235
|
+
--ct-metric-accent: var(--color-state-danger);
|
|
236
|
+
--ct-metric-status-color: var(--color-text-primary);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.ct-metric[data-status],
|
|
240
|
+
.ct-metric[data-state='error'] {
|
|
241
|
+
box-shadow: inset var(--component-datum-edge-width) 0 0 0 var(--ct-metric-accent);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.ct-metric:is([aria-busy='true'], [data-state='loading']) {
|
|
245
|
+
cursor: wait;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.ct-metric:is([aria-disabled='true'], [data-state='disabled']) {
|
|
249
|
+
opacity: var(--opacity-disabled);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/* ── Datum frame ──
|
|
253
|
+
The corner registration mark generalised beyond cards: an opt-in
|
|
254
|
+
"exhibit" treatment for figures, code samples, and media. Decorative;
|
|
255
|
+
forced colors keeps the borders via CanvasText automatically. */
|
|
256
|
+
.ct-datum-frame {
|
|
257
|
+
position: relative;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.ct-datum-frame::before {
|
|
261
|
+
content: '';
|
|
262
|
+
position: absolute;
|
|
263
|
+
inset-block-start: 0;
|
|
264
|
+
inset-inline-start: 0;
|
|
265
|
+
width: var(--component-datum-mark-size);
|
|
266
|
+
height: var(--component-datum-mark-size);
|
|
267
|
+
border-block-start: var(--border-medium) solid var(--color-brand-accent);
|
|
268
|
+
border-inline-start: var(--border-medium) solid var(--color-brand-accent);
|
|
269
|
+
border-start-start-radius: var(--radius-lg);
|
|
270
|
+
pointer-events: none;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
@media (forced-colors: active) {
|
|
274
|
+
:is(.ct-metric--active, .ct-metric[data-state='active'], .ct-metric[data-status], .ct-metric[data-state='error'], .ct-spec--origin) {
|
|
275
|
+
border-block-start: var(--border-medium) solid currentColor;
|
|
276
|
+
}
|
|
277
|
+
}
|
package/components/dropdown.css
CHANGED
|
@@ -146,7 +146,7 @@
|
|
|
146
146
|
/* ── Danger item ─────────────────────────────────────────────────── */
|
|
147
147
|
|
|
148
148
|
.ct-dropdown__item--danger {
|
|
149
|
-
color: var(--color-state-danger);
|
|
149
|
+
color: var(--color-state-danger-text);
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
@media (hover: hover) {
|
|
@@ -158,11 +158,11 @@
|
|
|
158
158
|
.ct-dropdown__item--danger:focus-visible,
|
|
159
159
|
.ct-dropdown__item--danger[data-highlighted] {
|
|
160
160
|
background: var(--color-state-danger-surface);
|
|
161
|
-
outline-color: var(--color-state-danger);
|
|
161
|
+
outline-color: var(--color-state-danger-text);
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
.ct-dropdown__item--danger .ct-dropdown__item-icon {
|
|
165
|
-
color: var(--color-state-danger);
|
|
165
|
+
color: var(--color-state-danger-text);
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
.ct-dropdown__item--danger .ct-dropdown__item-shortcut {
|
|
@@ -389,6 +389,15 @@
|
|
|
389
389
|
--ct-dropdown-item-gap: var(--space-4);
|
|
390
390
|
}
|
|
391
391
|
|
|
392
|
+
/* ── Construct 44×44 target policy (WCAG 2.2 SC 2.5.5 is AAA) ────── */
|
|
393
|
+
|
|
394
|
+
@media (pointer: coarse) {
|
|
395
|
+
.ct-dropdown__item,
|
|
396
|
+
.ct-dropdown__sub-trigger {
|
|
397
|
+
min-block-size: 44px;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
392
401
|
/* ── Reduced motion ──────────────────────────────────────────────── */
|
|
393
402
|
|
|
394
403
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -399,3 +408,14 @@
|
|
|
399
408
|
transition: none;
|
|
400
409
|
}
|
|
401
410
|
}
|
|
411
|
+
|
|
412
|
+
/* ── Forced colors ───────────────────────────────────────────────── */
|
|
413
|
+
|
|
414
|
+
@media (forced-colors: active) {
|
|
415
|
+
/* The radio-item dot is background-drawn and must survive forced
|
|
416
|
+
backgrounds. */
|
|
417
|
+
.ct-dropdown__item[aria-checked='true'] .ct-dropdown__item-radio::before {
|
|
418
|
+
background: SelectedItem;
|
|
419
|
+
forced-color-adjust: none;
|
|
420
|
+
}
|
|
421
|
+
}
|
package/components/field.css
CHANGED
|
@@ -16,12 +16,14 @@
|
|
|
16
16
|
|
|
17
17
|
.ct-field__error {
|
|
18
18
|
font-size: var(--font-size-sm);
|
|
19
|
-
color: var(--color-state-danger);
|
|
19
|
+
color: var(--color-state-danger-text);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
.ct-field__label--required::after {
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
/* Alt-text syntax hides the decorative asterisk from assistive tech;
|
|
24
|
+
the `required` attribute is the programmatic signal. */
|
|
25
|
+
content: ' *' / '';
|
|
26
|
+
color: var(--color-state-danger-text);
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
.ct-field__counter {
|
|
@@ -31,7 +33,7 @@
|
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
.ct-field__counter--limit {
|
|
34
|
-
color: var(--color-state-danger);
|
|
36
|
+
color: var(--color-state-danger-text);
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
.ct-field__row {
|