@godxjp/ui 18.10.0 → 18.11.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.
Files changed (54) hide show
  1. package/dist/components/data-display/card.d.ts +27 -3
  2. package/dist/components/data-display/card.js +4 -1
  3. package/dist/components/data-display/data-table.js +2 -3
  4. package/dist/components/data-entry/command-palette.d.ts +42 -1
  5. package/dist/components/data-entry/command-palette.js +31 -2
  6. package/dist/components/data-entry/input-otp.d.ts +1 -1
  7. package/dist/components/data-entry/select.d.ts +7 -1
  8. package/dist/components/data-entry/select.js +46 -26
  9. package/dist/components/feedback/banner.js +5 -1
  10. package/dist/components/layout/page-container.d.ts +1 -1
  11. package/dist/components/layout/page-container.js +61 -41
  12. package/dist/components/layout/resizable.d.ts +1 -1
  13. package/dist/components/layout/resizable.js +26 -8
  14. package/dist/components/navigation/pagination.js +20 -1
  15. package/dist/components/navigation/steps.d.ts +2 -2
  16. package/dist/components/navigation/steps.js +4 -2
  17. package/dist/components/navigation/tabs.js +6 -1
  18. package/dist/components/ui/avatar.d.ts +1 -1
  19. package/dist/components/ui/avatar.js +2 -1
  20. package/dist/components/ui/input-otp.d.ts +15 -3
  21. package/dist/components/ui/input-otp.js +2 -1
  22. package/dist/email/tokens.generated.d.ts +2 -2
  23. package/dist/email/tokens.generated.js +2 -2
  24. package/dist/i18n/messages/en.json +3 -0
  25. package/dist/i18n/messages/ja.json +3 -0
  26. package/dist/i18n/messages/vi.json +3 -0
  27. package/dist/lib/field-a11y.d.ts +7 -0
  28. package/dist/lib/field-a11y.js +6 -0
  29. package/dist/lib/hooks.d.ts +15 -0
  30. package/dist/lib/hooks.js +37 -0
  31. package/dist/props/components/data-display.prop.d.ts +16 -0
  32. package/dist/props/components/data-entry.prop.d.ts +7 -0
  33. package/dist/props/components/layout.prop.d.ts +8 -0
  34. package/dist/props/components/navigation.prop.d.ts +9 -0
  35. package/dist/props/registry.d.ts +20 -1
  36. package/dist/props/registry.js +21 -1
  37. package/dist/styles/base.css +28 -0
  38. package/dist/styles/card-layout.css +49 -13
  39. package/dist/styles/control.css +17 -2
  40. package/dist/styles/data-display-layout.css +16 -0
  41. package/dist/styles/layout.css +16 -0
  42. package/dist/styles/navigation-layout.css +8 -3
  43. package/dist/styles/shell-layout.css +10 -1
  44. package/dist/styles/table-layout.css +42 -0
  45. package/dist/tokens/components/card.css +12 -0
  46. package/dist/tokens/components/control.css +16 -0
  47. package/dist/tokens/components/data-display.css +12 -0
  48. package/dist/tokens/components/email.css +8 -2
  49. package/dist/tokens/components/feedback.css +20 -0
  50. package/dist/tokens/components/navigation.css +9 -0
  51. package/dist/tokens/components/shell.css +27 -4
  52. package/dist/tokens/components/table.css +21 -0
  53. package/dist/tokens/semantic/layout.css +8 -0
  54. package/package.json +2 -2
@@ -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-bold);
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
  }
@@ -192,9 +192,14 @@
192
192
  text-align: center;
193
193
  }
194
194
 
195
+ /* Device-authorization also owns its CODE FIELD (gh#12): the preset's subject is the code, so
196
+ * the slot box comes from the preset instead of the generic square control tier. Scoped on the
197
+ * shell so every OTP inside the device flow inherits it; a per-instance override still wins. */
195
198
  .ui-auth-shell[data-preset="device-authorization"] {
196
199
  --auth-shell-card-max-width: var(--auth-shell-device-card-max-width);
197
200
  --auth-shell-main-padding: var(--auth-shell-device-main-padding);
201
+ --otp-slot-inline-size: var(--auth-shell-device-otp-slot-inline-size);
202
+ --otp-slot-block-size: var(--auth-shell-device-otp-slot-block-size);
198
203
  }
199
204
 
200
205
  .ui-auth-shell[data-preset="context-selection"] {
@@ -528,7 +533,11 @@
528
533
  }
529
534
 
