@godxjp/ui 18.10.0 → 18.11.1
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/components/data-display/card.d.ts +27 -3
- package/dist/components/data-display/card.js +4 -1
- package/dist/components/data-display/data-table.d.ts +4 -1
- package/dist/components/data-display/data-table.js +33 -5
- package/dist/components/data-entry/command-palette.d.ts +42 -1
- package/dist/components/data-entry/command-palette.js +31 -2
- package/dist/components/data-entry/input-otp.d.ts +1 -1
- package/dist/components/data-entry/select.d.ts +7 -1
- package/dist/components/data-entry/select.js +46 -26
- package/dist/components/feedback/banner.js +5 -1
- package/dist/components/layout/page-container.d.ts +1 -1
- package/dist/components/layout/page-container.js +61 -41
- package/dist/components/layout/resizable.d.ts +1 -1
- package/dist/components/layout/resizable.js +26 -8
- package/dist/components/navigation/pagination.js +20 -1
- package/dist/components/navigation/steps.d.ts +2 -2
- package/dist/components/navigation/steps.js +4 -2
- package/dist/components/navigation/tabs.js +6 -1
- package/dist/components/ui/avatar.d.ts +1 -1
- package/dist/components/ui/avatar.js +2 -1
- package/dist/components/ui/input-otp.d.ts +15 -3
- package/dist/components/ui/input-otp.js +2 -1
- package/dist/email/tokens.generated.d.ts +2 -2
- package/dist/email/tokens.generated.js +2 -2
- package/dist/i18n/messages/en.json +3 -0
- package/dist/i18n/messages/ja.json +3 -0
- package/dist/i18n/messages/vi.json +3 -0
- package/dist/lib/field-a11y.d.ts +7 -0
- package/dist/lib/field-a11y.js +6 -0
- package/dist/lib/hooks.d.ts +15 -0
- package/dist/lib/hooks.js +37 -0
- package/dist/props/components/data-display.prop.d.ts +16 -0
- package/dist/props/components/data-entry.prop.d.ts +7 -0
- package/dist/props/components/layout.prop.d.ts +8 -0
- package/dist/props/components/navigation.prop.d.ts +9 -0
- package/dist/props/registry.d.ts +20 -1
- package/dist/props/registry.js +21 -1
- package/dist/styles/alert-layout.css +1 -1
- package/dist/styles/base.css +28 -0
- package/dist/styles/card-layout.css +49 -13
- package/dist/styles/control.css +17 -2
- package/dist/styles/data-display-layout.css +16 -0
- package/dist/styles/layout.css +16 -0
- package/dist/styles/navigation-layout.css +8 -3
- package/dist/styles/shell-layout.css +21 -1
- package/dist/styles/table-layout.css +53 -0
- package/dist/tokens/components/card.css +12 -0
- package/dist/tokens/components/control.css +16 -0
- package/dist/tokens/components/data-display.css +12 -0
- package/dist/tokens/components/email.css +8 -2
- package/dist/tokens/components/feedback.css +24 -0
- package/dist/tokens/components/navigation.css +9 -0
- package/dist/tokens/components/shell.css +27 -4
- package/dist/tokens/components/table.css +21 -0
- package/dist/tokens/semantic/layout.css +8 -0
- package/package.json +2 -2
|
@@ -51,9 +51,16 @@
|
|
|
51
51
|
background: transparent;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
/* `featured` = the brand-toned perimeter. Its colour is a role-mirror knob (rule #45): declared
|
|
55
|
+
* `initial` in the token layer so the --primary default resolves HERE, at the call site, and a
|
|
56
|
+
* scoped [data-tenant]/.dark override of --primary still reaches it. A service that wants the
|
|
57
|
+
* featured ring in a non-brand tone overrides --card-featured-border-color; a card that wants a
|
|
58
|
+
* SEMANTIC perimeter (attention / destructive / success …) uses
|
|
59
|
+
* `accent="…" accentPlacement="perimeter"` instead — same geometry, tone-owned. */
|
|
54
60
|
[data-slot="card"][data-variant="featured"] {
|
|
55
|
-
border-color: hsl(var(--primary));
|
|
56
|
-
box-shadow: 0 0 0
|
|
61
|
+
border-color: var(--card-featured-border-color, hsl(var(--primary)));
|
|
62
|
+
box-shadow: 0 0 0 var(--card-featured-ring-width)
|
|
63
|
+
var(--card-featured-border-color, hsl(var(--primary)));
|
|
57
64
|
}
|
|
58
65
|
|
|
59
66
|
/* ── Density — override the shell inset/body tokens ──────────────────────
|
|
@@ -72,33 +79,39 @@
|
|
|
72
79
|
--card-space-body-y: var(--space-5);
|
|
73
80
|
}
|
|
74
81
|
|
|
75
|
-
/* ── Accent
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
82
|
+
/* ── Accent tone — ONE resolved colour per semantic accent ─────────────
|
|
83
|
+
* `--card-accent-color` is RESOLVED from the card's own `data-accent`, so both placements read
|
|
84
|
+
* the same value and a new tone is one line. It is deliberately NOT a service knob: it is set on
|
|
85
|
+
* the element, so a :root / [data-tenant] override could never reach it. Retint the ROLE
|
|
86
|
+
* (--attention, --success, …) — that is what a role is for. */
|
|
80
87
|
[data-slot="card"][data-accent="primary"] {
|
|
81
|
-
|
|
88
|
+
--card-accent-color: hsl(var(--primary));
|
|
82
89
|
}
|
|
83
90
|
|
|
84
91
|
[data-slot="card"][data-accent="success"] {
|
|
85
|
-
|
|
92
|
+
--card-accent-color: hsl(var(--success));
|
|
86
93
|
}
|
|
87
94
|
|
|
88
95
|
[data-slot="card"][data-accent="warning"] {
|
|
89
|
-
|
|
96
|
+
--card-accent-color: hsl(var(--warning));
|
|
90
97
|
}
|
|
91
98
|
|
|
92
99
|
[data-slot="card"][data-accent="info"] {
|
|
93
|
-
|
|
100
|
+
--card-accent-color: hsl(var(--info));
|
|
94
101
|
}
|
|
95
102
|
|
|
96
103
|
[data-slot="card"][data-accent="attention"] {
|
|
97
|
-
|
|
104
|
+
--card-accent-color: hsl(var(--attention));
|
|
98
105
|
}
|
|
99
106
|
|
|
100
107
|
[data-slot="card"][data-accent="destructive"] {
|
|
101
|
-
|
|
108
|
+
--card-accent-color: hsl(var(--destructive));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* ── Accent placement `edge` (DEFAULT) — semantic left stripe (token width), content on shell ─ */
|
|
112
|
+
[data-slot="card"][data-accent] {
|
|
113
|
+
border-inline-start-width: var(--card-accent-rail-width);
|
|
114
|
+
border-inline-start-color: var(--card-accent-color);
|
|
102
115
|
}
|
|
103
116
|
|
|
104
117
|
[data-slot="card"][data-accent] > [data-slot="card-header"],
|
|
@@ -107,6 +120,29 @@
|
|
|
107
120
|
padding-inline-start: calc(var(--card-space-inset) - var(--card-accent-rail-width));
|
|
108
121
|
}
|
|
109
122
|
|
|
123
|
+
/* ── Accent placement `perimeter` — the full attention border (gh#12) ───
|
|
124
|
+
* The whole edge carries the semantic tone instead of one rail, so a card can read as
|
|
125
|
+
* "action required" / "failed" at a glance without borrowing `--primary` the way
|
|
126
|
+
* `variant="featured"` does. Same optical weight as `featured` (border + 1px ring), tone-owned.
|
|
127
|
+
* Because the rail is gone, the slot padding compensation above is undone — content returns to
|
|
128
|
+
* the normal shell column, so switching placement never shifts the text. */
|
|
129
|
+
[data-slot="card"][data-accent][data-accent-placement="perimeter"] {
|
|
130
|
+
border-width: var(--card-accent-perimeter-width);
|
|
131
|
+
border-color: var(--card-accent-color);
|
|
132
|
+
box-shadow:
|
|
133
|
+
0 0 0 var(--card-accent-perimeter-ring-width) var(--card-accent-color),
|
|
134
|
+
var(--card-shadow),
|
|
135
|
+
var(--card-glow);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
[data-slot="card"][data-accent][data-accent-placement="perimeter"] > [data-slot="card-header"],
|
|
139
|
+
[data-slot="card"][data-accent][data-accent-placement="perimeter"]
|
|
140
|
+
> [data-slot="card-content"]:not([data-flush]),
|
|
141
|
+
[data-slot="card"][data-accent][data-accent-placement="perimeter"]
|
|
142
|
+
> [data-slot="card-footer"]:not([data-flush]) {
|
|
143
|
+
padding-inline-start: var(--card-space-inset);
|
|
144
|
+
}
|
|
145
|
+
|
|
110
146
|
/* ── Header ──────────────────────────────────────────────────────────── */
|
|
111
147
|
[data-slot="card-header"] {
|
|
112
148
|
display: flex;
|
package/dist/styles/control.css
CHANGED
|
@@ -714,8 +714,18 @@
|
|
|
714
714
|
.ui-otp-container {
|
|
715
715
|
display: flex;
|
|
716
716
|
align-items: center;
|
|
717
|
+
justify-content: var(--otp-container-align, flex-start);
|
|
717
718
|
gap: var(--space-2);
|
|
718
719
|
}
|
|
720
|
+
/* `align` on InputOTP lands on the hidden <input> (input-otp owns the container element), so the
|
|
721
|
+
* container reads it through :has() — the same mechanism this file already uses for the invalid
|
|
722
|
+
* and disabled states. Specificity puts the per-instance prop above the theme token. */
|
|
723
|
+
.ui-otp-container:has(.ui-otp-input[data-align="center"]) {
|
|
724
|
+
justify-content: center;
|
|
725
|
+
}
|
|
726
|
+
.ui-otp-container:has(.ui-otp-input[data-align="end"]) {
|
|
727
|
+
justify-content: flex-end;
|
|
728
|
+
}
|
|
719
729
|
.ui-otp-input:disabled {
|
|
720
730
|
cursor: not-allowed;
|
|
721
731
|
}
|
|
@@ -726,8 +736,8 @@
|
|
|
726
736
|
.ui-otp-slot {
|
|
727
737
|
position: relative;
|
|
728
738
|
display: flex;
|
|
729
|
-
width: var(--otp-slot-size, var(--control-height));
|
|
730
|
-
height: var(--otp-slot-size, var(--control-height));
|
|
739
|
+
width: var(--otp-slot-inline-size, var(--otp-slot-size, var(--control-height)));
|
|
740
|
+
height: var(--otp-slot-block-size, var(--otp-slot-size, var(--control-height)));
|
|
731
741
|
align-items: center;
|
|
732
742
|
justify-content: center;
|
|
733
743
|
font-size: var(--font-size-base);
|
|
@@ -817,6 +827,11 @@
|
|
|
817
827
|
display: inline-flex;
|
|
818
828
|
align-items: center;
|
|
819
829
|
gap: var(--space-1);
|
|
830
|
+
/* A 10-star scale needs ~276px of hit area; a 320px viewport offers ~212 inside a card. Without
|
|
831
|
+
* wrapping, the last stars are painted outside the surface with nothing to scroll them into
|
|
832
|
+
* view, so they can be neither seen nor clicked (WCAG 2.1.1 / 2.4.7). Wrapping keeps every star
|
|
833
|
+
* reachable and is a no-op wherever the row already fits. */
|
|
834
|
+
flex-wrap: wrap;
|
|
820
835
|
}
|
|
821
836
|
.ui-rating-star {
|
|
822
837
|
/* ≥24px hit area (WCAG 2.5.8) around the 20px glyph */
|
|
@@ -30,6 +30,22 @@
|
|
|
30
30
|
color: var(--avatar-square-foreground, hsl(var(--primary-foreground)));
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/* CAPABILITY MEDALLION — `<Avatar appearance="tinted">` (gh#12). The tinted companion of the
|
|
34
|
+
* solid entity mark: same box, same radius, a soft role wash instead of a solid fill and the
|
|
35
|
+
* glyph in the role itself. Declared AFTER the square rule (equal specificity) so it retints
|
|
36
|
+
* either shape. The glyph rule is scoped to this appearance ON PURPOSE — a global
|
|
37
|
+
* `.ui-avatar svg` would outrank the per-call-site icon classes existing avatars already use. */
|
|
38
|
+
.ui-avatar[data-appearance="tinted"] {
|
|
39
|
+
--avatar-background: var(--avatar-tinted-background, hsl(var(--primary) / 0.1));
|
|
40
|
+
color: var(--avatar-tinted-foreground, hsl(var(--primary)));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.ui-avatar[data-appearance="tinted"] svg {
|
|
44
|
+
inline-size: var(--avatar-tinted-glyph-size);
|
|
45
|
+
block-size: var(--avatar-tinted-glyph-size);
|
|
46
|
+
flex: none;
|
|
47
|
+
}
|
|
48
|
+
|
|
33
49
|
.ui-avatar-image {
|
|
34
50
|
width: 100%;
|
|
35
51
|
height: 100%;
|
package/dist/styles/layout.css
CHANGED
|
@@ -626,6 +626,22 @@
|
|
|
626
626
|
margin-top: var(--space-1);
|
|
627
627
|
}
|
|
628
628
|
|
|
629
|
+
/* `headerLoading` placeholders — the real `<h1>`/`<p>` wearing the shared `.ui-skeleton-block`
|
|
630
|
+
* skin, so the pending band occupies the same rhythm the settled band will. `max-inline-size:
|
|
631
|
+
* 100%` keeps them inside a 390px column; the block size replaces the line box the (sr-only)
|
|
632
|
+
* text no longer fills. */
|
|
633
|
+
.ui-page-title-placeholder {
|
|
634
|
+
inline-size: var(--page-title-placeholder-measure);
|
|
635
|
+
max-inline-size: 100%;
|
|
636
|
+
min-block-size: var(--page-title-placeholder-block-size);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
.ui-page-subtitle-placeholder {
|
|
640
|
+
inline-size: var(--page-subtitle-placeholder-measure);
|
|
641
|
+
max-inline-size: 100%;
|
|
642
|
+
min-block-size: var(--page-subtitle-placeholder-block-size);
|
|
643
|
+
}
|
|
644
|
+
|
|
629
645
|
/* Default: content-height + top-packed. The container's min-height:100% keeps
|
|
630
646
|
* the page background spanning the viewport-tall shell, but the body itself is
|
|
631
647
|
* NOT stretched — so short pages no longer leave a jarring void below their
|
|
@@ -394,10 +394,15 @@
|
|
|
394
394
|
box-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--focus-ring-color, var(--ring)));
|
|
395
395
|
}
|
|
396
396
|
|
|
397
|
+
/* The step NUMBER carries the emphasis. Both knobs exist because the canonical hosted-identity
|
|
398
|
+
* row marks the step with an accent TINT at normal weight, while the original marked it with
|
|
399
|
+
* bold at the inherited colour — a service picks its own emphasis instead of forking this rule
|
|
400
|
+
* (rule #45). Defaults are the original bold + inherited colour, so nothing moves. */
|
|
397
401
|
.ui-steps-inline-index {
|
|
398
402
|
flex: none;
|
|
399
403
|
font-variant-numeric: tabular-nums;
|
|
400
|
-
font-weight: var(--font-weight
|
|
404
|
+
font-weight: var(--steps-inline-index-font-weight);
|
|
405
|
+
color: var(--steps-inline-index-color, currentColor);
|
|
401
406
|
}
|
|
402
407
|
|
|
403
408
|
.ui-steps-inline-title {
|
|
@@ -418,8 +423,7 @@
|
|
|
418
423
|
color: hsl(var(--destructive));
|
|
419
424
|
}
|
|
420
425
|
|
|
421
|
-
.ui-steps-inline-item[data-status="wait"] .ui-steps-inline-control
|
|
422
|
-
.ui-steps-inline-separator {
|
|
426
|
+
.ui-steps-inline-item[data-status="wait"] .ui-steps-inline-control {
|
|
423
427
|
color: hsl(var(--muted-foreground));
|
|
424
428
|
}
|
|
425
429
|
|
|
@@ -427,5 +431,6 @@
|
|
|
427
431
|
width: var(--steps-inline-separator-size);
|
|
428
432
|
height: var(--steps-inline-separator-size);
|
|
429
433
|
flex: none;
|
|
434
|
+
color: var(--steps-inline-separator-color, hsl(var(--muted-foreground)));
|
|
430
435
|
}
|
|
431
436
|
}
|
|
@@ -77,6 +77,17 @@
|
|
|
77
77
|
background-color: var(--app-shell-main-background);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
/* `.app-main` carries tabindex="0" (axe scrollable-region-focusable, dbad118), so keyboard
|
|
81
|
+
* focus can land on the region — without a rule here the USER-AGENT default outline (blue
|
|
82
|
+
* in Chrome) wraps the whole content area. Use the design-system ring instead; INSET because
|
|
83
|
+
* `.app-main` is the shell's clip boundary (`contain: paint`) and an outset shadow would be
|
|
84
|
+
* clipped by the grid. Never remove the indicator outright — the region is focusable and
|
|
85
|
+
* must show focus (WCAG 2.4.7, gh#271). */
|
|
86
|
+
.app-main:focus-visible {
|
|
87
|
+
outline: none;
|
|
88
|
+
box-shadow: inset 0 0 0 var(--focus-ring-width) hsl(var(--focus-ring-color, var(--ring)));
|
|
89
|
+
}
|
|
90
|
+
|
|
80
91
|
.app-footer {
|
|
81
92
|
grid-area: footer;
|
|
82
93
|
border-top: 1px solid hsl(var(--border));
|
|
@@ -192,9 +203,14 @@
|
|
|
192
203
|
text-align: center;
|
|
193
204
|
}
|
|
194
205
|
|
|
206
|
+
/* Device-authorization also owns its CODE FIELD (gh#12): the preset's subject is the code, so
|
|
207
|
+
* the slot box comes from the preset instead of the generic square control tier. Scoped on the
|
|
208
|
+
* shell so every OTP inside the device flow inherits it; a per-instance override still wins. */
|
|
195
209
|
.ui-auth-shell[data-preset="device-authorization"] {
|
|
196
210
|
--auth-shell-card-max-width: var(--auth-shell-device-card-max-width);
|
|
197
211
|
--auth-shell-main-padding: var(--auth-shell-device-main-padding);
|
|
212
|
+
--otp-slot-inline-size: var(--auth-shell-device-otp-slot-inline-size);
|
|
213
|
+
--otp-slot-block-size: var(--auth-shell-device-otp-slot-block-size);
|
|
198
214
|
}
|
|
199
215
|
|
|
200
216
|
.ui-auth-shell[data-preset="context-selection"] {
|
|
@@ -528,7 +544,11 @@
|
|
|
528
544
|
}
|
|
529
545
|
|
|
530
546
|
/* AppShell-owned mobile drawer trigger — hidden on desktop, shown once the docked sidebar
|
|
531
|
-
* collapses out
|
|
547
|
+
* collapses out at 900px, THE ONE AppShell breakpoint (gh#165, gh#259). NOT Tailwind's `lg`
|
|
548
|
+
* (1024px): this comment said "below lg" for six minors while the shipped rule below and the
|
|
549
|
+
* hamburger's own `max-[900px]:inline-flex` both fired at 900 — see the block comment on the
|
|
550
|
+
* `@media (width <= 56.25rem)` rule for why 900 is the canonical value. The drawer nav itself is
|
|
551
|
+
* a focus-trapped Sheet. */
|
|
532
552
|
.app-mobile-nav-trigger {
|
|
533
553
|
display: none;
|
|
534
554
|
flex: 0 0 auto;
|
|
@@ -169,6 +169,11 @@
|
|
|
169
169
|
padding-inline: var(--space-page-active-x);
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
+
/* Scroll-hint fade — a SCROLL affordance, so it only shows while the region
|
|
173
|
+
* actually overflows and is not scrolled to the inline-end. The component
|
|
174
|
+
* measures the scroll box and stamps `.ui-data-table-has-overflow-end`
|
|
175
|
+
* (gh#267 — the fade used to render unconditionally, washing out the last
|
|
176
|
+
* column of tables that never scroll). */
|
|
172
177
|
.ui-data-table-scroll::after {
|
|
173
178
|
content: "";
|
|
174
179
|
position: absolute;
|
|
@@ -178,14 +183,35 @@
|
|
|
178
183
|
z-index: 20;
|
|
179
184
|
width: 1.75rem;
|
|
180
185
|
pointer-events: none;
|
|
186
|
+
opacity: 0;
|
|
187
|
+
transition: opacity 150ms ease;
|
|
181
188
|
background: linear-gradient(to left, hsl(var(--background) / 0.88), transparent);
|
|
182
189
|
}
|
|
183
190
|
|
|
191
|
+
.ui-data-table-has-overflow-end::after {
|
|
192
|
+
opacity: 1;
|
|
193
|
+
}
|
|
194
|
+
|
|
184
195
|
[data-slot="card-content"][data-flush] .ui-data-table-scroll {
|
|
185
196
|
margin-inline: 0;
|
|
186
197
|
padding-inline: 0;
|
|
187
198
|
}
|
|
188
199
|
|
|
200
|
+
/* Same reset, same reason, for a table sitting straight on a FLUSH page. The bleed above exists
|
|
201
|
+
* to cancel the page gutter so the scroll region reaches the page edges — but
|
|
202
|
+
* `.ui-page-container--flush .ui-page-body` zeroes that gutter, leaving nothing to bleed into.
|
|
203
|
+
* The -16px then escapes the page: nothing clips it, so the whole page gained 16px of
|
|
204
|
+
* horizontal scroll at 320/375/390 (WCAG 2.2 SC 1.4.10 Reflow). Scoped by child combinator so a
|
|
205
|
+
* table inside a Card keeps its own bleed — the card supplies the padding that one compensates. */
|
|
206
|
+
.ui-page-container--flush > .ui-page-body > .ui-data-table-root > .ui-data-table-scroll {
|
|
207
|
+
margin-inline: 0;
|
|
208
|
+
padding-inline: 0;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.ui-page-container--flush > .ui-page-body > .ui-data-table-root > .ui-data-table-scroll::after {
|
|
212
|
+
inset-inline-end: 0;
|
|
213
|
+
}
|
|
214
|
+
|
|
189
215
|
[data-slot="card-content"][data-flush] .ui-data-table-scroll::after {
|
|
190
216
|
inset-inline-end: 0;
|
|
191
217
|
}
|
|
@@ -322,7 +348,18 @@
|
|
|
322
348
|
--table-action-collection-column-width: var(--table-action-collection-actions-width);
|
|
323
349
|
text-align: end;
|
|
324
350
|
}
|
|
351
|
+
}
|
|
325
352
|
|
|
353
|
+
/* The compact tier lives in `godxjp-ui-responsive` — the LAST layer, declared after Tailwind in
|
|
354
|
+
* styles/base.css — not in `@layer components`. `<table>` carries `text-sm`, a Tailwind utility,
|
|
355
|
+
* and `utilities` outranks `components` by LAYER ORDER, so a `font-size:` re-point written here as
|
|
356
|
+
* a component rule can never apply however specific it is: that is exactly why
|
|
357
|
+
* `--table-action-collection-font-size-compact` was dead at every width (gh#412), leaving the
|
|
358
|
+
* narrow frame at 14px where a 5–6 character Japanese label overflows its column measure. The
|
|
359
|
+
* column measures move with it so the whole compact tier is decided in ONE place, and so a
|
|
360
|
+
* consumer utility on the table can't half-collapse it either. See styles/base.css · THE LAYER
|
|
361
|
+
* CONTRACT. */
|
|
362
|
+
@layer godxjp-ui-responsive {
|
|
326
363
|
/* Below the step the SOURCE measures are re-pointed to their compact tier (and the type / row
|
|
327
364
|
* density with them); nothing else moves. One block per canonical step — a media/container
|
|
328
365
|
* query cannot read a `var()`, so the tokenized scale is written out (sm 40rem · md 48rem ·
|
|
@@ -339,6 +376,10 @@
|
|
|
339
376
|
--table-cell-space-x: var(--table-action-collection-cell-space-x-compact);
|
|
340
377
|
--table-cell-padding-y: var(--table-action-collection-cell-padding-y-compact);
|
|
341
378
|
font-size: var(--table-action-collection-font-size-compact);
|
|
379
|
+
/* The legibility floor. Default `0` keeps the table fitted to its container exactly as
|
|
380
|
+
* before; a consumer whose column count exceeds the priority budget raises it and the
|
|
381
|
+
* surrounding scroll region takes the overflow instead of the cells. */
|
|
382
|
+
min-inline-size: var(--table-action-collection-min-inline-size-compact);
|
|
342
383
|
}
|
|
343
384
|
|
|
344
385
|
/* A nowrap child (a Badge pill, a chip) would otherwise bleed into the next column at the
|
|
@@ -375,6 +416,10 @@
|
|
|
375
416
|
--table-cell-space-x: var(--table-action-collection-cell-space-x-compact);
|
|
376
417
|
--table-cell-padding-y: var(--table-action-collection-cell-padding-y-compact);
|
|
377
418
|
font-size: var(--table-action-collection-font-size-compact);
|
|
419
|
+
/* The legibility floor. Default `0` keeps the table fitted to its container exactly as
|
|
420
|
+
* before; a consumer whose column count exceeds the priority budget raises it and the
|
|
421
|
+
* surrounding scroll region takes the overflow instead of the cells. */
|
|
422
|
+
min-inline-size: var(--table-action-collection-min-inline-size-compact);
|
|
378
423
|
}
|
|
379
424
|
|
|
380
425
|
/* A nowrap child (a Badge pill, a chip) would otherwise bleed into the next column at the
|
|
@@ -411,6 +456,10 @@
|
|
|
411
456
|
--table-cell-space-x: var(--table-action-collection-cell-space-x-compact);
|
|
412
457
|
--table-cell-padding-y: var(--table-action-collection-cell-padding-y-compact);
|
|
413
458
|
font-size: var(--table-action-collection-font-size-compact);
|
|
459
|
+
/* The legibility floor. Default `0` keeps the table fitted to its container exactly as
|
|
460
|
+
* before; a consumer whose column count exceeds the priority budget raises it and the
|
|
461
|
+
* surrounding scroll region takes the overflow instead of the cells. */
|
|
462
|
+
min-inline-size: var(--table-action-collection-min-inline-size-compact);
|
|
414
463
|
}
|
|
415
464
|
|
|
416
465
|
/* A nowrap child (a Badge pill, a chip) would otherwise bleed into the next column at the
|
|
@@ -447,6 +496,10 @@
|
|
|
447
496
|
--table-cell-space-x: var(--table-action-collection-cell-space-x-compact);
|
|
448
497
|
--table-cell-padding-y: var(--table-action-collection-cell-padding-y-compact);
|
|
449
498
|
font-size: var(--table-action-collection-font-size-compact);
|
|
499
|
+
/* The legibility floor. Default `0` keeps the table fitted to its container exactly as
|
|
500
|
+
* before; a consumer whose column count exceeds the priority budget raises it and the
|
|
501
|
+
* surrounding scroll region takes the overflow instead of the cells. */
|
|
502
|
+
min-inline-size: var(--table-action-collection-min-inline-size-compact);
|
|
450
503
|
}
|
|
451
504
|
|
|
452
505
|
/* A nowrap child (a Badge pill, a chip) would otherwise bleed into the next column at the
|
|
@@ -74,6 +74,18 @@
|
|
|
74
74
|
* The slot padding compensation in card-layout.css subtracts the same token,
|
|
75
75
|
* so content stays aligned on the shell whatever the rail width. */
|
|
76
76
|
--card-accent-rail-width: 6px;
|
|
77
|
+
/* Accent placement `perimeter` (gh#12) — the FULL attention border. Two knobs so a service can
|
|
78
|
+
* tune the edge weight and the outer ring independently; the defaults reproduce the optical
|
|
79
|
+
* weight of `variant="featured"` (1px border + 1px ring) in the card's own semantic accent tone
|
|
80
|
+
* instead of --primary. Structural 1px literals, like the base card hairline. */
|
|
81
|
+
--card-accent-perimeter-width: 1px;
|
|
82
|
+
--card-accent-perimeter-ring-width: 1px;
|
|
83
|
+
/* `variant="featured"` edge — role-mirror knob (docs/TOKENS.md). `initial` so the --primary
|
|
84
|
+
* default resolves at the CALL SITE and a scoped [data-tenant]/.dark override of --primary
|
|
85
|
+
* reaches it; a service points it anywhere (e.g. var(--attention)) to retint every featured card
|
|
86
|
+
* at once. Default = hsl(var(--primary)). */
|
|
87
|
+
--card-featured-border-color: initial;
|
|
88
|
+
--card-featured-ring-width: 1px;
|
|
77
89
|
--stat-card-label-font-size: var(--font-size-xs);
|
|
78
90
|
--stat-card-label-font-weight: var(--font-weight-medium);
|
|
79
91
|
--stat-card-label-letter-spacing: 0.04em;
|
|
@@ -67,6 +67,22 @@
|
|
|
67
67
|
* re-scopes --control-height to 36px, would silently shrink. Verified in Chromium: 36px before
|
|
68
68
|
* and after. A service opts in with a NAMED tier (`var(--control-height-lg)`), never a calc. */
|
|
69
69
|
--otp-slot-size: initial; /* default = var(--control-height) at the call site */
|
|
70
|
+
/* Per-AXIS overrides of the slot box (gh#12). --otp-slot-size stays the square shorthand; these
|
|
71
|
+
* two win over it when set, so a code field can be WIDER-than-tall or TALLER-than-wide without
|
|
72
|
+
* abandoning the token. A canonical device-grant code field is 27.5x52 per slot (a 4-slot
|
|
73
|
+
* `appearance="grouped"` box of 112x54 with its 1px group border) — not expressible with one
|
|
74
|
+
* square knob, which is why grouped OTP measured 146x38 against that artboard. `initial` for the
|
|
75
|
+
* same tier-mirror reason as --otp-slot-size: the whole fallback chain
|
|
76
|
+
* (axis → square → --control-height) must resolve at the CALL SITE so a shell that re-scopes
|
|
77
|
+
* --control-height still reaches an OTP row that sets neither axis. */
|
|
78
|
+
--otp-slot-inline-size: initial; /* default = var(--otp-slot-size, var(--control-height)) */
|
|
79
|
+
--otp-slot-block-size: initial; /* default = var(--otp-slot-size, var(--control-height)) */
|
|
80
|
+
/* Main-axis alignment of the whole OTP row (rule #44/#45 — chrome is a token, default quiet).
|
|
81
|
+
* `flex-start` is the historical behaviour, so an existing field is byte-identical; a centred
|
|
82
|
+
* code field is `align="center"` on InputOTP (which wins over this knob) or this token set once
|
|
83
|
+
* in a service theme. It exists because EVERY consumer was wrapping .ui-otp-container in a
|
|
84
|
+
* flex-centring div to get a centred challenge. */
|
|
85
|
+
--otp-container-align: flex-start;
|
|
70
86
|
|
|
71
87
|
/* Checked/on/active fills — `initial` so the --primary default re-resolves at the call site
|
|
72
88
|
* under a scoped theme (a :root binding to var(--primary) freezes at the :root value and a scoped
|
|
@@ -28,6 +28,18 @@
|
|
|
28
28
|
--avatar-square-background: initial;
|
|
29
29
|
--avatar-square-foreground: initial;
|
|
30
30
|
|
|
31
|
+
/* Capability medallion — `<Avatar appearance="tinted">` (gh#12): the tinted plate a capability /
|
|
32
|
+
feature glyph sits on, as opposed to the SOLID entity mark above. Pair it with shape="square"
|
|
33
|
+
for the canonical rounded square. --avatar-tinted-background / --avatar-tinted-foreground are
|
|
34
|
+
role-mirror knobs (`initial`, docs/TOKENS.md) so the --primary defaults resolve at the CALL
|
|
35
|
+
SITE and a scoped [data-tenant]/.dark override of --primary reaches them — the exact reason
|
|
36
|
+
this is a token and not the `hsl(var(--primary) / 0.1)` literal consumers were writing in page
|
|
37
|
+
CSS. Defaults = hsl(var(--primary) / 0.1) wash · hsl(var(--primary)) glyph ·
|
|
38
|
+
--control-icon-size glyph box. */
|
|
39
|
+
--avatar-tinted-background: initial;
|
|
40
|
+
--avatar-tinted-foreground: initial;
|
|
41
|
+
--avatar-tinted-glyph-size: var(--control-icon-size);
|
|
42
|
+
|
|
31
43
|
/* Progress track + fill — `initial` so the role defaults re-resolve under a scoped theme.
|
|
32
44
|
Track reads --secondary, fill reads --success; a service re-tones once.
|
|
33
45
|
Defaults = hsl(var(--secondary)) track · hsl(var(--success)) fill. */
|
|
@@ -63,8 +63,14 @@
|
|
|
63
63
|
--email-heading-font-weight: 500; /* mirrors --font-weight-medium — an email title is calm, not bold */
|
|
64
64
|
|
|
65
65
|
/* ── Primary CTA — the one action button ────────────────────────────────────────────────── */
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
/* TOUCH TARGET, deliberately DECOUPLED from --control-height-lg (36px). Do not "restore" the
|
|
67
|
+
* mirror: email is a mobile-first, touch-only medium. There is no hover state, no precise
|
|
68
|
+
* pointer, and mail clients do not reliably offer zoom or a focus affordance — so the web's AA
|
|
69
|
+
* floor (SC 2.5.8, 24x24, which 36px clears) is the wrong bar here. 44px is the AAA target size
|
|
70
|
+
* (SC 2.5.5, 44x44) and the Apple HIG 44pt / Material 48dp convention. A service that must keep
|
|
71
|
+
* the old box sets --email-cta-height + --email-cta-line-height in its own theme. */
|
|
72
|
+
--email-cta-height: 44px;
|
|
73
|
+
--email-cta-line-height: 44px; /* equal to the height: bulletproof vertical centring in Outlook */
|
|
68
74
|
--email-cta-padding-x: 16px; /* mirrors --space-4 */
|
|
69
75
|
--email-cta-radius: 6px; /* mirrors the --radius base (0.375rem) */
|
|
70
76
|
--email-cta-font-size: 14px; /* mirrors --font-size-base */
|
|
@@ -21,6 +21,10 @@
|
|
|
21
21
|
--dialog-space-inset: var(--dialog-space-y) var(--dialog-space-x);
|
|
22
22
|
--dialog-space-gap: var(--space-stack-md);
|
|
23
23
|
--dialog-close-space-offset: var(--space-4);
|
|
24
|
+
/* Alert corner radius (gh#268 — rule #45): a full-width Alert often sits in the
|
|
25
|
+
* same page column as a Card (--card-radius); a service aligns them by
|
|
26
|
+
* overriding this once. Default keeps the historical --radius-md. */
|
|
27
|
+
--alert-radius: var(--radius-md);
|
|
24
28
|
--alert-space-inset: var(--space-section-active);
|
|
25
29
|
--alert-space-gap: var(--space-inline-md);
|
|
26
30
|
--alert-inner-space-gap: var(--space-stack-sm);
|
|
@@ -29,6 +33,26 @@
|
|
|
29
33
|
* (a brand's success-bg/-border are often more present than the faint 5%/30% default). */
|
|
30
34
|
--alert-bg-alpha: 0.05;
|
|
31
35
|
--alert-border-alpha: 0.3;
|
|
36
|
+
/* BANNER — the page-level Alert treatment (gh#255). Every constant a service would want to
|
|
37
|
+
* match to its page grid is a knob (rule #45): a banner that must keep the app's rounded card
|
|
38
|
+
* language sets --banner-radius, one that rules its top edge too sets --banner-border-width.
|
|
39
|
+
* The inline inset tracks the live page gutter, so a banner mounted above a PageContainer lines
|
|
40
|
+
* its text up with the page title instead of sitting at a second, unrelated margin. */
|
|
41
|
+
--banner-radius: 0;
|
|
42
|
+
--banner-border-width: 0;
|
|
43
|
+
--banner-border-block-end-width: 1px;
|
|
44
|
+
--banner-space-block: var(--space-3);
|
|
45
|
+
/* Inline inset tracks the page gutter so a banner's text lines up with the page title.
|
|
46
|
+
* `--space-page-active-x` steps down to the compact gutter on `.ui-page-container` only, and
|
|
47
|
+
* custom properties inherit — so a banner rendered INSIDE the container picks the compact value
|
|
48
|
+
* up for free, while the normal case (a banner mounted ABOVE the container, or in AppShell) would
|
|
49
|
+
* keep the desktop gutter and sit 8px out at 390px. The compact step below is what actually keeps
|
|
50
|
+
* the two aligned; retune it rather than hard-coding a mobile inset at the call site. */
|
|
51
|
+
--banner-space-inline: var(--space-page-active-x);
|
|
52
|
+
--banner-space-inline-compact: var(--space-page-compact-x);
|
|
53
|
+
/* Dismiss offset is measured from the banner's own (shorter) block inset, so the ✕ stays
|
|
54
|
+
* optically centred on a one-line strip instead of floating at the inline-alert offset. */
|
|
55
|
+
--banner-dismiss-space-offset: var(--space-2);
|
|
32
56
|
/* Brand glow layer for the raised dialog/sheet panel — invisible no-op at rest (rule #44).
|
|
33
57
|
* Paired AFTER --shadow-lg in the surface box-shadow so a service can wash the overlay with the
|
|
34
58
|
* global glow, e.g. --dialog-content-glow: var(--shadow-glow), with no markup change. */
|
|
@@ -14,6 +14,15 @@
|
|
|
14
14
|
--steps-inline-item-gap: var(--space-1);
|
|
15
15
|
--steps-inline-font-size: var(--font-size-xs);
|
|
16
16
|
--steps-inline-separator-size: var(--control-icon-size-sm);
|
|
17
|
+
/* Inline-step emphasis (gh#12). The step number's weight and tint are separate knobs so a
|
|
18
|
+
* service can express progression the canonical way — an accent TINT at normal weight — instead
|
|
19
|
+
* of the original bold. --steps-inline-index-color / --steps-inline-separator-color are
|
|
20
|
+
* role-mirror knobs (docs/TOKENS.md): `initial` so their defaults (the inherited step colour and
|
|
21
|
+
* the --muted-foreground role) resolve at the CALL SITE and a scoped [data-tenant]/.dark
|
|
22
|
+
* override still reaches them. Defaults reproduce today's row byte for byte. */
|
|
23
|
+
--steps-inline-index-font-weight: var(--font-weight-bold);
|
|
24
|
+
--steps-inline-index-color: initial; /* default = currentColor at the call site */
|
|
25
|
+
--steps-inline-separator-color: initial; /* default = hsl(var(--muted-foreground)) */
|
|
17
26
|
|
|
18
27
|
/* AppSettingPicker `compact` (gh#217) — the small, content-hugging labelled trigger used in an
|
|
19
28
|
* auth/legal footer, where the square icon-only default reads as a stray button and the full
|
|
@@ -81,10 +81,21 @@
|
|
|
81
81
|
--topbar-height: auto;
|
|
82
82
|
--topbar-inset: 0px;
|
|
83
83
|
--topbar-gap: var(--space-2);
|
|
84
|
-
/*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
84
|
+
/* ⚠ THIS KNOB DELETES CONTENT AT 1100px AND BELOW — read before you ship a center slot.
|
|
85
|
+
* At compact desktop/tablet widths the docked sidebar leaves too little inline room for three
|
|
86
|
+
* intrinsically-sized clusters, so the package hides the optional center slot before it can
|
|
87
|
+
* cover the breadcrumb/title or end utilities (gh#244). The default is `none`, which means a
|
|
88
|
+
* global search trigger placed in `center` is INVISIBLE from 1100px down — including on every
|
|
89
|
+
* phone — unless the consumer opts back in. That default arrived in 18.6.0 and removed the slot
|
|
90
|
+
* for consumers who never changed a line of their own code (gh#12); it stays because the
|
|
91
|
+
* overlap it prevents is a real defect and flipping a shipped default twice is worse than
|
|
92
|
+
* documenting it once, but it is a DECISION, not an accident:
|
|
93
|
+
*
|
|
94
|
+
* :root { --topbar-center-compact-display: flex; } ← restore the slot at every width
|
|
95
|
+
*
|
|
96
|
+
* Opt back in only once the center content has a compact presentation of its own (an icon-only
|
|
97
|
+
* search trigger, a collapsing field). A page-local media query is the anti-pattern this knob
|
|
98
|
+
* replaces. */
|
|
88
99
|
--topbar-center-compact-display: none;
|
|
89
100
|
--org-switcher-trigger-height: 2.75rem;
|
|
90
101
|
--org-switcher-trigger-padding-x: var(--space-2);
|
|
@@ -183,6 +194,18 @@
|
|
|
183
194
|
--auth-shell-device-card-max-width: 23.75rem;
|
|
184
195
|
--auth-shell-device-main-padding: 1rem;
|
|
185
196
|
--auth-shell-device-main-padding-mobile: 0.9375rem 0.3125rem;
|
|
197
|
+
/* CODE FIELD measure — the preset owns it (gh#12). A device grant is the one canonical flow whose
|
|
198
|
+
* PRIMARY control is the code field, so leaving it on the generic square --otp-slot-size made the
|
|
199
|
+
* preset own the page but not its own subject: two 4-slot `appearance="grouped"` boxes rendered
|
|
200
|
+
* 146x38 (4 x the canonical 36px control tier + the 1px group border) against a 112x54 artboard.
|
|
201
|
+
* These are the per-slot boxes that ADD UP to that artboard group:
|
|
202
|
+
* inline 27.5px x 4 + 2 x 1px group border = 112px
|
|
203
|
+
* block 52px + 2 x 1px group border = 54px
|
|
204
|
+
* Literal artboard lengths, like every other preset measure here — they must not drift with
|
|
205
|
+
* --scaling. A service that groups its code differently overrides --otp-slot-inline-size /
|
|
206
|
+
* --otp-slot-block-size on its own field; the generic square knob is untouched everywhere else. */
|
|
207
|
+
--auth-shell-device-otp-slot-inline-size: 1.71875rem;
|
|
208
|
+
--auth-shell-device-otp-slot-block-size: 3.25rem;
|
|
186
209
|
|
|
187
210
|
/* Context-selection preset — 25rem card measure on desktop/tablet, edge-to-edge on mobile
|
|
188
211
|
* (0 inline gutter) with a 16px rhythm between the intro, the card and the remember row. */
|
|
@@ -108,4 +108,25 @@
|
|
|
108
108
|
--table-action-collection-cell-space-x-compact: var(--space-2);
|
|
109
109
|
--table-action-collection-cell-padding-y-compact: var(--space-2);
|
|
110
110
|
--table-action-collection-row-height-compact: var(--table-row-height-compact);
|
|
111
|
+
/* Compact-tier LEGIBILITY FLOOR — the measure below which the preset stops fitting the table
|
|
112
|
+
* to its container and lets the scroll region it already owns take over.
|
|
113
|
+
*
|
|
114
|
+
* The percentage budget above is sized for ONE column per priority tier plus one free-text
|
|
115
|
+
* column. A queue that repeats a tier — two `secondary` columns, three `meta` columns, or an
|
|
116
|
+
* unmarked column beside them — asks for more than 100%, and under `table-layout: fixed` the
|
|
117
|
+
* surplus is taken out of the columns rather than out of the table: measured on a seven-column
|
|
118
|
+
* Japanese admin queue at 390, every column landed at 44–59px and CJK labels wrapped at ONE to
|
|
119
|
+
* TWO characters per line (dxs-platform/platform#680). That is a WCAG 2.2 SC 1.4.10 Reflow (AA)
|
|
120
|
+
* failure, and it is the failure mode this floor removes.
|
|
121
|
+
*
|
|
122
|
+
* Scrolling is the correct escape, not a concession: SC 1.4.10 exempts "parts of the content
|
|
123
|
+
* which require two-dimensional layout for usage or meaning", and its own note names data
|
|
124
|
+
* tables as the example. A table that scrolls horizontally inside its card conforms; a table
|
|
125
|
+
* whose cells are one character wide does not.
|
|
126
|
+
*
|
|
127
|
+
* Default `0` so nothing changes for any existing consumer: a queue that fits its priority
|
|
128
|
+
* budget keeps fitting. A consumer whose queue carries more columns than the budget sets this
|
|
129
|
+
* to the measure at which its narrowest column is still readable (roughly 5rem per column for
|
|
130
|
+
* Japanese at the compact type tier) and the table scrolls instead of crushing. */
|
|
131
|
+
--table-action-collection-min-inline-size-compact: 0;
|
|
111
132
|
}
|
|
@@ -89,6 +89,14 @@
|
|
|
89
89
|
* nor the wrapping row. */
|
|
90
90
|
--page-header-status-gap: var(--space-inline-sm);
|
|
91
91
|
|
|
92
|
+
/* PageContainer `headerLoading` placeholders (gh#255). The title/subtitle elements wear the
|
|
93
|
+
* shared `.ui-skeleton-block` skin, so they need only a MEASURE and a block size — a service
|
|
94
|
+
* retunes the pending band's proportions here instead of per page. */
|
|
95
|
+
--page-title-placeholder-measure: 14rem; /* 224px */
|
|
96
|
+
--page-title-placeholder-block-size: 1.5em;
|
|
97
|
+
--page-subtitle-placeholder-measure: 22rem; /* 352px */
|
|
98
|
+
--page-subtitle-placeholder-block-size: 1.25em;
|
|
99
|
+
|
|
92
100
|
/* Bounded page MEASURE (gh#245 / gh#247) — the shared inline cap applied to the page HEADER and
|
|
93
101
|
* BODY together, so the header `extra` action ends on the same edge as the body surface (the
|
|
94
102
|
* `variant="narrow"` complaint: it caps only `.ui-page-body`, leaving the header action out at
|