@godxjp/ui 18.5.0 → 18.7.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/dist/components/charts/compact-bar-trend.d.ts +2 -1
- package/dist/components/charts/index.d.ts +4 -1
- package/dist/components/data-display/data-table.d.ts +25 -4
- package/dist/components/data-display/data-table.js +13 -2
- package/dist/components/data-display/empty-state.js +1 -1
- package/dist/components/data-display/index.d.ts +1 -1
- package/dist/components/data-display/list-row.d.ts +19 -0
- package/dist/components/data-display/list-row.js +2 -0
- package/dist/components/data-display/qr-code.d.ts +1 -1
- package/dist/components/data-display/table.d.ts +47 -2
- package/dist/components/data-display/table.js +29 -4
- package/dist/components/data-entry/command.d.ts +10 -10
- package/dist/components/data-entry/index.d.ts +1 -0
- package/dist/components/data-entry/input-otp.d.ts +1 -0
- package/dist/components/feedback/alert.d.ts +20 -2
- package/dist/components/feedback/alert.js +11 -1
- package/dist/components/feedback/dialog.d.ts +14 -1
- package/dist/components/feedback/dialog.js +4 -0
- package/dist/components/feedback/index.d.ts +3 -3
- package/dist/components/feedback/index.js +5 -1
- package/dist/components/general/logo.d.ts +7 -1
- package/dist/components/general/logo.js +1 -1
- package/dist/components/layout/app-shell.d.ts +1 -1
- package/dist/components/layout/app-shell.js +55 -46
- package/dist/components/layout/auth-account-summary.d.ts +5 -0
- package/dist/components/layout/auth-account-summary.js +31 -0
- package/dist/components/layout/auth-shell.d.ts +5 -2
- package/dist/components/layout/centered-shell.d.ts +1 -1
- package/dist/components/layout/centered-shell.js +28 -19
- package/dist/components/layout/error-surface.d.ts +35 -0
- package/dist/components/layout/error-surface.js +155 -0
- package/dist/components/layout/flex.d.ts +1 -1
- package/dist/components/layout/flex.js +4 -0
- package/dist/components/layout/index.d.ts +7 -0
- package/dist/components/layout/index.js +6 -0
- package/dist/components/layout/page-container.d.ts +1 -1
- package/dist/components/layout/page-container.js +23 -41
- package/dist/components/layout/page-header.d.ts +22 -0
- package/dist/components/layout/page-header.js +89 -0
- package/dist/components/navigation/filter-bar.d.ts +2 -2
- package/dist/components/navigation/filter-bar.js +44 -1
- package/dist/components/navigation/index.d.ts +1 -1
- package/dist/components/navigation/steps.js +38 -19
- package/dist/components/navigation/tabs.js +12 -3
- package/dist/components/ui/avatar.d.ts +2 -1
- package/dist/components/ui/avatar.js +2 -1
- package/dist/components/ui/input-otp.d.ts +4 -1
- package/dist/components/ui/input-otp.js +2 -1
- package/dist/components/ui/toggle-group.d.ts +7 -4
- package/dist/components/ui/toggle-group.js +32 -21
- package/dist/email/brand-mark.d.ts +21 -5
- package/dist/email/brand-mark.js +5 -0
- package/dist/email/color.d.ts +2 -1
- package/dist/email/geometry.d.ts +9 -1
- package/dist/email/geometry.js +1 -0
- package/dist/email/index.d.ts +6 -1
- package/dist/email/index.js +6 -0
- package/dist/email/inline-style.d.ts +7 -5
- package/dist/email/inline-style.js +5 -0
- package/dist/email/tokens.generated.d.ts +40 -20
- package/dist/email/tokens.generated.js +40 -20
- package/dist/email/urgency.d.ts +22 -0
- package/dist/email/urgency.js +14 -0
- package/dist/i18n/messages/en.json +26 -1
- package/dist/i18n/messages/ja.json +23 -1
- package/dist/i18n/messages/vi.json +23 -1
- package/dist/props/components/data-display.prop.d.ts +63 -1
- package/dist/props/components/data-entry.prop.d.ts +6 -0
- package/dist/props/components/feedback.prop.d.ts +6 -0
- package/dist/props/components/index.d.ts +1 -1
- package/dist/props/components/layout.prop.d.ts +244 -4
- package/dist/props/components/navigation.prop.d.ts +57 -1
- package/dist/props/registry.d.ts +205 -5
- package/dist/props/registry.js +270 -6
- package/dist/props/vocabulary/data.prop.d.ts +27 -0
- package/dist/props/vocabulary/index.d.ts +3 -3
- package/dist/props/vocabulary/interaction.prop.d.ts +19 -2
- package/dist/props/vocabulary/layout.prop.d.ts +41 -7
- package/dist/styles/alert-layout.css +36 -0
- package/dist/styles/control.css +28 -0
- package/dist/styles/data-display-layout.css +26 -0
- package/dist/styles/layout.css +212 -0
- package/dist/styles/logo-layout.css +42 -9
- package/dist/styles/navigation-layout.css +109 -0
- package/dist/styles/shell-layout.css +336 -0
- package/dist/styles/table-layout.css +164 -1
- package/dist/theme/dxs.canonical.css +5 -4
- package/dist/tokens/axes.css +4 -3
- package/dist/tokens/base.css +1 -0
- package/dist/tokens/components/data-display.css +13 -0
- package/dist/tokens/components/email.css +43 -16
- package/dist/tokens/components/error-surface.css +36 -0
- package/dist/tokens/components/feedback.css +20 -0
- package/dist/tokens/components/list-row.css +20 -0
- package/dist/tokens/components/logo.css +52 -10
- package/dist/tokens/components/navigation.css +41 -0
- package/dist/tokens/components/shell.css +109 -0
- package/dist/tokens/components/table.css +45 -0
- package/dist/tokens/foundation.css +26 -0
- package/dist/tokens/semantic/layout.css +38 -0
- package/package.json +8 -2
|
@@ -40,6 +40,17 @@ export type ColumnDefProp<T> = {
|
|
|
40
40
|
* separating shadow. Pin at most one column per table.
|
|
41
41
|
*/
|
|
42
42
|
pin?: "end";
|
|
43
|
+
/**
|
|
44
|
+
* Relative importance of the column, read ONLY by `DataTable preset="action-collection"`
|
|
45
|
+
* (gh#253). It is the SAME contract the `Table` primitive exposes on `TableHead`/`TableCell`:
|
|
46
|
+
* DataTable stamps it onto both cells of the column for you, so the preset can swap the desktop
|
|
47
|
+
* intrinsic widths for the token-owned priority measures (`--table-action-collection-*`) instead
|
|
48
|
+
* of scrolling a five-column approval queue sideways at 390px. Leave the free-text column
|
|
49
|
+
* unmarked — it takes the remaining space. Emitted as `data-priority` only when set, so an
|
|
50
|
+
* ordinary column gains no attribute. Prefer this over `width` under the preset: an explicit
|
|
51
|
+
* `width` utility wins the cascade and defeats the priority measure.
|
|
52
|
+
*/
|
|
53
|
+
priority?: TableColumnPriorityProp;
|
|
43
54
|
};
|
|
44
55
|
/** Set of selected row IDs. */
|
|
45
56
|
export type SelectedIdsProp = Set<string>;
|
|
@@ -64,3 +75,19 @@ export type HasActiveFiltersProp = boolean;
|
|
|
64
75
|
* offset/background are tuned via the `--filter-bar-sticky-*` theme knobs.
|
|
65
76
|
*/
|
|
66
77
|
export type StickyProp = boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Table named collection preset. `"default"` keeps the plain table (and emits no attribute).
|
|
80
|
+
* `"action-collection"` is the canonical dense approval / action queue: below `collapseBelow`
|
|
81
|
+
* the desktop intrinsic column widths are replaced by token-owned column-PRIORITY measures under
|
|
82
|
+
* `table-layout: fixed` and cells wrap, so every column — including the row actions — stays inside
|
|
83
|
+
* the initial narrow frame. The real `<table>` semantics are never rewritten.
|
|
84
|
+
*/
|
|
85
|
+
export type TablePresetProp = "default" | "action-collection";
|
|
86
|
+
/**
|
|
87
|
+
* Relative importance of a table column, used by `preset="action-collection"` to allocate the
|
|
88
|
+
* narrow-frame measure. `"primary"` is the row's subject, `"secondary"` its object/target,
|
|
89
|
+
* `"meta"` a low-priority stamp (dates, ids), `"actions"` the row-action affordance whose measure
|
|
90
|
+
* is reserved first so it can never be pushed outside the viewport. A column with no priority
|
|
91
|
+
* takes the remaining space (the free-text column).
|
|
92
|
+
*/
|
|
93
|
+
export type TableColumnPriorityProp = "primary" | "secondary" | "meta" | "actions";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Barrel — all vocabulary prop types. */
|
|
2
2
|
export type { ClassNameProp, ChildrenProp, IdProp, OpenProp, DefaultOpenProp, OnOpenChangeProp, HandlerProp, PendingProp, RequiredProp, DisabledProp, LabelProp, HelperProp, ErrorProp, PlaceholderProp, NameProp, ValueProp, DefaultValueProp, OnValueChangeProp, OnChangeProp, OnClickProp, AsChildProp, WidthProp, } from "./shared.prop.js";
|
|
3
3
|
export type { TitleProp, SubtitleProp, DescriptionProp, ExtraProp, FooterProp, ActionProp, IconProp, ConfirmLabelProp, CancelLabelProp, ActionsProp, EmptyMessageProp, } from "./content.prop.js";
|
|
4
|
-
export type { PageDensityProp, PageContainerVariantProp, CenteredShellWidthProp, CenteredShellAlignProp, AuthShellPresetProp, TableDensityProp, DensityProp, GapProp, } from "./layout.prop.js";
|
|
5
|
-
export type { ButtonVariantProp, ButtonSizeProp, BadgeVariantProp, AppSettingPickerAppearanceProp, ShapeProp, TextSizeProp, TextToneProp, FontWeightProp, HeadingLevelProp, TextAlignProp, SizeProp, FormLayoutProp, BreakpointProp, ConfirmVariantProp, ToneProp, AlertVariantProp, SortDirectionProp, ColumnAlignProp, SortStateProp, RevealDelayProp, } from "./interaction.prop.js";
|
|
4
|
+
export type { PageDensityProp, PageContainerVariantProp, CenteredShellWidthProp, CenteredShellAlignProp, CenteredShellPresetProp, ErrorSurfaceModeProp, ErrorSurfaceStatusProp, AuthShellPresetProp, TableDensityProp, DensityProp, GapProp, } from "./layout.prop.js";
|
|
5
|
+
export type { ButtonVariantProp, ButtonSizeProp, BadgeVariantProp, AppSettingPickerAppearanceProp, ShapeProp, AvatarShapeProp, TextSizeProp, TextToneProp, FontWeightProp, HeadingLevelProp, TextAlignProp, SizeProp, FormLayoutProp, BreakpointProp, ConfirmVariantProp, ToneProp, AlertVariantProp, SortDirectionProp, ColumnAlignProp, SortStateProp, RevealDelayProp, } from "./interaction.prop.js";
|
|
6
6
|
export type { BreadcrumbItemProp, BreadcrumbProp } from "./navigation.prop.js";
|
|
7
|
-
export type { GetRowIdProp, OnRowClickProp, ColumnDefProp, SelectedIdsProp, OnSelectChangeProp, OnTableDensityChangeProp, OnSortChangeProp, OnSearchChangeProp, OnClearFiltersProp, HasActiveFiltersProp, StickyProp, } from "./data.prop.js";
|
|
7
|
+
export type { GetRowIdProp, OnRowClickProp, ColumnDefProp, TablePresetProp, TableColumnPriorityProp, SelectedIdsProp, OnSelectChangeProp, OnTableDensityChangeProp, OnSortChangeProp, OnSearchChangeProp, OnClearFiltersProp, HasActiveFiltersProp, StickyProp, } from "./data.prop.js";
|
|
@@ -7,6 +7,15 @@ export type ButtonVariantProp = "default" | "destructive" | "outline" | "dashed"
|
|
|
7
7
|
/** Corner shape — maps to the radius tokens (default = control/component radius). Shared by
|
|
8
8
|
* Button + Badge. `pill` = fully rounded (`--radius-pill`), `sharp` = square (`--radius-sharp`). */
|
|
9
9
|
export type ShapeProp = "default" | "pill" | "sharp";
|
|
10
|
+
/**
|
|
11
|
+
* Avatar geometry — WHAT the mark represents, not just its corner radius, which is why it is a
|
|
12
|
+
* separate vocabulary from the control `ShapeProp` (`default | pill | sharp`): an entity mark is a
|
|
13
|
+
* ROUNDED rect, a value `ShapeProp` cannot express (its `sharp` = `--radius-sharp` = 0).
|
|
14
|
+
* - `circle` (default) — a person: the fully-round `--radius-pill` identity avatar.
|
|
15
|
+
* - `square` — an organization / service entity mark for an entity header: the compact rounded
|
|
16
|
+
* square on the brand surface (`--avatar-square-*` tokens).
|
|
17
|
+
*/
|
|
18
|
+
export type AvatarShapeProp = "circle" | "square";
|
|
10
19
|
/** Text size — steps of the golden-ratio type scale (NEVER an arbitrary px). `sm` = base. */
|
|
11
20
|
export type TextSizeProp = "2xs" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
|
|
12
21
|
/** Text colour intent — maps to semantic foreground tokens (no raw palette). */
|
|
@@ -45,8 +54,16 @@ export type BreakpointProp = "sm" | "md" | "lg" | "xl";
|
|
|
45
54
|
export type ConfirmVariantProp = "default" | "destructive";
|
|
46
55
|
/** Semantic color/status intent. */
|
|
47
56
|
export type ToneProp = "default" | "success" | "warning" | "destructive" | "info" | "muted" | "neutral";
|
|
48
|
-
/**
|
|
49
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Alert visual treatment.
|
|
59
|
+
*
|
|
60
|
+
* `"default"` is the inset, rounded, fully-bordered INLINE alert that sits inside a page body.
|
|
61
|
+
* `"banner"` is the page-level strip the `Banner` alias ships (gh#255): square, edge-to-edge, ruled
|
|
62
|
+
* on the block-end edge only, so it reads as page chrome above the content rather than as a card
|
|
63
|
+
* inside it. Both share one implementation, one tone scale and one dismiss/actions contract —
|
|
64
|
+
* `banner` re-measures the box through `--banner-*` tokens and changes nothing else.
|
|
65
|
+
*/
|
|
66
|
+
export type AlertVariantProp = "default" | "banner";
|
|
50
67
|
/** Sort direction for table columns. */
|
|
51
68
|
export type SortDirectionProp = "asc" | "desc";
|
|
52
69
|
/** Table column text alignment. */
|
|
@@ -17,18 +17,52 @@ export type CenteredShellWidthProp = "sm" | "md" | "lg";
|
|
|
17
17
|
* maintenance notice) — the geometry stays package-owned, so no consumer `min-h-dvh` / flex CSS.
|
|
18
18
|
*/
|
|
19
19
|
export type CenteredShellAlignProp = "start" | "center";
|
|
20
|
+
/**
|
|
21
|
+
* ErrorSurface shell contract — WHERE the exception surface lives, not how it looks.
|
|
22
|
+
*
|
|
23
|
+
* `"application"` (403 / 404): the failure happened INSIDE the authenticated app, so the surface is
|
|
24
|
+
* the page BODY of the `AppShell` the route already renders. It never reconstructs nav chrome — a
|
|
25
|
+
* component cannot manufacture the sidebar/topbar/user menu, which are consumer-owned; it renders
|
|
26
|
+
* as `AppShell`'s children (usually inside a `PageContainer`) so the shell is PRESERVED and the
|
|
27
|
+
* user is never stranded chrome-less.
|
|
28
|
+
*
|
|
29
|
+
* `"system"` (500 / 503): the app itself failed — session and nav data may be gone — so the surface
|
|
30
|
+
* owns the whole page and renders package-owned viewport-centred geometry
|
|
31
|
+
* (`CenteredShell align="center"`), with no consumer `min-h-dvh` / flex CSS at 1440 / 1024 / 390.
|
|
32
|
+
*/
|
|
33
|
+
export type ErrorSurfaceModeProp = "application" | "system";
|
|
34
|
+
/**
|
|
35
|
+
* The HTTP status an ErrorSurface presents. Deliberately closed to the four exception pages every
|
|
36
|
+
* app ships (RFC 9110 §15.5.4 / §15.5.5 / §15.6.1 / §15.6.4); it drives the default icon, tone and
|
|
37
|
+
* the recommended `mode`. A number, never a string — it is the numeric status, and it is rendered
|
|
38
|
+
* with tabular figures.
|
|
39
|
+
*/
|
|
40
|
+
export type ErrorSurfaceStatusProp = 403 | 404 | 500 | 503;
|
|
20
41
|
/**
|
|
21
42
|
* AuthShell named flow preset — the page MEASURE contract for a canonical hosted-identity flow
|
|
22
43
|
* (card max-width plus the desktop and mobile page gutters), owned by component tokens.
|
|
23
|
-
* `"default"` keeps the shell's own measure; `"
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* measure
|
|
27
|
-
*
|
|
28
|
-
*
|
|
44
|
+
* `"default"` keeps the shell's own measure; `"login"` owns SCR-001's stable card anchor for
|
|
45
|
+
* standalone and requester flows; `"device-authorization"` is the 380px device-grant measure with
|
|
46
|
+
* a 5px mobile inline gutter; `"context-selection"` is the 25rem organisation/context picker
|
|
47
|
+
* measure that goes edge-to-edge on mobile; `"account-recovery"` is the 432px SCR-008 measure
|
|
48
|
+
* shared by the password-recovery and sign-in MFA challenge panels (15px mobile inline gutter);
|
|
49
|
+
* `"registration"` is the 360px sign-up measure — the only START-aligned preset, because a
|
|
50
|
+
* registration card is the tallest surface in the set and a centred tall card overflows above the
|
|
51
|
+
* scroll origin, and the only one with its own footer-clearance knob.
|
|
52
|
+
* Orthogonal to AuthShell's `variant` (which owns control density and heading size) — combine them.
|
|
29
53
|
*/
|
|
30
|
-
export type AuthShellPresetProp = "default" | "device-authorization" | "context-selection" | "account-recovery";
|
|
54
|
+
export type AuthShellPresetProp = "default" | "login" | "registration" | "device-authorization" | "context-selection" | "account-recovery";
|
|
31
55
|
/** Shared gap between layout children; components may document subsets. */
|
|
32
56
|
export type GapProp = "xs" | "sm" | "md" | "lg" | "xl";
|
|
33
57
|
/** DataTable row density subset. */
|
|
34
58
|
export type TableDensityProp = Exclude<DensityProp, "default">;
|
|
59
|
+
/**
|
|
60
|
+
* CenteredShell named page preset — the token-owned SHELL geometry of a whole page shape.
|
|
61
|
+
* `"default"` keeps the shell's own box (and emits no attribute at all, so it can match no rule).
|
|
62
|
+
* `"public-landing"` is the PUBLIC marketing / product landing contract: one content measure
|
|
63
|
+
* shared by the header bar, the centred column and the footer, the section rhythm between page
|
|
64
|
+
* sections, the flat (elevation-free) public-surface chrome and the hero heading tier — all owned
|
|
65
|
+
* by `--centered-shell-landing-*` knobs so the landing COMPOSITION (hero, nav, sections, footer —
|
|
66
|
+
* each of which fails the Framework-Component Test) needs no page-local CSS.
|
|
67
|
+
*/
|
|
68
|
+
export type CenteredShellPresetProp = "default" | "public-landing";
|
|
@@ -51,6 +51,42 @@
|
|
|
51
51
|
padding-inline-end: calc(var(--alert-space-inset) + var(--space-8));
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
/*
|
|
55
|
+
* BANNER variant (gh#255) — the same Alert, re-measured as page chrome: edge-to-edge, square,
|
|
56
|
+
* ruled only on the block-end edge so it separates itself from the content BELOW it. Declared
|
|
57
|
+
* after the base box (equal specificity → source order wins) and it touches geometry only, so
|
|
58
|
+
* every tone, the icon treatment, the actions grid and the dismiss control are inherited
|
|
59
|
+
* unchanged from the inline alert above.
|
|
60
|
+
*/
|
|
61
|
+
[data-slot="alert"][data-variant="banner"] {
|
|
62
|
+
border-radius: var(--banner-radius);
|
|
63
|
+
border-width: var(--banner-border-width);
|
|
64
|
+
border-block-end-width: var(--banner-border-block-end-width);
|
|
65
|
+
padding: var(--banner-space-block) var(--banner-space-inline);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* The dismissible reserve is re-derived from the BANNER inset — reusing the inline-alert
|
|
69
|
+
* calc would leave a ✕-sized hole on a strip whose own inset is half as deep. */
|
|
70
|
+
[data-slot="alert"][data-variant="banner"][data-dismissible] {
|
|
71
|
+
padding-inline-end: calc(var(--banner-space-inline) + var(--space-8));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
[data-slot="alert"][data-variant="banner"] > [data-slot="alert-dismiss"] {
|
|
75
|
+
top: var(--banner-dismiss-space-offset);
|
|
76
|
+
inset-inline-end: var(--banner-dismiss-space-offset);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* The compact page gutter. `.ui-page-container` steps `--space-page-active-x` down at this same
|
|
80
|
+
* breakpoint, and a banner mounted ABOVE the container (the normal case — page/app chrome) does
|
|
81
|
+
* not inherit that, so without this rule its text would sit 8px outside the page title at 390px.
|
|
82
|
+
* Bound to its own `--banner-space-inline-compact` knob rather than a literal, so a service
|
|
83
|
+
* retunes the mobile inset the same way it retunes the desktop one. */
|
|
84
|
+
@media (max-width: 720px) {
|
|
85
|
+
[data-slot="alert"][data-variant="banner"] {
|
|
86
|
+
--banner-space-inline: var(--banner-space-inline-compact);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
54
90
|
[data-slot="alert-icon"] {
|
|
55
91
|
width: 1.25rem;
|
|
56
92
|
height: 1.25rem;
|
package/dist/styles/control.css
CHANGED
|
@@ -745,6 +745,34 @@
|
|
|
745
745
|
border-start-end-radius: var(--radius-md);
|
|
746
746
|
border-end-end-radius: var(--radius-md);
|
|
747
747
|
}
|
|
748
|
+
.ui-otp-group[data-appearance="grouped"] {
|
|
749
|
+
overflow: hidden;
|
|
750
|
+
border: 1px solid hsl(var(--border));
|
|
751
|
+
border-radius: var(--radius-md);
|
|
752
|
+
background: hsl(var(--background));
|
|
753
|
+
}
|
|
754
|
+
.ui-otp-group[data-appearance="grouped"] .ui-otp-slot {
|
|
755
|
+
border: 0;
|
|
756
|
+
border-radius: 0;
|
|
757
|
+
background: transparent;
|
|
758
|
+
}
|
|
759
|
+
.ui-otp-group[data-appearance="grouped"] .ui-otp-slot[data-active="true"] {
|
|
760
|
+
box-shadow: none;
|
|
761
|
+
}
|
|
762
|
+
.ui-otp-group[data-appearance="grouped"]:has(.ui-otp-slot[data-active="true"]) {
|
|
763
|
+
border-color: hsl(var(--focus-ring-color, var(--ring)));
|
|
764
|
+
box-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--focus-ring-color, var(--ring)));
|
|
765
|
+
}
|
|
766
|
+
.ui-otp-container:has(.ui-otp-input[aria-invalid="true"]) .ui-otp-group[data-appearance="grouped"] {
|
|
767
|
+
border-color: hsl(var(--destructive));
|
|
768
|
+
}
|
|
769
|
+
.ui-otp-container:has(.ui-otp-input[aria-invalid="true"])
|
|
770
|
+
.ui-otp-group[data-appearance="grouped"]:has(.ui-otp-slot[data-active="true"]) {
|
|
771
|
+
box-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--destructive));
|
|
772
|
+
}
|
|
773
|
+
.ui-otp-container:has(.ui-otp-input:disabled) .ui-otp-group[data-appearance="grouped"] {
|
|
774
|
+
background: hsl(var(--muted));
|
|
775
|
+
}
|
|
748
776
|
.ui-otp-slot[data-active="true"] {
|
|
749
777
|
z-index: 1;
|
|
750
778
|
box-shadow: 0 0 0 var(--focus-ring-width) hsl(var(--focus-ring-color, var(--ring)));
|
|
@@ -18,6 +18,18 @@
|
|
|
18
18
|
font-weight: var(--font-weight-medium);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
/* Entity-header mark — `<Avatar shape="square">` (gh#249). Opt-in ONLY: without the attribute the
|
|
22
|
+
* avatar above is untouched. Geometry and colour are token-owned (rule #45); the brand fill is
|
|
23
|
+
* handed down through --avatar-background so the fallback slot picks it up with no extra rule,
|
|
24
|
+
* and a service that only wants a neutral square overrides --avatar-square-background. */
|
|
25
|
+
.ui-avatar[data-shape="square"] {
|
|
26
|
+
inline-size: var(--avatar-square-size);
|
|
27
|
+
block-size: var(--avatar-square-size);
|
|
28
|
+
border-radius: var(--avatar-square-radius);
|
|
29
|
+
--avatar-background: var(--avatar-square-background, hsl(var(--primary)));
|
|
30
|
+
color: var(--avatar-square-foreground, hsl(var(--primary-foreground)));
|
|
31
|
+
}
|
|
32
|
+
|
|
21
33
|
.ui-avatar-image {
|
|
22
34
|
width: 100%;
|
|
23
35
|
height: 100%;
|
|
@@ -508,6 +520,20 @@
|
|
|
508
520
|
[data-slot="list-row"][data-overflow="wrap"] [data-slot="list-row-body"] {
|
|
509
521
|
overflow-wrap: anywhere;
|
|
510
522
|
}
|
|
523
|
+
/* `density="compact"` (#246) — the compact inline-actions geometry: tighter block padding and
|
|
524
|
+
* column gap plus a LOWER body threshold, so a 36px avatar, the title/description and one or two
|
|
525
|
+
* small trailing Buttons stay inline inside the canonical 326px content column at 390px. It only
|
|
526
|
+
* LOWERS thresholds; the #224 overflow contract above is untouched (the row still wraps, the body
|
|
527
|
+
* is still clamped with min(…, 100%)), so a compact row can never widen the page root. */
|
|
528
|
+
[data-slot="list-row"][data-density="compact"] {
|
|
529
|
+
gap: var(--list-row-compact-gap, var(--space-2));
|
|
530
|
+
padding: var(--list-row-compact-padding-y, var(--space-2))
|
|
531
|
+
var(--list-row-compact-padding-x, var(--list-row-padding-x));
|
|
532
|
+
}
|
|
533
|
+
[data-slot="list-row"][data-density="compact"] [data-slot="list-row-body"] {
|
|
534
|
+
min-inline-size: min(var(--list-row-compact-body-min-width, 6rem), 100%);
|
|
535
|
+
flex-basis: var(--list-row-compact-body-min-width, 6rem);
|
|
536
|
+
}
|
|
511
537
|
[data-slot="list-row-trailing"] {
|
|
512
538
|
display: inline-flex;
|
|
513
539
|
max-inline-size: 100%;
|
package/dist/styles/layout.css
CHANGED
|
@@ -411,6 +411,29 @@
|
|
|
411
411
|
max-width: 42rem;
|
|
412
412
|
}
|
|
413
413
|
|
|
414
|
+
/* `measure` (gh#245 / gh#247) — a bounded page measure axis ORTHOGONAL to `variant`, so the
|
|
415
|
+
* quiet `ghost` chrome and a narrow/medium measure finally compose (they used to be the same
|
|
416
|
+
* variant axis, which is exactly why a notification feed could not have both). Unlike
|
|
417
|
+
* `variant="narrow"` — which caps only the body and leaves the header `extra` action stranded
|
|
418
|
+
* at the page edge — this caps the HEADER and the BODY to ONE shared measure, so the action
|
|
419
|
+
* ends on the same edge as the body surface. Both elements carry the identical page gutters,
|
|
420
|
+
* so their content edges line up exactly at both ends.
|
|
421
|
+
*
|
|
422
|
+
* `measure="default"` is the inert default: it emits `data-measure="default"`, which matches NO
|
|
423
|
+
* rule here, so every existing page renders byte-identically (the gh#231 `data-layout="stack"`
|
|
424
|
+
* precedent). The cap is a max, never a width — below it the page stays fluid, so 390px keeps
|
|
425
|
+
* the full compact-gutter surface. The footer is deliberately NOT capped: its border/background
|
|
426
|
+
* is page-level chrome that must span the page when `stickyFooter` pins it. */
|
|
427
|
+
.ui-page-container[data-measure="narrow"] .ui-page-header,
|
|
428
|
+
.ui-page-container[data-measure="narrow"] .ui-page-body {
|
|
429
|
+
max-inline-size: var(--page-measure-narrow);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
.ui-page-container[data-measure="medium"] .ui-page-header,
|
|
433
|
+
.ui-page-container[data-measure="medium"] .ui-page-body {
|
|
434
|
+
max-inline-size: var(--page-measure-medium);
|
|
435
|
+
}
|
|
436
|
+
|
|
414
437
|
/* flush = full-bleed body (DataTable); header/footer keep their inset. */
|
|
415
438
|
.ui-page-container--flush .ui-page-body {
|
|
416
439
|
padding-inline-start: 0;
|
|
@@ -506,6 +529,41 @@
|
|
|
506
529
|
overflow-wrap: anywhere;
|
|
507
530
|
}
|
|
508
531
|
|
|
532
|
+
/* Title + `meta` share one baseline-aligned band (gh#255). The band only exists when `meta` is
|
|
533
|
+
* passed, so a header without it keeps its historical bare-`<h1>` DOM. `wrap` is what keeps a
|
|
534
|
+
* long JA/VI title and a status Badge legible at 390px instead of squeezing the title column. */
|
|
535
|
+
.ui-page-title-band {
|
|
536
|
+
display: flex;
|
|
537
|
+
flex-wrap: wrap;
|
|
538
|
+
align-items: baseline;
|
|
539
|
+
gap: var(--page-header-meta-gap);
|
|
540
|
+
min-inline-size: 0;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
.ui-page-header-meta {
|
|
544
|
+
display: flex;
|
|
545
|
+
flex-wrap: wrap;
|
|
546
|
+
align-items: center;
|
|
547
|
+
gap: var(--page-header-meta-gap);
|
|
548
|
+
min-inline-size: 0;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/* `loading` placeholders — the real `<h1>`/`<p>` wearing the shared `.ui-skeleton-block` skin, so
|
|
552
|
+
* the pending band occupies the same rhythm the settled band will. `max-inline-size: 100%` keeps
|
|
553
|
+
* them inside a 390px column; the block size replaces the line box the (sr-only) text no longer
|
|
554
|
+
* fills. */
|
|
555
|
+
.ui-page-title-placeholder {
|
|
556
|
+
inline-size: var(--page-title-placeholder-measure);
|
|
557
|
+
max-inline-size: 100%;
|
|
558
|
+
min-block-size: var(--page-title-placeholder-block-size);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
.ui-page-subtitle-placeholder {
|
|
562
|
+
inline-size: var(--page-subtitle-placeholder-measure);
|
|
563
|
+
max-inline-size: 100%;
|
|
564
|
+
min-block-size: var(--page-subtitle-placeholder-block-size);
|
|
565
|
+
}
|
|
566
|
+
|
|
509
567
|
/* Compact viewports use a responsive type step, not compact density: body/control text and
|
|
510
568
|
* touch targets remain readable while page chrome stops competing with primary actions. A
|
|
511
569
|
* single Flex action group must wrap even when the consumer omitted `wrap`, because clipping
|
|
@@ -711,6 +769,20 @@
|
|
|
711
769
|
}
|
|
712
770
|
}
|
|
713
771
|
|
|
772
|
+
@layer components {
|
|
773
|
+
.ui-page-container[data-preset="admin-collection"] {
|
|
774
|
+
--control-height: var(--admin-collection-control-height);
|
|
775
|
+
--space-section-active: var(--admin-collection-section-gap);
|
|
776
|
+
--filter-bar-padding-y: var(--admin-collection-toolbar-padding-y);
|
|
777
|
+
--table-row-height: var(--admin-collection-table-row-height);
|
|
778
|
+
--table-cell-padding-y: var(--admin-collection-table-cell-padding-y);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
.ui-page-container[data-preset="admin-collection"] .ui-toolbar .ui-search-input {
|
|
782
|
+
inline-size: min(100%, var(--admin-collection-search-measure));
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
714
786
|
@layer components {
|
|
715
787
|
/* Pagination is ONE horizontal row and never wraps to a second desktop line (gh#153 — the old
|
|
716
788
|
* `flex-wrap: wrap` split the range / size-changer / page strip across rows). The number strip
|
|
@@ -881,6 +953,84 @@
|
|
|
881
953
|
font-size: var(--filter-label-font-size);
|
|
882
954
|
}
|
|
883
955
|
|
|
956
|
+
/* ── FilterBar typed model (#258) ──────────────────────────────────────────────────────────
|
|
957
|
+
* Every rule below is scoped to a region the bar only emits when its prop is passed, so a bar
|
|
958
|
+
* built the old way (children + onClear) matches none of them and is geometrically untouched.
|
|
959
|
+
*
|
|
960
|
+
* SEARCH — full-width while the strip is stacked (a bounded search box on a 390px screen wastes
|
|
961
|
+
* the row), then one shared token-owned measure from the 640px step up. This single measure is
|
|
962
|
+
* what the issue was filed against: as a plain child, every page picked its own width. */
|
|
963
|
+
.ui-filter-bar-search {
|
|
964
|
+
width: 100%;
|
|
965
|
+
min-width: 0;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
@media (min-width: 640px) {
|
|
969
|
+
.ui-filter-bar-search {
|
|
970
|
+
width: var(--filter-bar-search-width);
|
|
971
|
+
max-width: 100%;
|
|
972
|
+
flex: 0 0 auto;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
/* CHIPS — always their own full-width line. Applied filters are a record of state, not another
|
|
977
|
+
* control in the row; wrapping them in beside the pickers makes both harder to scan. */
|
|
978
|
+
.ui-filter-bar-chips {
|
|
979
|
+
display: flex;
|
|
980
|
+
width: 100%;
|
|
981
|
+
flex-basis: 100%;
|
|
982
|
+
flex-wrap: wrap;
|
|
983
|
+
align-items: center;
|
|
984
|
+
gap: var(--filter-bar-chip-row-gap);
|
|
985
|
+
min-width: 0;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
.ui-filter-bar-chip {
|
|
989
|
+
display: inline-flex;
|
|
990
|
+
max-width: 100%;
|
|
991
|
+
align-items: center;
|
|
992
|
+
gap: var(--filter-bar-chip-gap);
|
|
993
|
+
padding: var(--filter-bar-chip-padding-block) var(--filter-bar-chip-padding-inline);
|
|
994
|
+
border-radius: var(--filter-bar-chip-radius);
|
|
995
|
+
background: hsl(var(--filter-bar-chip-background, var(--muted)));
|
|
996
|
+
color: hsl(var(--filter-bar-chip-foreground, var(--muted-foreground)));
|
|
997
|
+
font-size: var(--filter-bar-chip-font-size);
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
/* A long JA/VI chip value wraps inside the chip rather than pushing the row into a horizontal
|
|
1001
|
+
* scroll — the remove control must stay reachable at 390px. */
|
|
1002
|
+
.ui-filter-bar-chip-label {
|
|
1003
|
+
min-width: 0;
|
|
1004
|
+
overflow-wrap: anywhere;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
.ui-filter-bar-chip-remove {
|
|
1008
|
+
flex: none;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
.ui-filter-bar-count {
|
|
1012
|
+
color: hsl(var(--filter-bar-count-foreground, var(--muted-foreground)));
|
|
1013
|
+
font-size: var(--filter-bar-count-font-size);
|
|
1014
|
+
/* The count is a number that changes in place — tabular figures stop the row from twitching
|
|
1015
|
+
* as it goes 9 → 10 → 100. */
|
|
1016
|
+
font-variant-numeric: tabular-nums;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
.ui-filter-bar-actions {
|
|
1020
|
+
display: flex;
|
|
1021
|
+
width: 100%;
|
|
1022
|
+
flex-wrap: wrap;
|
|
1023
|
+
align-items: center;
|
|
1024
|
+
gap: var(--space-inline-sm);
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
@media (min-width: 640px) {
|
|
1028
|
+
.ui-filter-bar-actions {
|
|
1029
|
+
width: auto;
|
|
1030
|
+
flex: 0 0 auto;
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
|
|
884
1034
|
/* Toolbar/FilterBar `overflow="scroll"` (#216) — one bounded row that scrolls INLINE instead of
|
|
885
1035
|
* wrapping onto extra rows. Below 640px the strip still stacks (the `wrap` base rules above own
|
|
886
1036
|
* that): a 390px viewport must never hide a filter behind a horizontal scroll it cannot see.
|
|
@@ -1122,3 +1272,65 @@
|
|
|
1122
1272
|
}
|
|
1123
1273
|
}
|
|
1124
1274
|
}
|
|
1275
|
+
|
|
1276
|
+
@layer components {
|
|
1277
|
+
/* ── Flex responsive region visibility (gh#252) ──────────────────────────────────────────────
|
|
1278
|
+
* `hideBelow` / `hideFrom` are the ONE public way to drop a layout region at a breakpoint step.
|
|
1279
|
+
* Without them a public header cannot hide its anchor navigation below the tablet step, or show
|
|
1280
|
+
* a compact-only affordance, without a page-local media query — which is exactly the API-first
|
|
1281
|
+
* violation gh#252 reports. The steps are the package's canonical breakpoint scale, the same
|
|
1282
|
+
* values `--master-detail-collapse-below` uses (sm 40rem · md 48rem · lg 64rem · xl 80rem); a
|
|
1283
|
+
* media query cannot read a `var()`, so the step values are the tokenized scale written out.
|
|
1284
|
+
*
|
|
1285
|
+
* Both props default to `undefined`, so NO `data-hide-*` attribute is emitted and none of these
|
|
1286
|
+
* selectors can match — every existing Flex is untouched (the gh#231 inert-default contract).
|
|
1287
|
+
* `display: none` removes the region from the a11y tree too, which is the point: the destination
|
|
1288
|
+
* must stay reachable from another region (the footer nav) at that width. */
|
|
1289
|
+
@media (width < 40rem) {
|
|
1290
|
+
.ui-flex[data-hide-below="sm"] {
|
|
1291
|
+
display: none;
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
@media (width < 48rem) {
|
|
1296
|
+
.ui-flex[data-hide-below="md"] {
|
|
1297
|
+
display: none;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
@media (width < 64rem) {
|
|
1302
|
+
.ui-flex[data-hide-below="lg"] {
|
|
1303
|
+
display: none;
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
@media (width < 80rem) {
|
|
1308
|
+
.ui-flex[data-hide-below="xl"] {
|
|
1309
|
+
display: none;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
@media (width >= 40rem) {
|
|
1314
|
+
.ui-flex[data-hide-from="sm"] {
|
|
1315
|
+
display: none;
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
@media (width >= 48rem) {
|
|
1320
|
+
.ui-flex[data-hide-from="md"] {
|
|
1321
|
+
display: none;
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
@media (width >= 64rem) {
|
|
1326
|
+
.ui-flex[data-hide-from="lg"] {
|
|
1327
|
+
display: none;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
@media (width >= 80rem) {
|
|
1332
|
+
.ui-flex[data-hide-from="xl"] {
|
|
1333
|
+
display: none;
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
@@ -39,19 +39,52 @@
|
|
|
39
39
|
font-size: var(--logo-font-size-lg);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
/* Identity fills —
|
|
43
|
-
* `
|
|
42
|
+
/* Identity fills — the IDENTITY role --brand (canonical GoDX emerald), NOT the --success STATUS
|
|
43
|
+
* green: `tone="success"` names the semantic slot, not the status role, and a badge/progress
|
|
44
|
+
* green must never drag the product mark with it (gh#250). Role defaults live HERE (call site),
|
|
45
|
+
* not on :root, so a scoped `.dark` / `[data-tenant]` override of --brand re-resolves
|
|
46
|
+
* (docs/TOKENS.md · role-mirror knobs).
|
|
47
|
+
*
|
|
48
|
+
* The INK is --logo-identity-foreground, NOT --brand-foreground: this branch sets caller-supplied
|
|
49
|
+
* TEXT on the fill, so WCAG 2.2 SC 1.4.3 applies at 4.5:1 (14px bold is not "large text"), while
|
|
50
|
+
* --brand-foreground is the artwork KNOCKOUT colour that tracks --background and only owes 3:1 as
|
|
51
|
+
* non-text (SC 1.4.11). Knockout white on the emerald measured 3.67:1 — an AA failure on every
|
|
52
|
+
* boxed `tone="success"` glyph. See src/tokens/components/logo.css. */
|
|
44
53
|
.ui-logo[data-tone="success"] {
|
|
45
|
-
background: hsl(var(--logo-success-background, var(--
|
|
46
|
-
color: hsl(var(--logo-success-foreground, var(--
|
|
54
|
+
background: hsl(var(--logo-success-background, var(--brand)));
|
|
55
|
+
color: hsl(var(--logo-success-foreground, var(--logo-identity-foreground)));
|
|
47
56
|
}
|
|
48
57
|
|
|
58
|
+
/* The identity mark honours `size` like every other Logo branch. The godx box has its OWN tier
|
|
59
|
+
* scale (--logo-godx-size-*) because the artwork is a horizontal capsule in a square viewBox and
|
|
60
|
+
* needs ~+0.25rem over the filled glyph box to read at the same weight — but a public `size` prop
|
|
61
|
+
* that renders identically at xs and lg is a silent no-op, and the lockup already scaled its
|
|
62
|
+
* wordmark per tier, so a frozen mark broke the mark↔wordmark proportion at `size="lg"`.
|
|
63
|
+
* These are `[data-mark][data-size]` (0,3,0) so they beat BOTH `.ui-logo[data-size]` and the
|
|
64
|
+
* `[data-mark="godx"]` base regardless of source order — the ordering bug that hid `size` here.
|
|
65
|
+
* --logo-godx-size stays the documented PIN: unset by default, and set once by a service theme it
|
|
66
|
+
* wins at every tier (call-site default, docs/TOKENS.md). */
|
|
49
67
|
.ui-logo[data-mark="godx"] {
|
|
50
|
-
width: var(--logo-godx-size);
|
|
51
|
-
height: var(--logo-godx-size);
|
|
68
|
+
width: var(--logo-godx-size, var(--logo-godx-size-md));
|
|
69
|
+
height: var(--logo-godx-size, var(--logo-godx-size-md));
|
|
52
70
|
border-radius: 0;
|
|
53
71
|
background: transparent;
|
|
54
|
-
color: hsl(var(--logo-godx-color, var(--
|
|
72
|
+
color: hsl(var(--logo-godx-color, var(--brand)));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.ui-logo[data-mark="godx"][data-size="xs"] {
|
|
76
|
+
width: var(--logo-godx-size, var(--logo-godx-size-xs));
|
|
77
|
+
height: var(--logo-godx-size, var(--logo-godx-size-xs));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.ui-logo[data-mark="godx"][data-size="sm"] {
|
|
81
|
+
width: var(--logo-godx-size, var(--logo-godx-size-sm));
|
|
82
|
+
height: var(--logo-godx-size, var(--logo-godx-size-sm));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.ui-logo[data-mark="godx"][data-size="lg"] {
|
|
86
|
+
width: var(--logo-godx-size, var(--logo-godx-size-lg));
|
|
87
|
+
height: var(--logo-godx-size, var(--logo-godx-size-lg));
|
|
55
88
|
}
|
|
56
89
|
|
|
57
90
|
.ui-logo[data-mark="godx"] [data-slot="logo-artwork"] {
|
|
@@ -95,9 +128,9 @@
|
|
|
95
128
|
font-size: var(--logo-wordmark-font-size-lg);
|
|
96
129
|
}
|
|
97
130
|
|
|
98
|
-
/* Canonical
|
|
131
|
+
/* Canonical GoDX wordmark — the --brand IDENTITY role, independent of --primary AND of --success. */
|
|
99
132
|
.ui-logo-lockup[data-mark="godx"] .ui-logo-wordmark,
|
|
100
133
|
.ui-logo-lockup[data-tone="success"] .ui-logo-wordmark {
|
|
101
|
-
color: hsl(var(--logo-wordmark-color, var(--logo-godx-color, var(--
|
|
134
|
+
color: hsl(var(--logo-wordmark-color, var(--logo-godx-color, var(--brand))));
|
|
102
135
|
}
|
|
103
136
|
}
|