530
535
  /* AppShell-owned mobile drawer trigger — hidden on desktop, shown once the docked sidebar
531
- * collapses out below `lg` (gh#165). The drawer nav itself is a focus-trapped Sheet. */
536
+ * collapses out at 900px, THE ONE AppShell breakpoint (gh#165, gh#259). NOT Tailwind's `lg`
537
+ * (1024px): this comment said "below lg" for six minors while the shipped rule below and the
538
+ * hamburger's own `max-[900px]:inline-flex` both fired at 900 — see the block comment on the
539
+ * `@media (width <= 56.25rem)` rule for why 900 is the canonical value. The drawer nav itself is
540
+ * a focus-trapped Sheet. */
532
541
  .app-mobile-nav-trigger {
533
542
  display: none;
534
543
  flex: 0 0 auto;
@@ -186,6 +186,21 @@
186
186
  padding-inline: 0;
187
187
  }
188
188
 
189
+ /* Same reset, same reason, for a table sitting straight on a FLUSH page. The bleed above exists
190
+ * to cancel the page gutter so the scroll region reaches the page edges — but
191
+ * `.ui-page-container--flush .ui-page-body` zeroes that gutter, leaving nothing to bleed into.
192
+ * The -16px then escapes the page: nothing clips it, so the whole page gained 16px of
193
+ * horizontal scroll at 320/375/390 (WCAG 2.2 SC 1.4.10 Reflow). Scoped by child combinator so a
194
+ * table inside a Card keeps its own bleed — the card supplies the padding that one compensates. */
195
+ .ui-page-container--flush > .ui-page-body > .ui-data-table-root > .ui-data-table-scroll {
196
+ margin-inline: 0;
197
+ padding-inline: 0;
198
+ }
199
+
200
+ .ui-page-container--flush > .ui-page-body > .ui-data-table-root > .ui-data-table-scroll::after {
201
+ inset-inline-end: 0;
202
+ }
203
+
189
204
  [data-slot="card-content"][data-flush] .ui-data-table-scroll::after {
190
205
  inset-inline-end: 0;
191
206
  }
@@ -322,7 +337,18 @@
322
337
  --table-action-collection-column-width: var(--table-action-collection-actions-width);
323
338
  text-align: end;
324
339
  }
340
+ }
325
341
 
342
+ /* The compact tier lives in `godxjp-ui-responsive` — the LAST layer, declared after Tailwind in
343
+ * styles/base.css — not in `@layer components`. `<table>` carries `text-sm`, a Tailwind utility,
344
+ * and `utilities` outranks `components` by LAYER ORDER, so a `font-size:` re-point written here as
345
+ * a component rule can never apply however specific it is: that is exactly why
346
+ * `--table-action-collection-font-size-compact` was dead at every width (gh#412), leaving the
347
+ * narrow frame at 14px where a 5–6 character Japanese label overflows its column measure. The
348
+ * column measures move with it so the whole compact tier is decided in ONE place, and so a
349
+ * consumer utility on the table can't half-collapse it either. See styles/base.css · THE LAYER
350
+ * CONTRACT. */
351
+ @layer godxjp-ui-responsive {
326
352
  /* Below the step the SOURCE measures are re-pointed to their compact tier (and the type / row
327
353
  * density with them); nothing else moves. One block per canonical step — a media/container
328
354
  * query cannot read a `var()`, so the tokenized scale is written out (sm 40rem · md 48rem ·
@@ -339,6 +365,10 @@
339
365
  --table-cell-space-x: var(--table-action-collection-cell-space-x-compact);
340
366
  --table-cell-padding-y: var(--table-action-collection-cell-padding-y-compact);
341
367
  font-size: var(--table-action-collection-font-size-compact);
368
+ /* The legibility floor. Default `0` keeps the table fitted to its container exactly as
369
+ * before; a consumer whose column count exceeds the priority budget raises it and the
370
+ * surrounding scroll region takes the overflow instead of the cells. */
371
+ min-inline-size: var(--table-action-collection-min-inline-size-compact);
342
372
  }
343
373
 
344
374
  /* A nowrap child (a Badge pill, a chip) would otherwise bleed into the next column at the
@@ -375,6 +405,10 @@
375
405
  --table-cell-space-x: var(--table-action-collection-cell-space-x-compact);
376
406
  --table-cell-padding-y: var(--table-action-collection-cell-padding-y-compact);
377
407
  font-size: var(--table-action-collection-font-size-compact);
408
+ /* The legibility floor. Default `0` keeps the table fitted to its container exactly as
409
+ * before; a consumer whose column count exceeds the priority budget raises it and the
410
+ * surrounding scroll region takes the overflow instead of the cells. */
411
+ min-inline-size: var(--table-action-collection-min-inline-size-compact);
378
412
  }
