@ponchia/ui 0.6.12 → 0.8.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/CHANGELOG.md +210 -0
- package/MIGRATIONS.json +14 -0
- package/README.md +14 -6
- package/behaviors/dialog.d.ts.map +1 -1
- package/behaviors/dialog.js +14 -0
- package/behaviors/modal.d.ts +4 -0
- package/behaviors/modal.d.ts.map +1 -1
- package/behaviors/modal.js +4 -0
- package/behaviors/splitter.d.ts +2 -0
- package/behaviors/splitter.d.ts.map +1 -1
- package/behaviors/splitter.js +15 -1
- package/behaviors/theme.d.ts +3 -2
- package/behaviors/theme.d.ts.map +1 -1
- package/behaviors/theme.js +10 -6
- package/bin/bronto-ui-check.mjs +286 -0
- package/classes/classes.json +86 -7
- package/classes/index.d.ts +53 -1
- package/classes/index.js +60 -0
- package/classes/vscode.css-custom-data.json +24 -0
- package/css/app.css +14 -3
- package/css/disclosure.css +15 -5
- package/css/dots.css +43 -18
- package/css/feedback.css +46 -8
- package/css/forms.css +3 -3
- package/css/navigation.css +1 -1
- package/css/overlay.css +19 -5
- package/css/primitives.css +101 -6
- package/css/report.css +0 -40
- package/css/site.css +19 -3
- package/css/skins.css +97 -3
- package/css/state.css +161 -0
- package/css/table.css +1 -1
- package/css/tokens.css +6 -0
- package/css/workbench.css +151 -0
- package/dist/bronto.css +1 -1
- package/dist/css/app.css +1 -1
- package/dist/css/disclosure.css +1 -1
- package/dist/css/dots.css +1 -1
- package/dist/css/feedback.css +1 -1
- package/dist/css/forms.css +1 -1
- package/dist/css/navigation.css +1 -1
- package/dist/css/overlay.css +1 -1
- package/dist/css/primitives.css +1 -1
- package/dist/css/report-kit.css +1 -1
- package/dist/css/report.css +1 -1
- package/dist/css/site.css +1 -1
- package/dist/css/skins.css +1 -1
- package/dist/css/state.css +1 -1
- package/dist/css/table.css +1 -1
- package/dist/css/tokens.css +1 -1
- package/dist/css/workbench.css +1 -1
- package/docs/adr/0001-color-system.md +32 -3
- package/docs/adr/0004-prune-unused-adapters.md +34 -0
- package/docs/architecture.md +14 -10
- package/docs/command.md +18 -4
- package/docs/contrast.md +102 -18
- package/docs/migrations/0.6-to-0.7.md +85 -0
- package/docs/package-contract.md +10 -5
- package/docs/reference.md +52 -1
- package/docs/reporting.md +8 -8
- package/docs/stability.md +39 -9
- package/docs/state.md +51 -1
- package/docs/theming.md +67 -5
- package/docs/usage.md +119 -19
- package/docs/workbench.md +99 -6
- package/llms.txt +7 -3
- package/package.json +13 -3
- package/qwik/index.d.ts.map +1 -1
- package/qwik/index.js +4 -0
- package/react/index.d.ts.map +1 -1
- package/react/index.js +4 -0
- package/solid/index.d.ts.map +1 -1
- package/solid/index.js +4 -0
- package/svelte/index.d.ts.map +1 -1
- package/svelte/index.js +4 -0
- package/tokens/figma.variables.json +84 -0
- package/tokens/index.d.ts +2 -2
- package/tokens/index.js +23 -0
- package/tokens/index.json +12 -0
- package/tokens/resolved.json +6 -0
- package/tokens/skins.js +117 -7
- package/tokens/tokens.dtcg.json +2514 -399
- package/vue/index.d.ts.map +1 -1
- package/vue/index.js +4 -0
package/classes/index.js
CHANGED
|
@@ -25,6 +25,8 @@ export const cls = Object.freeze({
|
|
|
25
25
|
buttonIcon: 'ui-button--icon',
|
|
26
26
|
buttonSm: 'ui-button--sm',
|
|
27
27
|
buttonLg: 'ui-button--lg',
|
|
28
|
+
buttonDense: 'ui-button--dense',
|
|
29
|
+
buttonLabel: 'ui-button__label',
|
|
28
30
|
card: 'ui-card',
|
|
29
31
|
cardHead: 'ui-card__head',
|
|
30
32
|
cardAccent: 'ui-card--accent',
|
|
@@ -58,6 +60,10 @@ export const cls = Object.freeze({
|
|
|
58
60
|
linkCta: 'ui-link--cta',
|
|
59
61
|
keyValue: 'ui-key-value',
|
|
60
62
|
emptyState: 'ui-empty-state',
|
|
63
|
+
emptyStateInvite: 'ui-empty-state--invite',
|
|
64
|
+
emptyStateGlyph: 'ui-empty-state__glyph',
|
|
65
|
+
emptyStateLead: 'ui-empty-state__lead',
|
|
66
|
+
emptyStateHint: 'ui-empty-state__hint',
|
|
61
67
|
// dots
|
|
62
68
|
dot: 'ui-dot',
|
|
63
69
|
dotAccent: 'ui-dot--accent',
|
|
@@ -597,6 +603,12 @@ export const cls = Object.freeze({
|
|
|
597
603
|
jobBlocked: 'ui-job--blocked',
|
|
598
604
|
jobFailed: 'ui-job--failed',
|
|
599
605
|
jobComplete: 'ui-job--complete',
|
|
606
|
+
// severity ladder — the second axis of state (css/state.css)
|
|
607
|
+
severity: 'ui-severity',
|
|
608
|
+
severityDot: 'ui-severity-dot',
|
|
609
|
+
severityRow: 'ui-severity-row',
|
|
610
|
+
severityRowTitle: 'ui-severity-row__title',
|
|
611
|
+
severityRowMeta: 'ui-severity-row__meta',
|
|
600
612
|
// generated content / AI-trust surfaces (css/generated.css)
|
|
601
613
|
generated: 'ui-generated',
|
|
602
614
|
generatedLabel: 'ui-generated__label',
|
|
@@ -616,11 +628,22 @@ export const cls = Object.freeze({
|
|
|
616
628
|
toolstrip: 'ui-toolstrip',
|
|
617
629
|
toolstripFloating: 'ui-toolstrip--floating',
|
|
618
630
|
toolstripCompact: 'ui-toolstrip--compact',
|
|
631
|
+
toolstripPane: 'ui-toolstrip--pane',
|
|
632
|
+
toolstripAnchored: 'ui-toolstrip--anchored',
|
|
633
|
+
toolstripAnchorBlockStart: 'ui-toolstrip--anchor-block-start',
|
|
634
|
+
toolstripAnchorBlockEnd: 'ui-toolstrip--anchor-block-end',
|
|
619
635
|
toolstripBrand: 'ui-toolstrip__brand',
|
|
620
636
|
toolstripContext: 'ui-toolstrip__context',
|
|
621
637
|
toolstripGroup: 'ui-toolstrip__group',
|
|
622
638
|
toolstripActions: 'ui-toolstrip__actions',
|
|
639
|
+
toolstripFill: 'ui-toolstrip__fill',
|
|
623
640
|
toolstripSearch: 'ui-toolstrip__search',
|
|
641
|
+
pane: 'ui-pane',
|
|
642
|
+
paneHead: 'ui-pane__head',
|
|
643
|
+
paneTitle: 'ui-pane__title',
|
|
644
|
+
paneTitleInput: 'ui-pane__title-input',
|
|
645
|
+
paneActions: 'ui-pane__actions',
|
|
646
|
+
paneBody: 'ui-pane__body',
|
|
624
647
|
segmentedButtons: 'ui-segmented-buttons',
|
|
625
648
|
segmentedButtonsButton: 'ui-segmented-buttons__button',
|
|
626
649
|
property: 'ui-property',
|
|
@@ -629,6 +652,9 @@ export const cls = Object.freeze({
|
|
|
629
652
|
selectionbar: 'ui-selectionbar',
|
|
630
653
|
selectionbarCount: 'ui-selectionbar__count',
|
|
631
654
|
selectionbarActions: 'ui-selectionbar__actions',
|
|
655
|
+
selectionbarAnchored: 'ui-selectionbar--anchored',
|
|
656
|
+
selectionbarAnchorBlockStart: 'ui-selectionbar--anchor-block-start',
|
|
657
|
+
selectionbarAnchorBlockEnd: 'ui-selectionbar--anchor-block-end',
|
|
632
658
|
splitter: 'ui-splitter',
|
|
633
659
|
splitterVertical: 'ui-splitter--vertical',
|
|
634
660
|
splitterHorizontal: 'ui-splitter--horizontal',
|
|
@@ -934,6 +960,7 @@ export const ui = {
|
|
|
934
960
|
icon && cls.buttonIcon,
|
|
935
961
|
size === 'sm' && cls.buttonSm,
|
|
936
962
|
size === 'lg' && cls.buttonLg,
|
|
963
|
+
size === 'dense' && cls.buttonDense,
|
|
937
964
|
),
|
|
938
965
|
card: ({ accent, interactive } = {}) =>
|
|
939
966
|
j(cls.card, accent && cls.cardAccent, interactive && cls.cardInteractive),
|
|
@@ -1092,8 +1119,41 @@ export const ui = {
|
|
|
1092
1119
|
state: ({ state, busy } = {}) => j(cls.state, stateTone(state), busy && cls.stateBusy),
|
|
1093
1120
|
job: ({ state, compact } = {}) => j(cls.job, jobTone(state), compact && cls.jobCompact),
|
|
1094
1121
|
originLabel: ({ ai } = {}) => j(cls.originLabel, ai && cls.originLabelAi),
|
|
1122
|
+
emptyState: ({ invite } = {}) => j(cls.emptyState, invite && cls.emptyStateInvite),
|
|
1123
|
+
toolstrip: ({ variant, anchor } = {}) =>
|
|
1124
|
+
j(
|
|
1125
|
+
cls.toolstrip,
|
|
1126
|
+
variant === 'floating' && cls.toolstripFloating,
|
|
1127
|
+
variant === 'compact' && cls.toolstripCompact,
|
|
1128
|
+
variant === 'pane' && cls.toolstripPane,
|
|
1129
|
+
anchor && cls.toolstripAnchored,
|
|
1130
|
+
anchor === 'block-start' && cls.toolstripAnchorBlockStart,
|
|
1131
|
+
anchor === 'block-end' && cls.toolstripAnchorBlockEnd,
|
|
1132
|
+
),
|
|
1133
|
+
selectionbar: ({ anchor } = {}) =>
|
|
1134
|
+
j(
|
|
1135
|
+
cls.selectionbar,
|
|
1136
|
+
anchor && cls.selectionbarAnchored,
|
|
1137
|
+
anchor === 'block-start' && cls.selectionbarAnchorBlockStart,
|
|
1138
|
+
anchor === 'block-end' && cls.selectionbarAnchorBlockEnd,
|
|
1139
|
+
),
|
|
1095
1140
|
};
|
|
1096
1141
|
|
|
1142
|
+
/** The canonical severity ladder, worst to best. `unknown` is deliberately NOT
|
|
1143
|
+
* in the ordering — it means "not measured", not "nearly ok", and sorting it
|
|
1144
|
+
* next to `ok` is how a dead collector reads as a healthy system. Exported as
|
|
1145
|
+
* data so a host can drive filters and sorts from the same list the CSS
|
|
1146
|
+
* paints, instead of re-typing the tiers. */
|
|
1147
|
+
export const SEVERITY_LEVELS = Object.freeze(['critical', 'error', 'warning', 'notice', 'ok']);
|
|
1148
|
+
|
|
1149
|
+
/** `data-level` + the severity class, bundled so the attribute that carries the
|
|
1150
|
+
* meaning cannot be forgotten — the class alone paints the neutral tone and
|
|
1151
|
+
* silently loses the level. Unknown/absent levels resolve to `unknown`. */
|
|
1152
|
+
export const severity = (level, { part = 'chip' } = {}) => ({
|
|
1153
|
+
class: part === 'dot' ? cls.severityDot : part === 'row' ? cls.severityRow : cls.severity,
|
|
1154
|
+
'data-level': SEVERITY_LEVELS.includes(level) ? level : 'unknown',
|
|
1155
|
+
});
|
|
1156
|
+
|
|
1097
1157
|
// Attribute + style bundle for the data-bearing fills (`ui-meter`/`ui-progress`).
|
|
1098
1158
|
// The class string alone paints a 0-width, unannounced bar: the fill width is a
|
|
1099
1159
|
// UNITLESS `--value` (0–100) and AT needs role + aria-valuenow/min/max. This sets
|
|
@@ -257,6 +257,22 @@
|
|
|
257
257
|
"name": "--radius-xl",
|
|
258
258
|
"description": "Global scale token. Value: `4px`"
|
|
259
259
|
},
|
|
260
|
+
{
|
|
261
|
+
"name": "--safe-area-bottom",
|
|
262
|
+
"description": "Global scale token. Value: `env(safe-area-inset-bottom, 0px)`"
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
"name": "--safe-area-left",
|
|
266
|
+
"description": "Global scale token. Value: `env(safe-area-inset-left, 0px)`"
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
"name": "--safe-area-right",
|
|
270
|
+
"description": "Global scale token. Value: `env(safe-area-inset-right, 0px)`"
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
"name": "--safe-area-top",
|
|
274
|
+
"description": "Global scale token. Value: `env(safe-area-inset-top, 0px)`"
|
|
275
|
+
},
|
|
260
276
|
{
|
|
261
277
|
"name": "--sans",
|
|
262
278
|
"description": "Global scale token. Value: `'Inter', 'SF Pro Text', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif`"
|
|
@@ -341,6 +357,14 @@
|
|
|
341
357
|
"name": "--surface-raised",
|
|
342
358
|
"description": "Global scale token. Value: `var(--panel-strong)`"
|
|
343
359
|
},
|
|
360
|
+
{
|
|
361
|
+
"name": "--tap-target",
|
|
362
|
+
"description": "Global scale token. Value: `max(44px, 2.9rem)`"
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
"name": "--tap-target-min",
|
|
366
|
+
"description": "Global scale token. Value: `max(24px, 1.6rem)`"
|
|
367
|
+
},
|
|
344
368
|
{
|
|
345
369
|
"name": "--text",
|
|
346
370
|
"description": "Theme token. Light: `#0a0a0a` · Dark: `#e6e6e6`"
|
package/css/app.css
CHANGED
|
@@ -21,7 +21,15 @@
|
|
|
21
21
|
display: flex;
|
|
22
22
|
flex-direction: column;
|
|
23
23
|
gap: var(--space-md);
|
|
24
|
-
|
|
24
|
+
|
|
25
|
+
/* The rail runs the full viewport height, so on a notched device its top and
|
|
26
|
+
bottom sit under the status bar and the home indicator. Grow the padding to
|
|
27
|
+
clear them; `max()` keeps the desktop value exactly as authored, since the
|
|
28
|
+
insets resolve to 0px there. The inline-start inset covers a landscape
|
|
29
|
+
rotation that puts the cutout on the rail's side. */
|
|
30
|
+
padding: max(var(--space-md), var(--safe-area-top)) var(--space-md)
|
|
31
|
+
max(var(--space-md), var(--safe-area-bottom))
|
|
32
|
+
max(var(--space-md), var(--safe-area-left));
|
|
25
33
|
position: sticky;
|
|
26
34
|
inset-block-start: 0;
|
|
27
35
|
block-size: 100svh;
|
|
@@ -108,7 +116,7 @@
|
|
|
108
116
|
|
|
109
117
|
@media (pointer: coarse) {
|
|
110
118
|
.ui-app-nav a {
|
|
111
|
-
min-block-size:
|
|
119
|
+
min-block-size: var(--tap-target);
|
|
112
120
|
padding-inline: 0.9rem;
|
|
113
121
|
}
|
|
114
122
|
}
|
|
@@ -178,7 +186,10 @@
|
|
|
178
186
|
display: flex;
|
|
179
187
|
gap: var(--space-md);
|
|
180
188
|
justify-content: space-between;
|
|
181
|
-
|
|
189
|
+
|
|
190
|
+
/* Sticks to the very top, so the bar's own background must bleed under the
|
|
191
|
+
status bar while its CONTENT clears it — pad, do not offset. */
|
|
192
|
+
padding: max(0.75rem, var(--safe-area-top)) var(--space-lg) 0.75rem;
|
|
182
193
|
position: sticky;
|
|
183
194
|
inset-block-start: 0;
|
|
184
195
|
z-index: var(--z-raised);
|
package/css/disclosure.css
CHANGED
|
@@ -44,11 +44,11 @@
|
|
|
44
44
|
border-color var(--duration-fast) var(--ease-standard);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
/* Coarse-pointer tap-target floor (
|
|
47
|
+
/* Coarse-pointer tap-target floor (`--tap-target`), matching inputs/sitenav —
|
|
48
48
|
tabs are easy to mis-tap on touch (WCAG 2.5.8 — C24). */
|
|
49
49
|
@media (pointer: coarse) {
|
|
50
50
|
.ui-tab {
|
|
51
|
-
min-block-size:
|
|
51
|
+
min-block-size: var(--tap-target);
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -272,6 +272,16 @@
|
|
|
272
272
|
}
|
|
273
273
|
}
|
|
274
274
|
|
|
275
|
+
@media (pointer: coarse) {
|
|
276
|
+
.ui-breadcrumb__item a {
|
|
277
|
+
align-items: center;
|
|
278
|
+
display: inline-flex;
|
|
279
|
+
justify-content: center;
|
|
280
|
+
min-block-size: var(--tap-target-min);
|
|
281
|
+
min-inline-size: var(--tap-target-min);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
275
285
|
/* --- Pagination --- */
|
|
276
286
|
|
|
277
287
|
.ui-pagination {
|
|
@@ -339,12 +349,12 @@
|
|
|
339
349
|
}
|
|
340
350
|
}
|
|
341
351
|
|
|
342
|
-
/* Coarse-pointer tap-target floor, matching inputs/sitenav
|
|
352
|
+
/* Coarse-pointer tap-target floor (`--tap-target`), matching inputs/sitenav —
|
|
343
353
|
pagination controls are easy to mis-tap on touch (WCAG 2.5.8 — C24). */
|
|
344
354
|
@media (pointer: coarse) {
|
|
345
355
|
.ui-pagination__item {
|
|
346
|
-
min-block-size:
|
|
347
|
-
min-inline-size:
|
|
356
|
+
min-block-size: var(--tap-target);
|
|
357
|
+
min-inline-size: var(--tap-target);
|
|
348
358
|
}
|
|
349
359
|
}
|
|
350
360
|
|
package/css/dots.css
CHANGED
|
@@ -136,7 +136,9 @@
|
|
|
136
136
|
|
|
137
137
|
/* Status dot — the glyph-style state indicator. */
|
|
138
138
|
.ui-dot {
|
|
139
|
-
|
|
139
|
+
--ui-dot-color: var(--text-dim);
|
|
140
|
+
|
|
141
|
+
background: var(--ui-dot-color);
|
|
140
142
|
border-radius: 50%;
|
|
141
143
|
display: inline-block;
|
|
142
144
|
flex: 0 0 auto;
|
|
@@ -145,34 +147,45 @@
|
|
|
145
147
|
}
|
|
146
148
|
|
|
147
149
|
.ui-dot--accent {
|
|
148
|
-
|
|
150
|
+
--ui-dot-color: var(--accent);
|
|
149
151
|
}
|
|
150
152
|
|
|
151
153
|
.ui-dot--success {
|
|
152
|
-
|
|
154
|
+
--ui-dot-color: var(--success);
|
|
153
155
|
}
|
|
154
156
|
|
|
155
157
|
.ui-dot--warning {
|
|
156
|
-
|
|
158
|
+
--ui-dot-color: var(--warning);
|
|
157
159
|
}
|
|
158
160
|
|
|
159
161
|
.ui-dot--danger {
|
|
160
|
-
|
|
162
|
+
--ui-dot-color: var(--danger);
|
|
161
163
|
}
|
|
162
164
|
|
|
163
165
|
.ui-dot--info {
|
|
164
|
-
|
|
166
|
+
--ui-dot-color: var(--info);
|
|
165
167
|
}
|
|
166
168
|
|
|
167
169
|
.ui-dot--live {
|
|
168
|
-
|
|
169
|
-
box-shadow: 0 0 0 0 color-mix(in srgb, var(--success) 70%, transparent);
|
|
170
|
+
box-shadow: 0 0 0 0 color-mix(in srgb, var(--ui-dot-color) 70%, transparent);
|
|
170
171
|
position: relative;
|
|
171
172
|
}
|
|
172
173
|
|
|
174
|
+
/* Live describes motion, not status. A standalone live dot defaults to
|
|
175
|
+
success; an explicit tone remains the colour of both the dot and ring. */
|
|
176
|
+
.ui-dot--live:not(
|
|
177
|
+
.ui-dot--accent,
|
|
178
|
+
.ui-dot--success,
|
|
179
|
+
.ui-dot--warning,
|
|
180
|
+
.ui-dot--danger,
|
|
181
|
+
.ui-dot--info
|
|
182
|
+
) {
|
|
183
|
+
--ui-dot-color: var(--success);
|
|
184
|
+
}
|
|
185
|
+
|
|
173
186
|
.ui-dot--live::after {
|
|
174
187
|
animation: pulseRing 1.8s var(--ease-out) infinite;
|
|
175
|
-
border: 1px solid var(--
|
|
188
|
+
border: 1px solid var(--ui-dot-color);
|
|
176
189
|
border-radius: 50%;
|
|
177
190
|
content: '';
|
|
178
191
|
inset: -3px;
|
|
@@ -562,36 +575,48 @@
|
|
|
562
575
|
differentiable. */
|
|
563
576
|
@media (forced-colors: active) {
|
|
564
577
|
.ui-dot--success {
|
|
578
|
+
--ui-dot-color: LinkText;
|
|
579
|
+
|
|
565
580
|
forced-color-adjust: none;
|
|
566
|
-
background: LinkText;
|
|
567
581
|
}
|
|
568
582
|
|
|
569
583
|
.ui-dot--warning {
|
|
584
|
+
--ui-dot-color: Mark;
|
|
585
|
+
|
|
570
586
|
forced-color-adjust: none;
|
|
571
|
-
background: Mark;
|
|
572
587
|
}
|
|
573
588
|
|
|
574
589
|
.ui-dot--danger {
|
|
590
|
+
--ui-dot-color: Highlight;
|
|
591
|
+
|
|
575
592
|
forced-color-adjust: none;
|
|
576
|
-
background: Highlight;
|
|
577
593
|
}
|
|
578
594
|
|
|
579
595
|
.ui-dot--info {
|
|
596
|
+
--ui-dot-color: ButtonText;
|
|
597
|
+
|
|
580
598
|
forced-color-adjust: none;
|
|
581
|
-
background: ButtonText;
|
|
582
599
|
}
|
|
583
600
|
|
|
584
601
|
/* Brand/live dots aren't status tones, but they still encode meaning via
|
|
585
602
|
background-color alone, which HCM flattens. Keep them on a distinct,
|
|
586
603
|
opted-out system colour for completeness. */
|
|
587
|
-
.ui-dot--accent
|
|
588
|
-
|
|
604
|
+
.ui-dot--accent {
|
|
605
|
+
--ui-dot-color: LinkText;
|
|
606
|
+
|
|
589
607
|
forced-color-adjust: none;
|
|
590
|
-
background: LinkText;
|
|
591
608
|
}
|
|
592
609
|
|
|
593
|
-
.ui-dot--live
|
|
594
|
-
|
|
610
|
+
.ui-dot--live:not(
|
|
611
|
+
.ui-dot--accent,
|
|
612
|
+
.ui-dot--success,
|
|
613
|
+
.ui-dot--warning,
|
|
614
|
+
.ui-dot--danger,
|
|
615
|
+
.ui-dot--info
|
|
616
|
+
) {
|
|
617
|
+
--ui-dot-color: LinkText;
|
|
618
|
+
|
|
619
|
+
forced-color-adjust: none;
|
|
595
620
|
}
|
|
596
621
|
|
|
597
622
|
/* The masked one-node icon paints `background: currentcolor` through an SVG
|
package/css/feedback.css
CHANGED
|
@@ -130,9 +130,12 @@
|
|
|
130
130
|
display: grid;
|
|
131
131
|
gap: 0.5rem;
|
|
132
132
|
|
|
133
|
-
/* Logical so the stack anchors bottom inline-end and mirrors in RTL.
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
/* Logical so the stack anchors bottom inline-end and mirrors in RTL. The
|
|
134
|
+
bottom offset clears the home-indicator gesture area: a toast landing there
|
|
135
|
+
is both hard to read and hard to dismiss, because the system swallows the
|
|
136
|
+
swipe. `max()` leaves the desktop offset untouched. */
|
|
137
|
+
inset-block: auto max(1.25rem, var(--safe-area-bottom));
|
|
138
|
+
inset-inline: auto max(1.25rem, var(--safe-area-right));
|
|
136
139
|
justify-items: end;
|
|
137
140
|
max-inline-size: min(22rem, calc(100vw - 2rem));
|
|
138
141
|
pointer-events: none;
|
|
@@ -144,7 +147,7 @@
|
|
|
144
147
|
overlaps the polite bottom stack and reads as a distinct, urgent
|
|
145
148
|
channel. */
|
|
146
149
|
.ui-toast-stack--assertive {
|
|
147
|
-
inset-block: 1.25rem auto;
|
|
150
|
+
inset-block: max(1.25rem, var(--safe-area-top)) auto;
|
|
148
151
|
}
|
|
149
152
|
|
|
150
153
|
.ui-toast {
|
|
@@ -225,15 +228,15 @@
|
|
|
225
228
|
}
|
|
226
229
|
|
|
227
230
|
/* Touch: enlarge the dismiss/close hit areas to the project's coarse-pointer
|
|
228
|
-
tap-target floor (
|
|
229
|
-
the desktop glyph size. The box is centred so the glyph stays put. */
|
|
231
|
+
tap-target floor (`--tap-target`, as in primitives.css / forms.css) without
|
|
232
|
+
changing the desktop glyph size. The box is centred so the glyph stays put. */
|
|
230
233
|
@media (pointer: coarse) {
|
|
231
234
|
.ui-alert__close,
|
|
232
235
|
.ui-toast__close {
|
|
233
236
|
display: inline-grid;
|
|
234
237
|
place-items: center;
|
|
235
|
-
min-block-size:
|
|
236
|
-
min-inline-size:
|
|
238
|
+
min-block-size: var(--tap-target);
|
|
239
|
+
min-inline-size: var(--tap-target);
|
|
237
240
|
}
|
|
238
241
|
|
|
239
242
|
.ui-alert__close {
|
|
@@ -517,6 +520,41 @@
|
|
|
517
520
|
background: var(--info);
|
|
518
521
|
}
|
|
519
522
|
|
|
523
|
+
/* A labelled meter is core feedback grammar, not report-only layout. The
|
|
524
|
+
visible value remains the data of record because role=meter support varies. */
|
|
525
|
+
.ui-meter__row {
|
|
526
|
+
align-items: center;
|
|
527
|
+
display: grid;
|
|
528
|
+
gap: var(--space-2xs) var(--space-md);
|
|
529
|
+
grid-template-columns: minmax(9rem, 14rem) 1fr auto;
|
|
530
|
+
margin-block: var(--space-2xs);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
.ui-meter__row .ui-meter {
|
|
534
|
+
min-inline-size: 8rem;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
.ui-meter__label {
|
|
538
|
+
color: var(--text-soft);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
.ui-meter__value {
|
|
542
|
+
color: var(--text);
|
|
543
|
+
font-family: var(--mono);
|
|
544
|
+
font-variant-numeric: tabular-nums;
|
|
545
|
+
text-align: end;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
@media (max-width: 32rem) {
|
|
549
|
+
.ui-meter__row {
|
|
550
|
+
grid-template-columns: 1fr;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
.ui-meter__value {
|
|
554
|
+
text-align: start;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
520
558
|
/* --- Steps — progress through a multi-step flow. Use an <ol>; the
|
|
521
559
|
current step is aria-current="step" (no class), completed steps take
|
|
522
560
|
--done. Counter-numbered markers, hairline connectors. --- */
|
package/css/forms.css
CHANGED
|
@@ -476,18 +476,18 @@
|
|
|
476
476
|
.ui-input,
|
|
477
477
|
.ui-select,
|
|
478
478
|
.ui-textarea {
|
|
479
|
-
min-block-size:
|
|
479
|
+
min-block-size: var(--tap-target);
|
|
480
480
|
}
|
|
481
481
|
|
|
482
482
|
.ui-search {
|
|
483
|
-
min-block-size:
|
|
483
|
+
min-block-size: var(--tap-target);
|
|
484
484
|
}
|
|
485
485
|
|
|
486
486
|
/* The whole label is the toggle target — float it to the ~44px floor so the
|
|
487
487
|
small track/box isn't a sub-target on touch (WCAG 2.5.8 — C24). */
|
|
488
488
|
.ui-switch,
|
|
489
489
|
.ui-check {
|
|
490
|
-
min-block-size:
|
|
490
|
+
min-block-size: var(--tap-target);
|
|
491
491
|
}
|
|
492
492
|
|
|
493
493
|
.ui-check input {
|
package/css/navigation.css
CHANGED
package/css/overlay.css
CHANGED
|
@@ -149,6 +149,15 @@ html:has(.ui-modal.is-open) {
|
|
|
149
149
|
margin-inline-start: auto;
|
|
150
150
|
max-block-size: 100vh;
|
|
151
151
|
max-inline-size: min(26rem, 100vw);
|
|
152
|
+
|
|
153
|
+
/* Full-height by design, so the panel's edges are the screen's edges: its
|
|
154
|
+
surface should still reach them, but its content must clear the cutout and
|
|
155
|
+
the home indicator. Add the INSET ONLY — `.ui-modal` is deliberately
|
|
156
|
+
`padding: 0` (its head/body parts own their spacing), so a `max(space, …)`
|
|
157
|
+
floor here would silently pad the drawer on every device, not just a
|
|
158
|
+
notched one. */
|
|
159
|
+
padding-block: var(--safe-area-top) var(--safe-area-bottom);
|
|
160
|
+
padding-inline-end: var(--safe-area-right);
|
|
152
161
|
border-radius: 0;
|
|
153
162
|
border-width: 0;
|
|
154
163
|
border-inline-start-width: 1px;
|
|
@@ -162,16 +171,16 @@ html:has(.ui-modal.is-open) {
|
|
|
162
171
|
}
|
|
163
172
|
|
|
164
173
|
/* Comfortable hit target on coarse pointers — this bespoke close button measured
|
|
165
|
-
~26px on touch, below the
|
|
166
|
-
|
|
174
|
+
~26px on touch, below the `--tap-target` floor the rest of the button family
|
|
175
|
+
meets. Scoped to coarse so the fine-pointer (mouse) rendering, which
|
|
167
176
|
already clears 24×24, is unchanged; centre the glyph in the enlarged box. */
|
|
168
177
|
@media (pointer: coarse) {
|
|
169
178
|
.ui-modal__close {
|
|
170
179
|
align-items: center;
|
|
171
180
|
display: inline-flex;
|
|
172
181
|
justify-content: center;
|
|
173
|
-
min-block-size:
|
|
174
|
-
min-inline-size:
|
|
182
|
+
min-block-size: var(--tap-target);
|
|
183
|
+
min-inline-size: var(--tap-target);
|
|
175
184
|
}
|
|
176
185
|
}
|
|
177
186
|
|
|
@@ -187,7 +196,12 @@ html:has(.ui-modal.is-open) {
|
|
|
187
196
|
color: var(--text);
|
|
188
197
|
max-block-size: 100vh;
|
|
189
198
|
max-inline-size: 100vw;
|
|
190
|
-
|
|
199
|
+
|
|
200
|
+
/* Deliberately full-bleed — the dimmed ground SHOULD cover the whole screen,
|
|
201
|
+
including the cutout. Only the insets keep the carousel's own controls out
|
|
202
|
+
from under it. */
|
|
203
|
+
padding: var(--safe-area-top) var(--safe-area-right) var(--safe-area-bottom)
|
|
204
|
+
var(--safe-area-left);
|
|
191
205
|
inline-size: 100vw;
|
|
192
206
|
}
|
|
193
207
|
|
package/css/primitives.css
CHANGED
|
@@ -423,6 +423,53 @@
|
|
|
423
423
|
padding: 0.85rem 1.4rem;
|
|
424
424
|
}
|
|
425
425
|
|
|
426
|
+
/* Dense — for bars whose HEIGHT is the constraint: a pane/node title bar, a
|
|
427
|
+
packed toolbar, a table row's actions. It lowers only the *visual* floor, to
|
|
428
|
+
the WCAG 2.5.8 minimum, and deliberately declares no coarse-pointer opinion:
|
|
429
|
+
the `@media (pointer: coarse)` block below matches at equal specificity and
|
|
430
|
+
later in the file, so it still floats a dense control to the full
|
|
431
|
+
`--tap-target`. Shrinking a control for a mouse therefore never shrinks it
|
|
432
|
+
for a finger — which is the reason this is a separate tier and not just a
|
|
433
|
+
smaller `--sm`. */
|
|
434
|
+
.ui-button--dense {
|
|
435
|
+
gap: 0.25rem;
|
|
436
|
+
min-block-size: var(--tap-target-min);
|
|
437
|
+
min-inline-size: var(--tap-target-min);
|
|
438
|
+
padding: 0.2rem 0.35rem;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/* Keep the square icon well; padding is safe to raise specificity on because
|
|
442
|
+
it is not the tap floor. */
|
|
443
|
+
.ui-button--dense.ui-button--icon {
|
|
444
|
+
padding: 0;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/* The label slot. An icon button still needs its words — for the accessible
|
|
448
|
+
name, and for the test/automation selector that reads the button by its
|
|
449
|
+
text — but not the pixels. Wrapping the text in this slot lets ONE markup
|
|
450
|
+
shape serve both: the button keeps a real text node, and `--icon` is what
|
|
451
|
+
decides whether it is painted. Without it, an icon-only button has to choose
|
|
452
|
+
between an empty accessible name and a title-bar-inflating word.
|
|
453
|
+
|
|
454
|
+
The hiding technique matches `.ui-visually-hidden` (base.css) rather than
|
|
455
|
+
`display: none`, which would take the text out of the accessible name too —
|
|
456
|
+
the exact opposite of the point. */
|
|
457
|
+
.ui-button__label {
|
|
458
|
+
min-inline-size: 0;
|
|
459
|
+
overflow: hidden;
|
|
460
|
+
text-overflow: ellipsis;
|
|
461
|
+
white-space: nowrap;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
.ui-button--icon .ui-button__label {
|
|
465
|
+
clip-path: inset(50%);
|
|
466
|
+
block-size: 1px;
|
|
467
|
+
overflow: hidden;
|
|
468
|
+
position: absolute;
|
|
469
|
+
white-space: nowrap;
|
|
470
|
+
inline-size: 1px;
|
|
471
|
+
}
|
|
472
|
+
|
|
426
473
|
.ui-button:disabled,
|
|
427
474
|
.ui-button[aria-disabled='true'] {
|
|
428
475
|
cursor: not-allowed;
|
|
@@ -539,11 +586,11 @@
|
|
|
539
586
|
/* Standalone CTA links are tap targets, not inline prose links: on a coarse
|
|
540
587
|
pointer float them to the WCAG 2.5.8 AA 24px floor (the 2.5.8 inline-link
|
|
541
588
|
exception doesn't cover a block-level call-to-action, which is what these
|
|
542
|
-
are). Buttons already
|
|
589
|
+
are). Buttons already float to the full `--tap-target` on coarse pointers. */
|
|
543
590
|
@media (pointer: coarse) {
|
|
544
591
|
.ui-link--arrow,
|
|
545
592
|
.ui-link--cta {
|
|
546
|
-
min-block-size:
|
|
593
|
+
min-block-size: var(--tap-target-min);
|
|
547
594
|
}
|
|
548
595
|
}
|
|
549
596
|
|
|
@@ -743,16 +790,23 @@
|
|
|
743
790
|
}
|
|
744
791
|
}
|
|
745
792
|
|
|
746
|
-
/* --- Touch: comfortable hit targets on coarse pointers
|
|
793
|
+
/* --- Touch: comfortable hit targets on coarse pointers.
|
|
794
|
+
|
|
795
|
+
The floor is `--tap-target`, not a bare rem. Bronto's own base sets
|
|
796
|
+
`html { font-size: 0.9375rem }`, so the 2.9rem this family used to carry
|
|
797
|
+
resolved to 43.5px — half a pixel under the 44px WCAG 2.5.5 / platform-HIG
|
|
798
|
+
target, and under any host that shrinks the root further it drifted further
|
|
799
|
+
still. The token clamps in px against the rem so the standard holds however
|
|
800
|
+
the root is re-pointed. --- */
|
|
747
801
|
|
|
748
802
|
@media (pointer: coarse) {
|
|
749
803
|
.ui-button {
|
|
750
|
-
min-block-size:
|
|
751
|
-
min-inline-size:
|
|
804
|
+
min-block-size: var(--tap-target);
|
|
805
|
+
min-inline-size: var(--tap-target);
|
|
752
806
|
}
|
|
753
807
|
|
|
754
808
|
.ui-button--icon {
|
|
755
|
-
min-inline-size:
|
|
809
|
+
min-inline-size: var(--tap-target);
|
|
756
810
|
}
|
|
757
811
|
}
|
|
758
812
|
|
|
@@ -774,6 +828,47 @@
|
|
|
774
828
|
margin: 0.3rem 0 0;
|
|
775
829
|
}
|
|
776
830
|
|
|
831
|
+
/* Slots. Without them every empty surface in an app re-invents the same three
|
|
832
|
+
parts under a different name, and they drift: one has a glyph, the next has
|
|
833
|
+
two sentences in the same weight, a third is a bare <p>. The shape is always
|
|
834
|
+
mark → one sentence in full ink → a quieter follow-up. */
|
|
835
|
+
.ui-empty-state__glyph {
|
|
836
|
+
display: block;
|
|
837
|
+
font-size: 1.5rem;
|
|
838
|
+
line-height: 1;
|
|
839
|
+
opacity: 0.5;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/* The one sentence that says what is missing. Full ink — it is the message,
|
|
843
|
+
not a caption, and the container's --text-dim would bury it. */
|
|
844
|
+
.ui-empty-state__lead {
|
|
845
|
+
color: var(--text);
|
|
846
|
+
margin: 0;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
.ui-empty-state__hint {
|
|
850
|
+
font-size: var(--text-2xs);
|
|
851
|
+
margin: 0;
|
|
852
|
+
opacity: 0.8;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
/* An empty state REPORTS absence; an invite OFFERS the next action. That is a
|
|
856
|
+
different job, so it gets a different frame: no dashed box (there is nothing
|
|
857
|
+
to outline — the surface itself is the thing you are being invited into) and
|
|
858
|
+
it centres in whatever block space it is given rather than sitting as a
|
|
859
|
+
card. Use it for a surface the user is meant to fill, and keep the plain
|
|
860
|
+
`.ui-empty-state` for a region that simply has no rows today. */
|
|
861
|
+
.ui-empty-state--invite {
|
|
862
|
+
align-items: center;
|
|
863
|
+
border: 0;
|
|
864
|
+
display: flex;
|
|
865
|
+
flex-direction: column;
|
|
866
|
+
gap: var(--space-2xs);
|
|
867
|
+
justify-content: center;
|
|
868
|
+
min-block-size: 100%;
|
|
869
|
+
padding: var(--space-sm);
|
|
870
|
+
}
|
|
871
|
+
|
|
777
872
|
/* --- Keyboard key — inline glyph for shortcut hints. Wrap a <kbd>. --- */
|
|
778
873
|
|
|
779
874
|
.ui-kbd {
|