@godxjp/ui 18.4.0 → 18.5.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/README.md +16 -1
- package/dist/app/theme-axes.d.ts +11 -3
- package/dist/app/theme-axes.js +2 -1
- package/dist/components/charts/chart-frame.d.ts +17 -3
- package/dist/components/charts/chart-frame.js +7 -1
- package/dist/components/charts/chart-summary.d.ts +21 -0
- package/dist/components/charts/chart-summary.js +32 -5
- package/dist/components/charts/compact-bar-trend.d.ts +29 -0
- package/dist/components/charts/compact-bar-trend.js +74 -0
- package/dist/components/charts/index.d.ts +3 -0
- package/dist/components/charts/index.js +2 -0
- package/dist/components/data-display/badge.d.ts +8 -0
- package/dist/components/data-display/badge.js +7 -1
- package/dist/components/data-display/data-table.d.ts +16 -1
- package/dist/components/data-display/data-table.js +56 -0
- package/dist/components/data-display/index.d.ts +3 -1
- package/dist/components/data-display/index.js +10 -1
- package/dist/components/data-display/list-row.d.ts +22 -4
- package/dist/components/data-display/list-row.js +21 -3
- package/dist/components/data-display/service-launcher-card.d.ts +40 -0
- package/dist/components/data-display/service-launcher-card.js +97 -0
- package/dist/components/data-entry/command-palette.d.ts +37 -0
- package/dist/components/data-entry/command-palette.js +125 -0
- package/dist/components/data-entry/index.d.ts +2 -0
- package/dist/components/data-entry/index.js +2 -0
- package/dist/components/feedback/dialog.d.ts +2 -0
- package/dist/components/feedback/dialog.js +42 -33
- package/dist/components/feedback/index.d.ts +4 -1
- package/dist/components/feedback/index.js +6 -2
- package/dist/components/feedback/sheet.d.ts +26 -4
- package/dist/components/feedback/sheet.js +54 -7
- package/dist/components/feedback/two-factor-setup.d.ts +31 -0
- package/dist/components/feedback/two-factor-setup.js +97 -0
- package/dist/components/general/index.d.ts +1 -1
- package/dist/components/general/logo.d.ts +42 -8
- package/dist/components/general/logo.js +67 -14
- package/dist/components/layout/app-shell.js +21 -5
- package/dist/components/layout/auth-divider.d.ts +7 -0
- package/dist/components/layout/auth-divider.js +21 -0
- package/dist/components/layout/auth-footer.d.ts +5 -0
- package/dist/components/layout/auth-footer.js +24 -0
- package/dist/components/layout/auth-identity.d.ts +5 -0
- package/dist/components/layout/auth-identity.js +17 -0
- package/dist/components/layout/auth-shell.d.ts +8 -1
- package/dist/components/layout/auth-shell.js +25 -6
- package/dist/components/layout/auth-stack.d.ts +4 -0
- package/dist/components/layout/auth-stack.js +8 -0
- package/dist/components/layout/centered-shell.d.ts +5 -2
- package/dist/components/layout/centered-shell.js +2 -1
- package/dist/components/layout/index.d.ts +18 -3
- package/dist/components/layout/index.js +17 -2
- package/dist/components/layout/legal-document-shell.d.ts +34 -0
- package/dist/components/layout/legal-document-shell.js +186 -0
- package/dist/components/layout/master-detail.d.ts +24 -0
- package/dist/components/layout/master-detail.js +48 -0
- package/dist/components/layout/org-switcher.d.ts +9 -0
- package/dist/components/layout/org-switcher.js +218 -0
- package/dist/components/layout/page-container.d.ts +1 -1
- package/dist/components/layout/page-container.js +3 -2
- package/dist/components/layout/sidebar-link.d.ts +43 -0
- package/dist/components/layout/sidebar-link.js +13 -0
- package/dist/components/layout/sidebar.d.ts +42 -5
- package/dist/components/layout/sidebar.js +170 -85
- package/dist/components/navigation/app-setting-picker.js +17 -6
- package/dist/components/navigation/filter-bar.d.ts +12 -3
- package/dist/components/navigation/filter-bar.js +8 -2
- package/dist/components/navigation/index.d.ts +2 -2
- package/dist/components/navigation/index.js +3 -1
- package/dist/components/navigation/tabs-scroll.d.ts +70 -0
- package/dist/components/navigation/tabs-scroll.js +76 -0
- package/dist/components/navigation/tabs.js +35 -20
- package/dist/email/brand-mark.d.ts +92 -0
- package/dist/email/brand-mark.js +83 -0
- package/dist/email/color.d.ts +52 -0
- package/dist/email/color.js +37 -0
- package/dist/email/geometry.d.ts +112 -0
- package/dist/email/geometry.js +89 -0
- package/dist/email/index.d.ts +78 -0
- package/dist/email/index.js +76 -0
- package/dist/email/inline-style.d.ts +25 -0
- package/dist/email/inline-style.js +21 -0
- package/dist/email/tokens.generated.d.ts +147 -0
- package/dist/email/tokens.generated.js +142 -0
- package/dist/i18n/messages/en.json +21 -2
- package/dist/i18n/messages/ja.json +21 -2
- package/dist/i18n/messages/vi.json +21 -2
- package/dist/inertia/index.d.ts +50 -0
- package/dist/inertia/index.js +5 -0
- package/dist/props/components/app.prop.d.ts +11 -1
- package/dist/props/components/charts.prop.d.ts +40 -1
- package/dist/props/components/data-display.prop.d.ts +13 -1
- package/dist/props/components/feedback.prop.d.ts +10 -0
- package/dist/props/components/index.d.ts +2 -2
- package/dist/props/components/layout.prop.d.ts +373 -10
- package/dist/props/components/layout.prop.js +1 -0
- package/dist/props/components/navigation.prop.d.ts +19 -1
- package/dist/props/registry.d.ts +291 -7
- package/dist/props/registry.js +341 -7
- package/dist/props/vocabulary/index.d.ts +1 -1
- package/dist/props/vocabulary/interaction.prop.d.ts +1 -1
- package/dist/props/vocabulary/layout.prop.d.ts +18 -0
- package/dist/styles/base.css +8 -2
- package/dist/styles/card-layout.css +161 -7
- package/dist/styles/chart-layout.css +97 -0
- package/dist/styles/control.css +48 -2
- package/dist/styles/data-display-layout.css +52 -6
- package/dist/styles/dialog-layout.css +105 -4
- package/dist/styles/fonts.css +15 -4
- package/dist/styles/index.css +11 -3
- package/dist/styles/layout.css +367 -1
- package/dist/styles/logo-layout.css +62 -0
- package/dist/styles/navigation-layout.css +43 -0
- package/dist/styles/shell-layout.css +488 -40
- package/dist/theme/dxs.canonical.css +76 -0
- package/dist/tokens/axes.css +39 -0
- package/dist/tokens/base.css +5 -0
- package/dist/tokens/components/card.css +48 -0
- package/dist/tokens/components/chart.css +47 -0
- package/dist/tokens/components/control.css +16 -0
- package/dist/tokens/components/email.css +66 -0
- package/dist/tokens/components/feedback.css +17 -0
- package/dist/tokens/components/legal-document.css +67 -0
- package/dist/tokens/components/list-row.css +23 -2
- package/dist/tokens/components/logo.css +32 -0
- package/dist/tokens/components/navigation.css +13 -0
- package/dist/tokens/components/sheet.css +19 -0
- package/dist/tokens/components/shell.css +138 -0
- package/dist/tokens/components/sidebar.css +33 -0
- package/dist/tokens/foundation.css +5 -2
- package/dist/tokens/semantic/layout.css +44 -2
- package/package.json +16 -5
package/dist/styles/layout.css
CHANGED
|
@@ -195,6 +195,102 @@
|
|
|
195
195
|
min-width: 0;
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
/* MasterDetail owns its tracks AND its collapse threshold — both token-driven (gh#223).
|
|
199
|
+
*
|
|
200
|
+
* WHY FLEX AND NOT `@container (min-width: …)`: a media/container-query CONDITION cannot read a
|
|
201
|
+
* `var()` — it is resolved before custom properties, so a tokenised breakpoint is impossible
|
|
202
|
+
* there (the reason the first cut hard-coded `40rem`, violating rule #45). The flex threshold
|
|
203
|
+
* below ("Holy Albatross") puts the breakpoint in a `calc()`, where a `var()` DOES resolve, so
|
|
204
|
+
* `--master-detail-collapse-below` is a genuine knob a service theme can retune once globally.
|
|
205
|
+
*
|
|
206
|
+
* How it reads: the fluid region's flex-basis is `(threshold - 100%) * 999`, where 100% is the
|
|
207
|
+
* composition's own inline size (so, like the old container query, the decision comes from the
|
|
208
|
+
* space available to the layout — never the viewport).
|
|
209
|
+
* · width < threshold → a huge positive basis → each region takes a whole flex line and
|
|
210
|
+
* shrinks to it: master stacked above detail, both full width.
|
|
211
|
+
* · width ≥ threshold → the calc goes negative and is clamped to 0 → one line: the fluid
|
|
212
|
+
* region absorbs the free space while the rail keeps its token width (`max()` keeps the
|
|
213
|
+
* rail's basis at the preset because a negative operand can never win a max).
|
|
214
|
+
*
|
|
215
|
+
* `data-rail` picks WHICH region is the fixed rail. Default `detail` = the canonical
|
|
216
|
+
* "fluid list + fixed detail rail" (1fr / 320px) composition; `master` puts the fixed rail on
|
|
217
|
+
* the leading edge for a category/navigator list. DOM order is always master → detail, so the
|
|
218
|
+
* stacked order is list-then-detail either way, and the flex row follows the writing direction
|
|
219
|
+
* (RTL-safe without a single physical property). */
|
|
220
|
+
.ui-master-detail {
|
|
221
|
+
display: flex;
|
|
222
|
+
flex-wrap: wrap;
|
|
223
|
+
gap: var(--master-detail-gap);
|
|
224
|
+
--master-detail-rail-size: var(--master-detail-rail-standard);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.ui-master-detail[data-rail-width="compact"] {
|
|
228
|
+
--master-detail-rail-size: var(--master-detail-rail-compact);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/* Per-instance override of the token threshold (theme sets it globally, prop overrides here). */
|
|
232
|
+
.ui-master-detail[data-collapse-below="false"] {
|
|
233
|
+
--master-detail-collapse-below: 0px;
|
|
234
|
+
}
|
|
235
|
+
.ui-master-detail[data-collapse-below="sm"] {
|
|
236
|
+
--master-detail-collapse-below: 40rem;
|
|
237
|
+
}
|
|
238
|
+
.ui-master-detail[data-collapse-below="md"] {
|
|
239
|
+
--master-detail-collapse-below: 48rem;
|
|
240
|
+
}
|
|
241
|
+
.ui-master-detail[data-collapse-below="lg"] {
|
|
242
|
+
--master-detail-collapse-below: 64rem;
|
|
243
|
+
}
|
|
244
|
+
.ui-master-detail[data-collapse-below="xl"] {
|
|
245
|
+
--master-detail-collapse-below: 80rem;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.ui-master-detail-master,
|
|
249
|
+
.ui-master-detail-detail {
|
|
250
|
+
min-inline-size: 0;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/* The fluid region. */
|
|
254
|
+
.ui-master-detail[data-rail="detail"] > .ui-master-detail-master,
|
|
255
|
+
.ui-master-detail[data-rail="master"] > .ui-master-detail-detail {
|
|
256
|
+
flex: 1 1 calc((var(--master-detail-collapse-below) - 100%) * 999);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/* The fixed rail. */
|
|
260
|
+
.ui-master-detail[data-rail="detail"] > .ui-master-detail-detail,
|
|
261
|
+
.ui-master-detail[data-rail="master"] > .ui-master-detail-master {
|
|
262
|
+
flex: 0 1
|
|
263
|
+
max(var(--master-detail-rail-size), calc((var(--master-detail-collapse-below) - 100%) * 999));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/* Bounded master viewport (gh#231) — OPT-IN via `masterViewport`. `auto` (the default) emits no
|
|
267
|
+
* bound at all, so an existing composition renders byte-identically; the two presets cap the
|
|
268
|
+
* collection's BLOCK size with a token and scroll it INSIDE the region, which is what keeps a
|
|
269
|
+
* long real collection from pushing the detail thousands of pixels below the fold once the
|
|
270
|
+
* layout stacks. Block axis only — the inline axis stays owned by the flex threshold above, so
|
|
271
|
+
* the bound is writing-direction agnostic (RTL-safe).
|
|
272
|
+
*
|
|
273
|
+
* A11y: the region carries tabIndex={0} in this mode (see master-detail.tsx) so the scroll
|
|
274
|
+
* container is reachable and scrollable by keyboard alone, and the inset below reserves room
|
|
275
|
+
* for a focused row's focus ring — an overflow container clips BOTH axes, so a ring drawn at
|
|
276
|
+
* the region's edge would otherwise be cut. Focus is never trapped: the region is a plain tab
|
|
277
|
+
* stop that Tab/Shift+Tab leave normally. */
|
|
278
|
+
.ui-master-detail[data-master-viewport="compact"] > .ui-master-detail-master,
|
|
279
|
+
.ui-master-detail[data-master-viewport="standard"] > .ui-master-detail-master {
|
|
280
|
+
overflow-y: auto;
|
|
281
|
+
overscroll-behavior: contain;
|
|
282
|
+
padding: var(--master-detail-master-viewport-inset);
|
|
283
|
+
scroll-padding-block: var(--master-detail-master-viewport-inset);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.ui-master-detail[data-master-viewport="compact"] > .ui-master-detail-master {
|
|
287
|
+
max-block-size: var(--master-detail-master-viewport-compact);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.ui-master-detail[data-master-viewport="standard"] > .ui-master-detail-master {
|
|
291
|
+
max-block-size: var(--master-detail-master-viewport-standard);
|
|
292
|
+
}
|
|
293
|
+
|
|
198
294
|
@container responsive-grid (min-width: 40rem) {
|
|
199
295
|
.ui-responsive-grid {
|
|
200
296
|
grid-template-columns: repeat(var(--responsive-grid-sm, 2), minmax(0, 1fr));
|
|
@@ -435,6 +531,34 @@
|
|
|
435
531
|
}
|
|
436
532
|
}
|
|
437
533
|
|
|
534
|
+
/* `headerLayout="responsive-inline"` (gh#231): below the 640px step the DEFAULT arrangement
|
|
535
|
+
* drops `extra` onto its own full-width line under the subtitle, which pushes a compact header
|
|
536
|
+
* control (a member search, one primary action) off the title row on a 390px screen. This
|
|
537
|
+
* arrangement keeps it beside the title band at a token-owned measure instead, and lets the
|
|
538
|
+
* title/subtitle wrap into the space that is left. Scoped to the compact range only, so at
|
|
539
|
+
* >=640px both arrangements resolve to the identical row below — and a page that never sets
|
|
540
|
+
* `headerLayout` never matches these rules at all. */
|
|
541
|
+
@media (max-width: 639.98px) {
|
|
542
|
+
.ui-page-header[data-layout="responsive-inline"] .ui-page-header-row {
|
|
543
|
+
flex-direction: row;
|
|
544
|
+
align-items: flex-start;
|
|
545
|
+
justify-content: space-between;
|
|
546
|
+
gap: var(--space-inline-md);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
.ui-page-header[data-layout="responsive-inline"] .ui-page-header-heading {
|
|
550
|
+
flex: 1 1 auto;
|
|
551
|
+
min-inline-size: 0;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
.ui-page-header[data-layout="responsive-inline"] .ui-page-header-extra {
|
|
555
|
+
flex: 0 0 var(--page-header-extra-measure);
|
|
556
|
+
inline-size: var(--page-header-extra-measure);
|
|
557
|
+
max-inline-size: 100%;
|
|
558
|
+
justify-content: flex-end;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
438
562
|
@media (min-width: 640px) {
|
|
439
563
|
.ui-page-header-row {
|
|
440
564
|
flex-direction: row;
|
|
@@ -550,7 +674,8 @@
|
|
|
550
674
|
.ui-empty-state-description {
|
|
551
675
|
font-size: var(--font-size-base);
|
|
552
676
|
color: hsl(var(--muted-foreground));
|
|
553
|
-
|
|
677
|
+
/* Measure knob (rule #45) — default 28rem, retunable per service/locale. */
|
|
678
|
+
max-width: var(--empty-state-description-max-width);
|
|
554
679
|
}
|
|
555
680
|
|
|
556
681
|
.ui-empty-state-icon {
|
|
@@ -755,4 +880,245 @@
|
|
|
755
880
|
color: hsl(var(--muted-foreground));
|
|
756
881
|
font-size: var(--filter-label-font-size);
|
|
757
882
|
}
|
|
883
|
+
|
|
884
|
+
/* Toolbar/FilterBar `overflow="scroll"` (#216) — one bounded row that scrolls INLINE instead of
|
|
885
|
+
* wrapping onto extra rows. Below 640px the strip still stacks (the `wrap` base rules above own
|
|
886
|
+
* that): a 390px viewport must never hide a filter behind a horizontal scroll it cannot see.
|
|
887
|
+
* `overflow-x` matches the rest of the package's scroll regions (.ui-data-table-scroll, tabs
|
|
888
|
+
* list) and the browser already mirrors its scroll origin under `dir="rtl"`; the INSET/margin
|
|
889
|
+
* properties below stay logical. No `tabindex` is needed — every group holds a focusable
|
|
890
|
+
* control, so the region is keyboard-reachable (axe scrollable-region-focusable / WCAG 2.1.1). */
|
|
891
|
+
@media (min-width: 640px) {
|
|
892
|
+
.ui-toolbar[data-overflow="scroll"] {
|
|
893
|
+
flex-wrap: nowrap;
|
|
894
|
+
overflow-x: auto;
|
|
895
|
+
overscroll-behavior-x: contain;
|
|
896
|
+
scrollbar-gutter: stable;
|
|
897
|
+
padding-block-end: var(--filter-bar-scroll-padding-y);
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
.ui-toolbar[data-overflow="scroll"] > .ui-toolbar-group {
|
|
901
|
+
flex: 0 0 auto;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
/* The clear action stays parked at the inline end of the VIEWPORT of the scroll region, not at
|
|
905
|
+
* the end of its scroll width — `margin-inline-start: auto` would push it past the fold and
|
|
906
|
+
* make "clear all" unreachable without scrolling. */
|
|
907
|
+
.ui-toolbar[data-overflow="scroll"] > .ui-toolbar-clear {
|
|
908
|
+
position: sticky;
|
|
909
|
+
inset-inline-end: 0;
|
|
910
|
+
margin-inline-start: auto;
|
|
911
|
+
flex: 0 0 auto;
|
|
912
|
+
/* Above the scrolling groups, and opaque, so a filter never slides underneath it. */
|
|
913
|
+
z-index: 1;
|
|
914
|
+
background: hsl(var(--filter-bar-sticky-background, var(--background)));
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
/* ── LegalDocumentShell (#222) ─────────────────────────────────────────────────
|
|
919
|
+
* The long-form legal/policy document surface: a readable, measure-capped document
|
|
920
|
+
* column with a STICKY table-of-contents rail beside it. Every value reads a
|
|
921
|
+
* `--legal-document-*` component token, so a service re-tunes the measure, the rail
|
|
922
|
+
* and the section rhythm from theme.css with zero consumer `.legal-*` CSS.
|
|
923
|
+
*
|
|
924
|
+
* It OWNS its query container (`container: legal-document / inline-size`, the SplitPane
|
|
925
|
+
* precedent — gh#165), so the one-column ⇄ two-column decision comes from the SHELL's
|
|
926
|
+
* own available width, not the viewport: dropped into a narrow pane on a 1440px screen
|
|
927
|
+
* it correctly stays single-column.
|
|
928
|
+
*
|
|
929
|
+
* DOM order is header → rail (contents) → sections → footer, which is exactly the
|
|
930
|
+
* mobile reading order; the desktop branch only re-places those boxes into a grid.
|
|
931
|
+
*/
|
|
932
|
+
.ui-legal-document-scope {
|
|
933
|
+
container: legal-document / inline-size;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
.ui-legal-document {
|
|
937
|
+
display: grid;
|
|
938
|
+
grid-template-columns: minmax(0, 1fr);
|
|
939
|
+
row-gap: var(--legal-document-section-gap);
|
|
940
|
+
align-items: start;
|
|
941
|
+
min-width: 0;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
.ui-legal-document-header {
|
|
945
|
+
display: flex;
|
|
946
|
+
flex-direction: column;
|
|
947
|
+
gap: var(--legal-document-header-gap);
|
|
948
|
+
min-width: 0;
|
|
949
|
+
max-width: var(--legal-document-measure-max-width);
|
|
950
|
+
border-block-end: var(--legal-document-header-border);
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
.ui-legal-document-meta {
|
|
954
|
+
display: flex;
|
|
955
|
+
flex-wrap: wrap;
|
|
956
|
+
align-items: baseline;
|
|
957
|
+
gap: var(--legal-document-meta-gap);
|
|
958
|
+
font-size: var(--legal-document-meta-font-size);
|
|
959
|
+
color: hsl(var(--legal-document-meta-foreground, var(--muted-foreground)));
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
/* Long-form prose wrapping that holds in JA (kinsoku via `line-break: strict`), in EN
|
|
963
|
+
* (long compounds / URLs via `overflow-wrap: break-word`) and in VI (diacritic-heavy
|
|
964
|
+
* words must NOT be broken mid-word — `word-break: normal` keeps that true). */
|
|
965
|
+
.ui-legal-document-summary,
|
|
966
|
+
.ui-legal-document-section-body {
|
|
967
|
+
min-width: 0;
|
|
968
|
+
line-height: var(--legal-document-body-line-height);
|
|
969
|
+
overflow-wrap: break-word;
|
|
970
|
+
word-break: normal;
|
|
971
|
+
line-break: strict;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
.ui-legal-document-summary {
|
|
975
|
+
color: hsl(var(--legal-document-summary-foreground, var(--muted-foreground)));
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
.ui-legal-document-rail {
|
|
979
|
+
display: flex;
|
|
980
|
+
flex-direction: column;
|
|
981
|
+
gap: var(--legal-document-nav-gap);
|
|
982
|
+
min-width: 0;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
.ui-legal-document-toc {
|
|
986
|
+
display: flex;
|
|
987
|
+
flex-direction: column;
|
|
988
|
+
gap: var(--legal-document-toc-gap);
|
|
989
|
+
min-width: 0;
|
|
990
|
+
min-height: 0;
|
|
991
|
+
border-inline-end: var(--legal-document-toc-border);
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
.ui-legal-document-toc-title {
|
|
995
|
+
font-size: var(--legal-document-toc-title-font-size);
|
|
996
|
+
color: hsl(var(--legal-document-toc-foreground, var(--muted-foreground)));
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
.ui-legal-document-toc-list {
|
|
1000
|
+
display: flex;
|
|
1001
|
+
flex-direction: column;
|
|
1002
|
+
gap: var(--legal-document-toc-gap);
|
|
1003
|
+
margin: 0;
|
|
1004
|
+
padding: 0;
|
|
1005
|
+
list-style: none;
|
|
1006
|
+
min-width: 0;
|
|
1007
|
+
min-height: 0;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
/* REAL anchors — `href="#<section id>"` — so the contents list is keyboard-reachable,
|
|
1011
|
+
* middle-clickable and deep-linkable even before JS boots. */
|
|
1012
|
+
.ui-legal-document-toc-link {
|
|
1013
|
+
display: block;
|
|
1014
|
+
padding: var(--legal-document-toc-item-padding);
|
|
1015
|
+
border-radius: var(--legal-document-toc-item-radius);
|
|
1016
|
+
border-inline-start: var(--legal-document-toc-marker-width) solid transparent;
|
|
1017
|
+
font-size: var(--legal-document-toc-font-size);
|
|
1018
|
+
line-height: 1.6;
|
|
1019
|
+
color: hsl(var(--legal-document-toc-foreground, var(--muted-foreground)));
|
|
1020
|
+
text-decoration: none;
|
|
1021
|
+
overflow-wrap: break-word;
|
|
1022
|
+
transition-property: color, background-color, border-color;
|
|
1023
|
+
transition-duration: var(--duration-fast);
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
.ui-legal-document-toc-link:hover {
|
|
1027
|
+
color: hsl(var(--legal-document-toc-active-foreground, var(--foreground)));
|
|
1028
|
+
background: hsl(var(--legal-document-toc-active-background, var(--accent)) / 0.6);
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
.ui-legal-document-toc-link:focus-visible {
|
|
1032
|
+
outline: none;
|
|
1033
|
+
box-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--focus-ring-color, var(--ring)));
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
/* Active entry: colour + a leading marker + a heavier weight, so the state survives a
|
|
1037
|
+
* monochrome / high-contrast rendering (never colour-only, WCAG 1.4.1). The anchor also
|
|
1038
|
+
* carries `aria-current="location"`. */
|
|
1039
|
+
.ui-legal-document-toc-link[data-active] {
|
|
1040
|
+
color: hsl(var(--legal-document-toc-active-foreground, var(--foreground)));
|
|
1041
|
+
background: hsl(var(--legal-document-toc-active-background, var(--accent)));
|
|
1042
|
+
border-inline-start-color: hsl(var(--legal-document-toc-marker-color, var(--primary)));
|
|
1043
|
+
font-weight: var(--font-weight-medium);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
.ui-legal-document-content {
|
|
1047
|
+
display: flex;
|
|
1048
|
+
flex-direction: column;
|
|
1049
|
+
gap: var(--legal-document-section-gap);
|
|
1050
|
+
min-width: 0;
|
|
1051
|
+
max-width: var(--legal-document-measure-max-width);
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
/* `scroll-margin-block-start` (logical, so it survives vertical writing modes) is what
|
|
1055
|
+
* makes a hash jump AND a programmatic `scrollIntoView` land BELOW sticky chrome — no
|
|
1056
|
+
* JS offset arithmetic, no consumer CSS. */
|
|
1057
|
+
.ui-legal-document-section {
|
|
1058
|
+
display: flex;
|
|
1059
|
+
flex-direction: column;
|
|
1060
|
+
gap: var(--legal-document-section-title-gap);
|
|
1061
|
+
min-width: 0;
|
|
1062
|
+
scroll-margin-block-start: var(--legal-document-scroll-offset);
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
/* The section is `tabIndex={-1}` and receives focus when its contents anchor is
|
|
1066
|
+
* activated, so the focus ring must be VISIBLE (WCAG 2.4.7 / 2.4.11). */
|
|
1067
|
+
.ui-legal-document-section:focus-visible {
|
|
1068
|
+
outline: var(--focus-ring-width) solid hsl(var(--focus-ring-color, var(--ring)));
|
|
1069
|
+
outline-offset: var(--space-1);
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
.ui-legal-document-footer {
|
|
1073
|
+
display: flex;
|
|
1074
|
+
flex-wrap: wrap;
|
|
1075
|
+
align-items: center;
|
|
1076
|
+
gap: var(--legal-document-footer-gap);
|
|
1077
|
+
min-width: 0;
|
|
1078
|
+
max-width: var(--legal-document-measure-max-width);
|
|
1079
|
+
border-block-start: var(--legal-document-footer-border);
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
/* Two-column mode (shell ≥56rem — a 1024px and a 1440px viewport both qualify): the
|
|
1083
|
+
* rail is PINNED beside a measure-capped document column and spans the header/body/
|
|
1084
|
+
* footer rows, so it stays in view for the whole scroll. Logical grid placement means
|
|
1085
|
+
* column 1 is the START side, i.e. it mirrors correctly under `dir="rtl"`.
|
|
1086
|
+
*
|
|
1087
|
+
* Below 56rem (the 390×844 case) NOTHING here applies: the shell stays a single column
|
|
1088
|
+
* and the rail is a STATIC compact contents block between the document header and the
|
|
1089
|
+
* first section — deliberately not sticky and not height-capped, so a phone never loses
|
|
1090
|
+
* vertical space to pinned chrome. */
|
|
1091
|
+
@container legal-document (min-width: 56rem) {
|
|
1092
|
+
.ui-legal-document {
|
|
1093
|
+
grid-template-columns:
|
|
1094
|
+
var(--legal-document-toc-width)
|
|
1095
|
+
minmax(0, var(--legal-document-measure-max-width));
|
|
1096
|
+
column-gap: var(--legal-document-column-gap);
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
.ui-legal-document-header {
|
|
1100
|
+
grid-column: 2;
|
|
1101
|
+
grid-row: 1;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
.ui-legal-document-rail {
|
|
1105
|
+
grid-column: 1;
|
|
1106
|
+
grid-row: 1 / span 3;
|
|
1107
|
+
position: sticky;
|
|
1108
|
+
inset-block-start: var(--legal-document-toc-inset-block-start);
|
|
1109
|
+
align-self: start;
|
|
1110
|
+
max-height: var(--legal-document-toc-max-height);
|
|
1111
|
+
overflow-y: auto;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
.ui-legal-document-content {
|
|
1115
|
+
grid-column: 2;
|
|
1116
|
+
grid-row: 2;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
.ui-legal-document-footer {
|
|
1120
|
+
grid-column: 2;
|
|
1121
|
+
grid-row: 3;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
758
1124
|
}
|
|
@@ -38,4 +38,66 @@
|
|
|
38
38
|
height: var(--logo-size-lg);
|
|
39
39
|
font-size: var(--logo-font-size-lg);
|
|
40
40
|
}
|
|
41
|
+
|
|
42
|
+
/* Identity fills — role defaults live HERE (call site), not on :root, so a scoped `.dark` /
|
|
43
|
+
* `[data-tenant]` override of --success re-resolves (docs/TOKENS.md · role-mirror knobs). */
|
|
44
|
+
.ui-logo[data-tone="success"] {
|
|
45
|
+
background: hsl(var(--logo-success-background, var(--success)));
|
|
46
|
+
color: hsl(var(--logo-success-foreground, var(--success-foreground)));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.ui-logo[data-mark="godx"] {
|
|
50
|
+
width: var(--logo-godx-size);
|
|
51
|
+
height: var(--logo-godx-size);
|
|
52
|
+
border-radius: 0;
|
|
53
|
+
background: transparent;
|
|
54
|
+
color: hsl(var(--logo-godx-color, var(--success)));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.ui-logo[data-mark="godx"] [data-slot="logo-artwork"] {
|
|
58
|
+
display: block;
|
|
59
|
+
width: 100%;
|
|
60
|
+
height: 100%;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/*
|
|
64
|
+
* LOCKUP — `<Logo wordmark="…" />`: the mark plus the readable product name as ONE element, so a
|
|
65
|
+
* shell header / auth brand bar never hand-rolls `inline-flex items-center gap-2` around a Logo
|
|
66
|
+
* and a `<Text>` (rule #46). Every knob is a token (rule #45); the wordmark colour defaults to
|
|
67
|
+
* the IDENTITY role and never reads --primary, so re-theming the action colour cannot recolour
|
|
68
|
+
* the brand — no consumer page CSS is required for the logo colour (gh#214).
|
|
69
|
+
*/
|
|
70
|
+
.ui-logo-lockup {
|
|
71
|
+
display: inline-flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
gap: var(--logo-wordmark-gap);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.ui-logo-lockup .ui-logo-wordmark {
|
|
77
|
+
font-family: var(--logo-wordmark-font-family, var(--font-family-display));
|
|
78
|
+
font-size: var(--logo-wordmark-font-size-md);
|
|
79
|
+
font-weight: var(--logo-wordmark-font-weight);
|
|
80
|
+
letter-spacing: var(--logo-wordmark-letter-spacing);
|
|
81
|
+
line-height: 1;
|
|
82
|
+
color: hsl(var(--logo-wordmark-color, var(--foreground)));
|
|
83
|
+
white-space: nowrap;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.ui-logo-lockup[data-size="xs"] .ui-logo-wordmark {
|
|
87
|
+
font-size: var(--logo-wordmark-font-size-xs);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.ui-logo-lockup[data-size="sm"] .ui-logo-wordmark {
|
|
91
|
+
font-size: var(--logo-wordmark-font-size-sm);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.ui-logo-lockup[data-size="lg"] .ui-logo-wordmark {
|
|
95
|
+
font-size: var(--logo-wordmark-font-size-lg);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* Canonical brand-green GoDX wordmark — the identity role, independent of --primary. */
|
|
99
|
+
.ui-logo-lockup[data-mark="godx"] .ui-logo-wordmark,
|
|
100
|
+
.ui-logo-lockup[data-tone="success"] .ui-logo-wordmark {
|
|
101
|
+
color: hsl(var(--logo-wordmark-color, var(--logo-godx-color, var(--success))));
|
|
102
|
+
}
|
|
41
103
|
}
|
|
@@ -1,4 +1,47 @@
|
|
|
1
1
|
@layer components {
|
|
2
|
+
.ui-app-setting-picker-inline {
|
|
3
|
+
width: auto;
|
|
4
|
+
min-height: 0;
|
|
5
|
+
height: auto;
|
|
6
|
+
border: 0;
|
|
7
|
+
border-radius: 0;
|
|
8
|
+
padding: 0;
|
|
9
|
+
background: transparent;
|
|
10
|
+
box-shadow: none;
|
|
11
|
+
color: inherit;
|
|
12
|
+
font: inherit;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
[data-slot="auth-legal-footer"] .ui-app-setting-picker-inline {
|
|
16
|
+
width: auto;
|
|
17
|
+
min-height: 0;
|
|
18
|
+
height: auto;
|
|
19
|
+
border-width: 0;
|
|
20
|
+
border-radius: 0;
|
|
21
|
+
padding: 0;
|
|
22
|
+
background: transparent;
|
|
23
|
+
box-shadow: none;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.ui-app-setting-picker-inline:hover,
|
|
27
|
+
.ui-app-setting-picker-inline[data-state="open"] {
|
|
28
|
+
background: transparent;
|
|
29
|
+
color: hsl(var(--foreground));
|
|
30
|
+
text-decoration: underline;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/*
|
|
34
|
+
* AppSettingPicker `compact` (gh#217) — a small, content-hugging LABELLED trigger for auth /
|
|
35
|
+
* legal footers: it keeps the icon, the readable value and the real control chrome (so it is
|
|
36
|
+
* obviously a switcher), but re-tiers the box to --control-height-sm and drops the picker's owned
|
|
37
|
+
* per-kind width. Every value is a token; the height comes from the official control tier, never
|
|
38
|
+
* a literal or an ad-hoc calc.
|
|
39
|
+
*/
|
|
40
|
+
.ui-app-setting-picker-compact {
|
|
41
|
+
--control-height: var(--app-setting-picker-compact-control-height);
|
|
42
|
+
--control-padding-x: var(--app-setting-picker-compact-padding-x);
|
|
43
|
+
--control-font-size: var(--app-setting-picker-compact-font-size);
|
|
44
|
+
}
|
|
2
45
|
[data-slot="tabs-list"] {
|
|
3
46
|
max-inline-size: var(--tabs-list-max-inline-size);
|
|
4
47
|
overflow-x: var(--tabs-list-overflow);
|