379
413
 
380
414
  /* A nowrap child (a Badge pill, a chip) would otherwise bleed into the next column at the
@@ -411,6 +445,10 @@
411
445
  --table-cell-space-x: var(--table-action-collection-cell-space-x-compact);
412
446
  --table-cell-padding-y: var(--table-action-collection-cell-padding-y-compact);
413
447
  font-size: var(--table-action-collection-font-size-compact);
448
+ /* The legibility floor. Default `0` keeps the table fitted to its container exactly as
449
+ * before; a consumer whose column count exceeds the priority budget raises it and the
450
+ * surrounding scroll region takes the overflow instead of the cells. */
451
+ min-inline-size: var(--table-action-collection-min-inline-size-compact);
414
452
  }
415
453
 
416
454
  /* A nowrap child (a Badge pill, a chip) would otherwise bleed into the next column at the
@@ -447,6 +485,10 @@
447
485
  --table-cell-space-x: var(--table-action-collection-cell-space-x-compact);
448
486
  --table-cell-padding-y: var(--table-action-collection-cell-padding-y-compact);
449
487
  font-size: var(--table-action-collection-font-size-compact);
488
+ /* The legibility floor. Default `0` keeps the table fitted to its container exactly as
489
+ * before; a consumer whose column count exceeds the priority budget raises it and the
490
+ * surrounding scroll region takes the overflow instead of the cells. */
491
+ min-inline-size: var(--table-action-collection-min-inline-size-compact);
450
492
  }
451
493
 
452
494
  /* 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
- --email-cta-height: 36px; /* mirrors --control-height-lg (2.25rem) the canonical CTA box */
67
- --email-cta-line-height: 36px; /* equal to the height: bulletproof vertical centring in Outlook */
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 */
@@ -29,6 +29,26 @@
29
29
  * (a brand's success-bg/-border are often more present than the faint 5%/30% default). */
30
30
  --alert-bg-alpha: 0.05;
31
31
  --alert-border-alpha: 0.3;
32
+ /* BANNER — the page-level Alert treatment (gh#255). Every constant a service would want to
33
+ * match to its page grid is a knob (rule #45): a banner that must keep the app's rounded card
34
+ * language sets --banner-radius, one that rules its top edge too sets --banner-border-width.
35
+ * The inline inset tracks the live page gutter, so a banner mounted above a PageContainer lines
36
+ * its text up with the page title instead of sitting at a second, unrelated margin. */
37
+ --banner-radius: 0;
38
+ --banner-border-width: 0;
39
+ --banner-border-block-end-width: 1px;
40
+ --banner-space-block: var(--space-3);
41
+ /* Inline inset tracks the page gutter so a banner's text lines up with the page title.
42
+ * `--space-page-active-x` steps down to the compact gutter on `.ui-page-container` only, and
43
+ * custom properties inherit — so a banner rendered INSIDE the container picks the compact value
44
+ * up for free, while the normal case (a banner mounted ABOVE the container, or in AppShell) would
45
+ * keep the desktop gutter and sit 8px out at 390px. The compact step below is what actually keeps
46
+ * the two aligned; retune it rather than hard-coding a mobile inset at the call site. */
47
+ --banner-space-inline: var(--space-page-active-x);
48
+ --banner-space-inline-compact: var(--space-page-compact-x);
49
+ /* Dismiss offset is measured from the banner's own (shorter) block inset, so the ✕ stays
50
+ * optically centred on a one-line strip instead of floating at the inline-alert offset. */
51
+ --banner-dismiss-space-offset: var(--space-2);
32
52
  /* Brand glow layer for the raised dialog/sheet panel — invisible no-op at rest (rule #44).
33
53
  * Paired AFTER --shadow-lg in the surface box-shadow so a service can wash the overlay with the
34
54
  * 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
- /* At compact desktop/tablet widths the docked sidebar leaves too little inline room for three
85
- * intrinsically-sized clusters. The package hides the optional center slot before it can cover
86
- * the breadcrumb/title or end utilities. Consumers may opt back into another display strategy
87
- * (for example `flex`) once their center content has its own compact presentation. */
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
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@godxjp/ui",
3
- "version": "18.10.0",
4
- "godxUiMcp": "18.10.0",
3
+ "version": "18.11.0",
4
+ "godxUiMcp": "18.11.0",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",