@nxgt/material 3.11.0 → 3.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/README.md +6 -7
- package/dist/components/components.css +1 -0
- package/dist/components/ui/feedback/toast/toast-expressive.css +17 -0
- package/dist/components/ui/layout/app-shell/activity-backdrop-shapes.css +49 -0
- package/dist/components/ui/layout/app-shell/activity-backdrop.css +0 -17
- package/dist/components/ui/layout/sidebar/sidebar-tokens.css +7 -2
- package/dist/{components-1GK7YIhY.js → components-yx1t_5_P.js} +13981 -13660
- package/dist/components.js +3 -3
- package/dist/editor.js +1 -1
- package/dist/lib/components/ui/display/badge/badge.d.ts +7 -2
- package/dist/lib/components/ui/display/entity-header/entity-header.d.ts +9 -1
- package/dist/lib/components/ui/display/entity-header/entity-stats.d.ts +16 -0
- package/dist/lib/components/ui/display/entity-header/partials/entity-header-actions.d.ts +6 -0
- package/dist/lib/components/ui/display/entity-header/partials/entity-header-body.d.ts +14 -0
- package/dist/lib/components/ui/display/entity-header/partials/entity-header-icon.d.ts +6 -0
- package/dist/lib/components/ui/display/entity-header/partials/shown.d.ts +2 -0
- package/dist/lib/components/ui/display/list-tile/list-tile.d.ts +8 -1
- package/dist/lib/components/ui/display/list-tile/partials/list-tile-body.d.ts +2 -1
- package/dist/lib/components/ui/display/stat-card/index.d.ts +1 -1
- package/dist/lib/components/ui/display/stat-card/partials/stat-card-compact.d.ts +12 -0
- package/dist/lib/components/ui/display/stat-card/stat-card-classes.d.ts +3 -2
- package/dist/lib/components/ui/display/stat-card/stat-card.d.ts +2 -2
- package/dist/lib/components/ui/display/stat-card/types.d.ts +9 -0
- package/dist/lib/components/ui/feedback/alert/alert-variants.d.ts +18 -0
- package/dist/lib/components/ui/feedback/alert/alert.d.ts +2 -6
- package/dist/lib/components/ui/layout/app-shell/activity-column.d.ts +17 -0
- package/dist/lib/components/ui/layout/app-shell/activity-layout.d.ts +3 -106
- package/dist/lib/components/ui/layout/app-shell/activity-layout.types.d.ts +113 -0
- package/dist/lib/components/ui/layout/app-shell/app-breadcrumb.d.ts +2 -1
- package/dist/lib/components/ui/layout/app-shell/app-sidebar-icon-item.d.ts +13 -0
- package/dist/lib/components/ui/layout/app-shell/app-sidebar-label-item.d.ts +11 -0
- package/dist/lib/components/ui/layout/app-shell/keep-shapes-inside.d.ts +6 -0
- package/dist/lib/components/ui/layout/app-shell/observe-layout.d.ts +11 -0
- package/dist/lib/components/ui/layout/app-shell/shape-units.d.ts +16 -0
- package/dist/lib/components/ui/layout/app-shell/shapes-clear-content.d.ts +13 -0
- package/dist/lib/components/ui/layout/app-shell/types.d.ts +10 -0
- package/dist/lib/components/ui/layout/app-shell/use-keep-shapes-inside.d.ts +14 -0
- package/dist/lib/components/ui/layout/app-shell/watch-content.d.ts +19 -0
- package/dist/lib/components/ui/tones.d.ts +1 -1
- package/dist/main.js +3 -3
- package/dist/material.css +1 -1
- package/dist/{use-chat-composer-Bt2ULsv6.js → use-chat-composer-B0_STpQN.js} +1 -1
- package/docs/components/data-display.md +40 -9
- package/docs/components/feedback-and-status.md +12 -1
- package/docs/components/navigation-and-layout.md +3 -2
- package/docs/guides/app-shell.md +52 -8
- package/docs/guides/backgrounds.md +2 -0
- package/docs/guides/theming.md +4 -3
- package/docs/previews/data-display-badge.webp +0 -0
- package/docs/previews/data-display-entityheader.webp +0 -0
- package/docs/previews/data-display-listtile.webp +0 -0
- package/docs/previews/data-display-statcard.webp +0 -0
- package/docs/previews/feedback-status-banner.webp +0 -0
- package/docs/previews/navigation-layout-appshell-footer-icon.webp +0 -0
- package/docs/previews/navigation-layout-appshell-menu-badge.webp +0 -0
- package/docs/previews/navigation-layout-appshell.webp +0 -0
- package/docs/previews/navigation-layout-sidebar.webp +0 -0
- package/docs/roadmap.md +18 -0
- package/docs/troubleshooting.md +17 -2
- package/lib/components/components.css +1 -0
- package/lib/components/ui/feedback/toast/toast-expressive.css +17 -0
- package/lib/components/ui/layout/app-shell/activity-backdrop-shapes.css +49 -0
- package/lib/components/ui/layout/app-shell/activity-backdrop.css +0 -17
- package/lib/components/ui/layout/sidebar/sidebar-tokens.css +7 -2
- package/package.json +2 -1
|
@@ -6912,7 +6912,7 @@ function ov({ className: e, side: t = "bottom", sideOffset: r, align: i = "start
|
|
|
6912
6912
|
function sv({ className: e, ...t }) {
|
|
6913
6913
|
return /* @__PURE__ */ G(Zg, {
|
|
6914
6914
|
"data-slot": "combobox-list",
|
|
6915
|
-
className: n("grid gap-0.5 max-h-[min(calc(--spacing(96)---spacing(9)),calc(var(--available-height)---spacing(9)))] scroll-py-1 overflow-y-auto p-1 data-empty:p-0", e),
|
|
6915
|
+
className: n("grid auto-rows-max gap-0.5 max-h-[min(calc(--spacing(96)---spacing(9)),calc(var(--available-height)---spacing(9)))] scroll-py-1 overflow-y-auto p-1 data-empty:p-0", e),
|
|
6916
6916
|
...t
|
|
6917
6917
|
});
|
|
6918
6918
|
}
|
|
@@ -9,7 +9,7 @@ from `@nxgt/material/components`.
|
|
|
9
9
|
| <img src="../previews/data-display-avatar.webp" width="96" alt=""> | [Avatar](#avatar) | Image with a fallback and an optional online / offline dot |
|
|
10
10
|
| <img src="../previews/data-display-avatargroup.webp" width="96" alt=""> | [AvatarGroup](#avatargroup) | Overlapping avatars with a `+N` overflow |
|
|
11
11
|
| <img src="../previews/data-display-shape.webp" width="96" alt=""> | [Shape](#shape) | Material 3 Expressive's shapes as a backdrop, an icon container or a mask for a photo |
|
|
12
|
-
| <img src="../previews/data-display-badge.webp" width="96" alt=""> | [Badge](#badge) |
|
|
12
|
+
| <img src="../previews/data-display-badge.webp" width="96" alt=""> | [Badge](#badge) | Status pill, 20 px, in seven tones, soft or outlined |
|
|
13
13
|
| <img src="../previews/data-display-countbadge.webp" width="96" alt=""> | [CountBadge](#countbadge) | Count in the corner of its children |
|
|
14
14
|
| <img src="../previews/data-display-dayseparator.webp" width="96" alt=""> | [DaySeparator](#dayseparator) | "Today", "Yesterday" or the date between two hairlines |
|
|
15
15
|
| <img src="../previews/data-display-periodcards.webp" width="96" alt=""> | [PeriodCards](#periodcards) | Consecutive date ranges (terms, sprints) as cards, with a current marker and an optional link per card |
|
|
@@ -210,20 +210,25 @@ and no lazy loading: give `src` the size you need.
|
|
|
210
210
|
|
|
211
211
|

|
|
212
212
|
|
|
213
|
-
|
|
213
|
+
Status badge: a 20 px pill with the `label-sm` text, compact padding and no hover or pointer affordance. Use `soft` (a tonal fill from the tone tokens) for a state; `outlined` is a thin 1 px line at 40 % of the tone. Every tone stays at 4.5:1 or more in the light and dark themes and follows the theme presets.
|
|
214
214
|
|
|
215
215
|
```tsx
|
|
216
216
|
import { Badge } from '@nxgt/material/components';
|
|
217
217
|
|
|
218
|
-
<Badge variant="success">Paid</Badge>
|
|
218
|
+
<Badge variant="soft" tone="success">Paid</Badge>
|
|
219
|
+
<Badge variant="outlined" tone="error">Late</Badge>
|
|
220
|
+
<Badge variant="success">New</Badge>
|
|
219
221
|
```
|
|
220
222
|
|
|
221
223
|
| Prop | Type | Default | Effect |
|
|
222
224
|
| --- | --- | --- | --- |
|
|
223
|
-
| `variant` | `'default' \| 'secondary' \| 'error' \| 'success' \| 'info' \| 'warning' \| 'outlined'` | `'default'` |
|
|
225
|
+
| `variant` | `'default' \| 'secondary' \| 'error' \| 'success' \| 'info' \| 'warning' \| 'soft' \| 'outlined'` | `'default'` | the filled tones, `soft` (recommended for a state) or `outlined` |
|
|
226
|
+
| `tone` | the eight named tones, or a tone of your own declared in `AppTones` | `'neutral'` | the colour of a `soft` or `outlined` badge; ignored by the filled variants |
|
|
224
227
|
| `asChild` | `boolean` | `false` | renders its child (a link) with the badge styles |
|
|
225
228
|
|
|
226
|
-
|
|
229
|
+
A `Badge` link (`asChild`) no longer fades on hover: a badge is a status, not a button. Put a `Chip` or a `Button` where the user acts.
|
|
230
|
+
|
|
231
|
+
**Storybook:** `Data display/Badge` (Soft, SoftContrast) · **Vue:** `Badge` (without `soft` and `tone` yet)
|
|
227
232
|
|
|
228
233
|
---
|
|
229
234
|
|
|
@@ -428,12 +433,22 @@ import { StatCard } from '@nxgt/material/components';
|
|
|
428
433
|
| `tone` | the eight named tones, or a tone of your own declared in `AppTones` | — | the value is drawn in the tone's text colour and the icon sits on a chip of its container. Without one the card is as before |
|
|
429
434
|
| `variant` | `'outlined' \| 'tonal' \| 'filled'` | `'outlined'` | `tonal` paints the card with the tone's container and a fine outline in its text colour (3:1 against the page); `filled` paints it with the tone and draws everything on it in the on-colour. Both use `neutral` when there is no `tone`. Every text is 4.5:1 or more. In a [calm zone](../guides/theming.md#calm-zone), a `tonal` or `filled` card is the card's own surface with the tone's ring, and its icon chip a ring |
|
|
430
435
|
| `valueSize` | `'default' \| 'display'` | `'default'` | `display` draws the value larger (`headline-large`) |
|
|
436
|
+
| `size` | `'default' \| 'sm'` | `'default'` | `sm` is a compact tile for a band or a header (see below); it ignores `hint`, `delta` and `valueSize` |
|
|
431
437
|
|
|
432
438
|
```tsx
|
|
433
439
|
<StatCard tone="success" variant="tonal" valueSize="display" label="Revenue" value="€48,200" delta={12.4} icon={<CreditCard />} />
|
|
434
440
|
```
|
|
435
441
|
|
|
436
|
-
|
|
442
|
+
**Compact tile (`size="sm"`).** For figures in a header band: a tile 40 to 56 px tall that hugs its content, the optional icon chip, then the value (tabular figures) over its label. It takes the same `tone` and `variant` (`tonal`, `outlined`, `filled`); `hint`, `delta` and `valueSize` are ignored (nothing is drawn for them, and `loading` shows a skeleton in place of the value only). Tiles in a `flex flex-wrap gap-2` row sit side by side and wrap on a phone.
|
|
443
|
+
|
|
444
|
+
```tsx
|
|
445
|
+
<div className="flex flex-wrap gap-2">
|
|
446
|
+
<StatCard size="sm" variant="tonal" tone="primary" label="Students" value={22} />
|
|
447
|
+
<StatCard size="sm" variant="tonal" tone="tertiary" label="Classes" value={6} />
|
|
448
|
+
</div>
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
Parts: the root carries `data-slot="stat-card"`, `data-variant` and, when a tone applies, `data-tone` (the tone, or `neutral` on a `tonal` or `filled` card without one); the icon wrapper carries `data-slot="stat-card-icon"`. The compact tile adds `data-size="sm"`. The types `StatCardProps`, `StatCardVariant`, `StatCardValueSize` and `StatCardSize` are exported.
|
|
437
452
|
|
|
438
453
|
In `filled`, the delta and its trend arrow take the on-colour too (the arrow and the sign still say up or down). The outline's share is `--stat-card-ring-share` (`68%`, or `--nxgt-tone-ring-share` for every ring); it draws the tonal card's border, the filled card's edge in a calm zone and the hairline round the icon chip. Known limit: the dark theme's primary `filled` card has an edge of about 2.3:1 on a `Card` and 2.5:1 on the page (its text is 7:1); a `tonal` card avoids it. See [App tones](../guides/theming.md#app-tones-on-shape-fab-avatar-statcard-and-the-metric-cards).
|
|
439
454
|
|
|
@@ -618,10 +633,24 @@ import { Badge, EntityHeader } from '@nxgt/material/components';
|
|
|
618
633
|
| `title` | `ReactNode` | — | the `h1`; optional: without it no heading is rendered (see below) |
|
|
619
634
|
| `icon`, `status` | `ReactNode` | — | before and after the title; `icon` is decoration, shown in an `aria-hidden` badge, so put nothing meaningful or focusable there |
|
|
620
635
|
| `metadata` | `{ label: string; value: ReactNode }[]` | `[]` | facts under the title, a description list: a muted label beside its value, no colon; a link in a value is drawn in the primary text colour (`text-primary-text`), not the prose link's info blue, and `nxgt-plain-link` still opts out |
|
|
636
|
+
| `stats` | `EntityStat[]` | `[]` | figures (counts, a mean) as compact `size="sm"` [StatCard](#statcard) tiles in a wrapping row under the metadata; a `tone` paints a tile, `0` is a figure, an empty value drops the tile (see Stats below) |
|
|
621
637
|
| `actions` | `ResponsiveButtonProps[] \| ReactNode` | `[]` | an array becomes responsive buttons (icon-only on phones) |
|
|
622
638
|
|
|
623
639
|
The root carries `data-slot="entity-header"`; the metadata is a `dl` with `data-slot="entity-metadata"` (a muted `dt` label beside its `dd` value, no colon), and the actions sit in `data-slot="entity-header-actions"` (absent when there are none). Inside an EntityHeader that replaces the old `summary-data` slot and its `Label:` text, so update a selector or a test that read "Label: value".
|
|
624
640
|
|
|
641
|
+
**Stats.** Figures (counts, a mean) go in `stats`, shown as compact [StatCard](#statcard) tiles (`size="sm"`) in a wrapping row under the metadata; facts (dates, names) stay in `metadata`. A `tone` paints a tile (tonal), none leaves it outlined; `0` is a figure, an empty value drops the tile. The row is `data-slot="entity-stats"`, and the type `EntityStat` is exported.
|
|
642
|
+
|
|
643
|
+
```tsx
|
|
644
|
+
<EntityHeader
|
|
645
|
+
title="Class 6A"
|
|
646
|
+
metadata={[{ label: 'Year', value: '2026-2027' }]}
|
|
647
|
+
stats={[
|
|
648
|
+
{ label: 'Students', value: 24, tone: 'primary' },
|
|
649
|
+
{ label: 'Coefficient', value: 2 },
|
|
650
|
+
]}
|
|
651
|
+
/>
|
|
652
|
+
```
|
|
653
|
+
|
|
625
654
|
`title` is optional. A record page whose `h1` is the app bar's title (`handle.title`, `AppShellHandle.title`, see the [app shell guide](../guides/app-shell.md#the-pages-title-in-the-app-bar)) uses EntityHeader without it, so the page keeps a single heading:
|
|
626
655
|
|
|
627
656
|
```tsx
|
|
@@ -653,15 +682,17 @@ With a title, in a narrow container (under `@sm`) the order is title, metadata,
|
|
|
653
682
|
Row with leading / trailing slots, title and subtitle; becomes a link or button when interactive. `size="sm"` is the dense row.
|
|
654
683
|
|
|
655
684
|
```tsx
|
|
656
|
-
import { ChevronRight } from 'lucide-react';
|
|
657
685
|
import { ListTile } from '@nxgt/material/components';
|
|
658
686
|
|
|
659
|
-
<ListTile title="Notifications" subtitle="Email and push"
|
|
687
|
+
<ListTile title="Notifications" subtitle="Email and push" href="/settings/notifications" />
|
|
660
688
|
```
|
|
661
689
|
|
|
690
|
+
A tile with an `href` ends with a discreet chevron after `trailing`: decorative (`aria-hidden`, so the link's name stays its text) and flipped in right-to-left. `chevron={false}` opts out; `chevron` on a button tile (an `onClick` that navigates) opts in.
|
|
691
|
+
|
|
662
692
|
| Prop | Type | Default | Effect |
|
|
663
693
|
| --- | --- | --- | --- |
|
|
664
|
-
| `href` | `string` | — | renders an `<a
|
|
694
|
+
| `href` | `string` | — | renders an `<a>`, with a chevron by default |
|
|
695
|
+
| `chevron` | `boolean` | `true` with an `href`, else `false` | the decorative chevron after `trailing` |
|
|
665
696
|
| `linkComponent` | `AppShellLinkComponent` | plain `<a>` | the link element, as in the app shell: takes `href`, spreads every other prop, forwards `ref` |
|
|
666
697
|
| `onClick` | `MouseEventHandler` | — | renders a `<button>` |
|
|
667
698
|
| `selected` | `boolean` | `false` | highlighted row |
|
|
@@ -7,7 +7,7 @@ centre.
|
|
|
7
7
|
| | Component | What it does |
|
|
8
8
|
| --- | --- | --- |
|
|
9
9
|
| <img src="../previews/feedback-status-alert.webp" width="96" alt=""> | [Alert](#alert) | Callout with icon, title and description in seven tones |
|
|
10
|
-
| <img src="../previews/feedback-status-banner.webp" width="96" alt=""> | [Banner](#banner) | Dismissible banner with an action |
|
|
10
|
+
| <img src="../previews/feedback-status-banner.webp" width="96" alt=""> | [Banner](#banner) | Dismissible banner with an action, in the look of Alert |
|
|
11
11
|
| <img src="../previews/feedback-status-alertdialog.webp" width="96" alt=""> | [AlertDialog](#alertdialog) | Parts for a blocking confirmation dialog |
|
|
12
12
|
| <img src="../previews/feedback-status-confirmdialog.webp" width="96" alt=""> | [ConfirmDialog](#confirmdialog) | Confirm / cancel dialog, `ConfirmationDialog`, `CustomAlertDialog` |
|
|
13
13
|
| <img src="../previews/feedback-status-customdialog.webp" width="96" alt=""> | [CustomDialog](#customdialog) | Ready-made dialog with trigger, icon, title, body and footer |
|
|
@@ -63,6 +63,13 @@ The root carries `data-slot="alert"`, the title `data-slot="alert-title"` and th
|
|
|
63
63
|
|
|
64
64
|
Dismissible info / success / warning / error banner with an action.
|
|
65
65
|
|
|
66
|
+
A Banner looks like an [Alert](#alert): the same light tone surface, start
|
|
67
|
+
accent bar, tone-coloured icon, title and description typography and padding.
|
|
68
|
+
They are built on one shared variants module, so they cannot drift. What a
|
|
69
|
+
Banner adds is its role: page level (full width), an `action` and a dismiss
|
|
70
|
+
button. Before 3.12 it was a solid tinted fill with a full tone border and
|
|
71
|
+
rounded corners.
|
|
72
|
+
|
|
66
73
|
```tsx
|
|
67
74
|
import { Banner, Button } from '@nxgt/material/components';
|
|
68
75
|
|
|
@@ -754,6 +761,10 @@ longer than the toast (a URL) breaks at its edge instead of pushing the
|
|
|
754
761
|
action out. With a long message and an action, the action and the X go under
|
|
755
762
|
the text; the icon stays 40px and never squeezes the text.
|
|
756
763
|
|
|
764
|
+
In a collapsed stack only the front toast's action and X are reachable; the
|
|
765
|
+
ones behind it appear when the stack opens on hover or when focus is inside
|
|
766
|
+
the toaster.
|
|
767
|
+
|
|
757
768
|
The toast id defaults to the title, so two toasts with the same title
|
|
758
769
|
replace each other; pass `options: { id }` to show both. `Toaster` wraps
|
|
759
770
|
Sonner's and takes its props (`position`, `expand`, `richColors`…), all
|
|
@@ -7,7 +7,7 @@ documentation-site shell. Everything is imported from
|
|
|
7
7
|
|
|
8
8
|
| | Component | What it does |
|
|
9
9
|
| --- | --- | --- |
|
|
10
|
-
| <img src="../previews/navigation-layout-appshell.webp" width="96" alt=""> | [ActivityLayout](#activitylayout) | App shell: sidebar, header, breadcrumbs, titled page; a route's `handle.title` is the page's `h1` in the app bar (it labels `main`; `usePageTitleFocus()` focuses it after a navigation), `breadcrumbRoot` a text root crumb, `context` the tenant line atop the phone's menu sheet; `menuOpen` / `onMenuOpenChange` control the phone's menu sheet, which closes itself on a followed link or any location change |
|
|
10
|
+
| <img src="../previews/navigation-layout-appshell.webp" width="96" alt=""> | [ActivityLayout](#activitylayout) | App shell: sidebar, header, breadcrumbs, titled page; a route's `handle.title` is the page's `h1` in the app bar (it labels `main`; `usePageTitleFocus()` focuses it after a navigation), `breadcrumbRoot` a text root crumb, `breadcrumb={false}` no trail, `context` the tenant line atop the phone's menu sheet; `menuOpen` / `onMenuOpenChange` control the phone's menu sheet, which closes itself on a followed link or any location change |
|
|
11
11
|
| <img src="../previews/navigation-layout-accountmenu.webp" width="96" alt=""> | [AccountMenu](#accountmenu) | The app bar's avatar menu: the account, the app's links, Sign out |
|
|
12
12
|
| <img src="../previews/navigation-layout-authcard.webp" width="96" alt=""> | [AuthCard](#authcard) | Signed-out page frame: one `main`, a centred card, its title the `h1` |
|
|
13
13
|
| <img src="../previews/navigation-layout-sidebar.webp" width="96" alt=""> | [Sidebar](#sidebar) | Collapsible navigation sidebar |
|
|
@@ -64,7 +64,7 @@ phone's menu sheet pad clear of `env(safe-area-inset-*)`, as classes; see the
|
|
|
64
64
|
|
|
65
65
|
| Prop | Type | Default | Effect |
|
|
66
66
|
| --- | --- | --- | --- |
|
|
67
|
-
| `items` | `MenuItem[]` | — | `label` (already translated), `url`, `icon`, `footer`, `badge` (`true` a dot, a number a count up to "99+"), `badgeLabel` (see [Badges](../guides/app-shell.md#badges)) |
|
|
67
|
+
| `items` | `MenuItem[]` | — | `label` (already translated), `url`, `icon`, `footer`, `display` (`'icon'`: a round icon-only button, the label as tooltip and name; see the [app shell guide](../guides/app-shell.md)), `badge` (`true` a dot, a number a count up to "99+"), `badgeLabel` (see [Badges](../guides/app-shell.md#badges)) |
|
|
68
68
|
| `linkComponent` | `AppShellLinkComponent` | React Router `Link` | The link each menu entry renders, given `href`; see the [app shell guide](../guides/app-shell.md) |
|
|
69
69
|
| `logo`, `name` | `AppLogo` (`ReactNode \| string \| AppLogoImage`, `AppLogoImage` = `{ src, alt? }`), `string` | — | the app's logo alone in the collapsed rail, named by `name` (also its tooltip); logo and name side by side in the phone's menu sheet |
|
|
70
70
|
| `context` | `ReactNode` | — | a line at the top of the phone's menu sheet, above the brand: the current tenant's name; `name` stays the app's brand; `AppHeader` and `AppSidebar` take it too (see the [app shell guide](../guides/app-shell.md#the-brand-logo-and-name)) |
|
|
@@ -72,6 +72,7 @@ phone's menu sheet pad clear of `env(safe-area-inset-*)`, as classes; see the
|
|
|
72
72
|
| `avatar` | `ReactNode` | — | an image URL or a node, in the header |
|
|
73
73
|
| `headerTitle` | `ReactNode` | — | the header's leading title (the context: an organisation's name), after the phone's menu button, truncated when long; when it is cut off, its full text is the `title` tooltip (#629) |
|
|
74
74
|
| `handle.title` | `ReactNode \| (match: UIMatch) => ReactNode` | — | on a route: the page's title in the app bar, its one `h1`, in the first server HTML; `ActivityContent` leaves its own title out while it is set (see the [app shell guide](../guides/app-shell.md#the-pages-title-in-the-app-bar)) |
|
|
75
|
+
| `breadcrumb` | `boolean` | `true` | `false` hides the breadcrumb trail (an error page); it is also hidden when every matched `handle.breadcrumb` renders `null`; see the [app shell guide](../guides/app-shell.md#breadcrumbs) |
|
|
75
76
|
| `breadcrumbRoot` | `{ label: ReactNode; to: string }` | — | the breadcrumb's first crumb, a text link in place of the home icon (`AppBreadcrumb`'s `root`); see the [app shell guide](../guides/app-shell.md#breadcrumbs) |
|
|
76
77
|
| `headerActions` | `ReactNode` | — | header, before the avatar |
|
|
77
78
|
| `title`, `subtitle`, `actions` | `ReactNode` | — | `ActivityContent`'s header card; `title` is left out while a route's `handle.title` puts the page's title in the app bar |
|
package/docs/guides/app-shell.md
CHANGED
|
@@ -82,6 +82,7 @@ type MenuItem = {
|
|
|
82
82
|
url: string;
|
|
83
83
|
icon: IconName; // a symbol of the duotone sprite, see Getting started › Icons
|
|
84
84
|
footer?: boolean; // pin to the sidebar footer
|
|
85
|
+
display?: 'icon'; // a round, icon-only button; see below
|
|
85
86
|
badge?: number | boolean | ReactNode; // see Badges
|
|
86
87
|
badgeLabel?: string; // the badge's text in the link's name, already translated
|
|
87
88
|
};
|
|
@@ -103,6 +104,22 @@ type MenuItem = {
|
|
|
103
104
|
|
|
104
105
|
- **`footer: true` pins an entry to the footer.** Being last in the list does
|
|
105
106
|
not: appending an entry never moves another one out of the footer.
|
|
107
|
+
- **`display: 'icon'` draws the entry as a round, icon-only button.** Meant
|
|
108
|
+
for the footer's "Settings": in the collapsed rail, the expanded sidebar and
|
|
109
|
+
the phone's menu sheet alike, it is an `IconButton` (the Button family's
|
|
110
|
+
look, ghost, tonal when it is the active entry) with the label as its
|
|
111
|
+
tooltip and, visually hidden, its accessible name, `aria-current="page"`
|
|
112
|
+
when active, and a 44px target on a coarse pointer. The entries without it
|
|
113
|
+
keep their icon and label. An icon entry draws no `badge`.
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
{ label: t('menu.settings'), url: '/settings', icon: 'gear', footer: true, display: 'icon' }
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
- The active entry's icon and label are in primary on the rail: a darker
|
|
120
|
+
primary in light (85 % of its lightness, so the label reads 4.5:1 on its
|
|
121
|
+
pill for every theme preset) and a lavender of its hue in dark; see
|
|
122
|
+
[the sidebar tokens](./theming.md).
|
|
106
123
|
- The icons are drawn with `Icon type="duotone"`, so the duotone sprite must
|
|
107
124
|
be served ([getting started](../getting-started.md#icons)).
|
|
108
125
|
|
|
@@ -183,6 +200,7 @@ partitionMenu(items); // { content, footer }
|
|
|
183
200
|
| `avatar` | `ReactNode` | — | End of the header. A `string` is used as the image URL of an `Avatar`; anything else is rendered as-is |
|
|
184
201
|
| `linkComponent` | `AppShellLinkComponent` | React Router `Link` | The link each menu entry renders, given `href` (see below) |
|
|
185
202
|
| `headerTitle` | `ReactNode` | — | The header's leading title: the context the app is in (the active organisation's name), right after the phone's menu button, truncated when long; when it is cut off, its full text is the `title` tooltip (#629) |
|
|
203
|
+
| `breadcrumb` | `boolean` | `true` | `false` hides the breadcrumb trail, for an error page whose matched routes would show a misleading one (see [Breadcrumbs](#breadcrumbs)) |
|
|
186
204
|
| `breadcrumbRoot` | `AppBreadcrumbRoot` (`{ label: ReactNode; to: string }`) | — | The breadcrumb's first crumb, in place of the home icon: a text link to `to` (see [Breadcrumbs](#breadcrumbs)) |
|
|
187
205
|
| `headerActions` | `ReactNode` | — | In the header, before the avatar: notifications, help, `ThemeToggle` |
|
|
188
206
|
| `children` | `ReactNode` | — | The routed content, usually `<Outlet />` |
|
|
@@ -498,11 +516,23 @@ or an `NxgtTheme` zone around it; see [Themes](./theming.md#the-provider-and-zon
|
|
|
498
516
|
| `backdrop={<Background … />}` | that node, for this screen |
|
|
499
517
|
| `backdrop={false}` | no background and no blobs: a plain page (`data-plain` on the root) |
|
|
500
518
|
|
|
501
|
-
The theme's shapes (the motif and illustrated presets)
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
519
|
+
**Where the shapes go.** The theme's shapes (the motif and illustrated presets)
|
|
520
|
+
are drawn whole or not at all, and only in empty space. A shape that would cross the layer's edge,
|
|
521
|
+
or reach the app bar, the breadcrumb, the header card or an `EntityHeader`, is
|
|
522
|
+
not drawn (a permanent exclusion). A shape that lies over (or within 8px of) the page's text or
|
|
523
|
+
controls (inputs, buttons, links, images, and every line of text) is hidden: it
|
|
524
|
+
fades out in 150ms (none under `prefers-reduced-motion`) and fades back once
|
|
525
|
+
the content moves away, for instance when the page scrolls or a list changes.
|
|
526
|
+
Shapes are hidden, never moved. The check reads the content when it changes or
|
|
527
|
+
resizes and keeps the boxes, so a scroll only shifts them (one `requestAnimationFrame`-throttled
|
|
528
|
+
pass, well under a millisecond on a typical page; text past the first 6000 boxes is not collected, so a shape may be drawn over it). A moon, a planet or an eclipse counts as one shape: it is hidden whole, with its halo. Limits: a sticky element is measured where it is laid out, not where it sticks, and a late font or image, or an inner scroller, triggers a new check a moment later. Nothing shows until the first
|
|
529
|
+
check has run, so no shape flashes over the content. On a phone, where the
|
|
530
|
+
content fills the width, the shapes you see are the ones in the empty part of
|
|
531
|
+
the page, usually below the content. Full-width textures such as `waves` and the
|
|
532
|
+
gradient are not shapes and are not filtered. A `Background` passed as
|
|
533
|
+
`backdrop` follows the same rule; any other node is left as it is. The layer is
|
|
534
|
+
below the content in z-order (`-z-10` in the column's own stacking context), so
|
|
535
|
+
a dialog, a sheet or a sticky header is always above it.
|
|
506
536
|
|
|
507
537
|
`backdrop={false}` used to draw the built-in gradient, as if it were left out;
|
|
508
538
|
it now removes it. To keep the gradient, leave `backdrop` out. A theme's
|
|
@@ -522,7 +552,7 @@ to know the theme on the server (a cookie, passed to `defaultTheme`).
|
|
|
522
552
|
|
|
523
553
|
The shell draws the gradient with three blurred blobs behind its frame. To use
|
|
524
554
|
one of the 50 presets of `@nxgt/material/backgrounds` instead, pass it as
|
|
525
|
-
`backdrop
|
|
555
|
+
`backdrop` (behind the shell its shapes show only in empty space, see [Where the shapes go](#the-themes-background)):
|
|
526
556
|
|
|
527
557
|
```tsx
|
|
528
558
|
import { Outlet } from 'react-router';
|
|
@@ -559,7 +589,7 @@ export function Shell({ items }: { items: MenuItem[] }) {
|
|
|
559
589
|
`scroll="container"` alike.
|
|
560
590
|
- **The chrome is lightly translucent and frosted.** Under a `backdrop`, the
|
|
561
591
|
app bar, the breadcrumb (a pill as wide as its crumbs) and the tabs strip
|
|
562
|
-
show
|
|
592
|
+
show the backdrop faintly through their fill, over a 6px blur that keeps it from
|
|
563
593
|
competing with a text. The lists and tables (band and rows), the segmented
|
|
564
594
|
`ToggleGroup`, the list bar, the phone tiles and the cards stay opaque, so no
|
|
565
595
|
shape runs behind a row. The sidebar is frosted too (87%), but the layer is
|
|
@@ -797,7 +827,21 @@ to the router's root, then adds one crumb per matched route that has a
|
|
|
797
827
|
the trail. The last crumb is text (`aria-current="page"`); the others are
|
|
798
828
|
links, each inside its `<li>`. The home icon is itself the link, named "Home"
|
|
799
829
|
(`material.app-shell.home`, translated): `getByRole('link', { name: 'Home' })`.
|
|
800
|
-
The trail is hidden when no route has a breadcrumb.
|
|
830
|
+
The trail is hidden when no route has a breadcrumb. A crumb whose
|
|
831
|
+
`handle.breadcrumb` returns `null` (or `undefined`, `false`) is left out with
|
|
832
|
+
its separator, as `handle.title` accepts `null`, so the trail is hidden too
|
|
833
|
+
when every matched route returns `null`.
|
|
834
|
+
|
|
835
|
+
An error page rendered by `ActivityLayout` still matches the failed route, so
|
|
836
|
+
it would show its trail ("Home > Section" on a 404). Pass `breadcrumb={false}`
|
|
837
|
+
to render the layout without the trail:
|
|
838
|
+
|
|
839
|
+
```tsx
|
|
840
|
+
// the root route's errorElement
|
|
841
|
+
<ActivityLayout items={items} breadcrumb={false}>
|
|
842
|
+
<ActivityContent title="Page not found" />
|
|
843
|
+
</ActivityLayout>
|
|
844
|
+
```
|
|
801
845
|
|
|
802
846
|
A multi-tenant app whose root is the current tenant passes `breadcrumbRoot`
|
|
803
847
|
(`AppBreadcrumb`'s `root`) to replace the home icon with a text link,
|
|
@@ -181,6 +181,8 @@ of path data; `low-poly` is about 20 KB (210 nodes); `circuit` about 9 KB (90 no
|
|
|
181
181
|
|
|
182
182
|
<img src="../previews/backgrounds-motifs.webp" width="480" alt="The shapes preset behind a card">
|
|
183
183
|
|
|
184
|
+
**Behind an `ActivityLayout`** a preset's discrete shapes are hidden wherever they would lie over the page's text or controls, and fade back when the content moves away (see [Where the shapes go](./app-shell.md#the-themes-background)). A `Background` used on its own, on a page you lay out, draws all its shapes.
|
|
185
|
+
|
|
184
186
|
`shapes`, `confetti-grid`, `constellation`, `halftone`, `truchet`, `floating` and `scatter-icons` draw seeded, deterministic geometry from
|
|
185
187
|
the tone's three colours: the same `seed` gives the same composition on the server and in the
|
|
186
188
|
browser, and a new `seed` redraws it.
|
package/docs/guides/theming.md
CHANGED
|
@@ -578,15 +578,16 @@ rail and menu sheet) is drawn with three tokens, in light and in dark:
|
|
|
578
578
|
| --- | --- | --- | --- |
|
|
579
579
|
| `--sidebar-active` | `bg-sidebar-active` | primary at 15 % | primary at 30 % |
|
|
580
580
|
| `--sidebar-active-hover` | `bg-sidebar-active-hover` | primary at 20 % | primary at 35 % |
|
|
581
|
-
| `--sidebar-active-foreground` | `text-sidebar-active-foreground` |
|
|
581
|
+
| `--sidebar-active-foreground` | `text-sidebar-active-foreground` | `oklch(from var(--primary) calc(l * 0.85) c h)`, 4.5:1 or more on the pill for every theme preset | `oklch(from var(--primary) 0.72 0.15 h)`, 6.3:1 or more on the pill |
|
|
582
582
|
|
|
583
583
|
The pill is the icon's (collapsed rail) or the whole row (expanded), a pill in the collapsed rail and, in the expanded row, an `--nxgt-item-radius` (6px) shape that carries the 3px start bar every chosen row has, drawn in `--sidebar-active-foreground` (a `Highlight` bar in forced colours); the
|
|
584
584
|
foreground is the label and the icon, whose duotone layers both follow it (in
|
|
585
585
|
dark too: the icon of `AppSidebar` is the lifted lavender like its label). The
|
|
586
586
|
label is read on the pill (expanded) or on the sidebar (collapsed rail); the
|
|
587
587
|
icon, on the pill. In browsers without relative colour syntax (before Chrome
|
|
588
|
-
119, Safari 18, Firefox 128) the
|
|
589
|
-
itself, as the dark lift
|
|
588
|
+
119, Safari 18, Firefox 128) the foreground falls back to `--primary`
|
|
589
|
+
itself in light and dark, as both the light darkening and the dark lift need
|
|
590
|
+
`oklch(from …)`.
|
|
590
591
|
|
|
591
592
|
The defaults are derived where the entry is drawn, from the `--primary` it
|
|
592
593
|
sits under, so a `--primary` set on a wrapper recolours the rail inside it.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/docs/roadmap.md
CHANGED
|
@@ -308,6 +308,24 @@ version they shipped in, and planned entries may name the major they target.
|
|
|
308
308
|
|
|
309
309
|
## Shipped
|
|
310
310
|
|
|
311
|
+
- **Display polish** — `Badge` is a status badge: a 20 px pill with the
|
|
312
|
+
`label-sm` text and no hover, a `soft` variant (the tone's container under
|
|
313
|
+
the tone's text, 4.5:1) and a `tone` prop for `soft` and `outlined`, taking
|
|
314
|
+
your own tones too; `CountBadge` keeps its own size. `ListTile` with an
|
|
315
|
+
`href` ends with a discreet chevron (`chevron={false}` opts out). `StatCard`
|
|
316
|
+
gets a compact `size="sm"` tile and `EntityHeader` a `stats` prop that shows
|
|
317
|
+
figures as these tiles. `Banner` takes `Alert`'s look. A footer `MenuItem`
|
|
318
|
+
can be `display: 'icon'`, a round icon-only button with a tooltip, 44 px on
|
|
319
|
+
a touch screen; the rail's active label is drawn in a darker primary so it
|
|
320
|
+
reads 4.5:1 on every theme preset. `breadcrumb={false}` hides the trail, and
|
|
321
|
+
so does a trail whose crumbs all render `null` (#1475). `Combobox` rows are
|
|
322
|
+
touch-sized on a touch screen (#1474). The backdrop's shapes are hidden while
|
|
323
|
+
they lie over the page's text or controls, never moved (#1478). Nothing
|
|
324
|
+
changes in core. React only; the
|
|
325
|
+
Vue port is a `vue` issue. Ships in 3.12.0.
|
|
326
|
+
([Data display](components/data-display.md),
|
|
327
|
+
[App shell](guides/app-shell.md))
|
|
328
|
+
|
|
311
329
|
- **Buttons and the type scale in Material 3 Expressive** — `Typography` takes
|
|
312
330
|
the Material 3 scale (`display`, `headline`, `title`, `body`, `label`, each
|
|
313
331
|
`lg`, `md` and `sm`, plus `emphasized`) as Tailwind tokens: `text-title-md`
|
package/docs/troubleshooting.md
CHANGED
|
@@ -109,6 +109,7 @@ words in your error.
|
|
|
109
109
|
|
|
110
110
|
**Navigation**
|
|
111
111
|
|
|
112
|
+
- [A `ListTile` link shows two chevrons](#a-listtile-link-shows-two-chevrons)
|
|
112
113
|
- [A `ListTile` link reloads the whole page](#a-listtile-link-reloads-the-whole-page)
|
|
113
114
|
- [After a link in the phone menu, the focus goes to `<main>`, not to the page's title](#after-a-link-in-the-phone-menu-the-focus-goes-to-main-not-to-the-pages-title)
|
|
114
115
|
- [The phone menu needs two Escapes to close, or a tooltip shows over it](#the-phone-menu-needs-two-escapes-to-close-or-a-tooltip-shows-over-it)
|
|
@@ -979,8 +980,9 @@ your token against the page at 4.5:1:
|
|
|
979
980
|
<Button variant="tonal" color="error">Delete</Button>
|
|
980
981
|
```
|
|
981
982
|
|
|
982
|
-
A `Badge`
|
|
983
|
-
in its `className` puts the fade back: darken
|
|
983
|
+
A `Badge` no longer changes on hover; a hover fade of your own such as
|
|
984
|
+
`[a&]:hover:bg-error/90` in its `className` puts the fade back: darken
|
|
985
|
+
instead, as in
|
|
984
986
|
`[a&]:hover:bg-[color-mix(in_oklab,var(--color-error)_90%,black)]` (with
|
|
985
987
|
your badge's colour in place of `error`). A
|
|
986
988
|
subtitle colour of your own on a selected `ListTile` must reach 4.5:1 on
|
|
@@ -1974,6 +1976,19 @@ tree.
|
|
|
1974
1976
|
|
|
1975
1977
|
## Navigation
|
|
1976
1978
|
|
|
1979
|
+
### A `ListTile` link shows two chevrons
|
|
1980
|
+
|
|
1981
|
+
**When:** a `ListTile` with an `href` and a `trailing={<ChevronRight />}` of
|
|
1982
|
+
your own draws a second chevron since the tile adds one.
|
|
1983
|
+
**Why:** a link tile ends with a decorative chevron by default.
|
|
1984
|
+
**Fix:** drop your own, or keep it and pass `chevron={false}`:
|
|
1985
|
+
|
|
1986
|
+
```tsx
|
|
1987
|
+
<ListTile title="Notifications" href="/settings/notifications" />
|
|
1988
|
+
// or, with your own icon
|
|
1989
|
+
<ListTile title="Notifications" href="/settings/notifications" chevron={false} trailing={<MyArrow />} />
|
|
1990
|
+
```
|
|
1991
|
+
|
|
1977
1992
|
### A `ListTile` link reloads the whole page
|
|
1978
1993
|
|
|
1979
1994
|
**When:** a `ListTile` with an `href` is clicked inside a router app: the
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
/* ActivityLayout: the route transition (cross-fade, none under reduced motion). */
|
|
56
56
|
@import "./ui/layout/app-shell/route-transition.css";
|
|
57
57
|
@import "./ui/layout/app-shell/activity-backdrop.css";
|
|
58
|
+
@import "./ui/layout/app-shell/activity-backdrop-shapes.css";
|
|
58
59
|
/* DataTable cards and tiles: the item shell (hairline, tint, start bar, columns by the list's width). */
|
|
59
60
|
@import "./ui/collections/data-table/data-table-items.css";
|
|
60
61
|
@import "./ui/collections/data-table/data-table-items-compact.css";
|
|
@@ -107,6 +107,23 @@
|
|
|
107
107
|
gap: 8px;
|
|
108
108
|
margin-inline-start: auto;
|
|
109
109
|
}
|
|
110
|
+
/* A stacked toast behind the front one (`data-expanded="false"`,
|
|
111
|
+
* `data-front="false"`) sits under it, scaled and offset by a few pixels:
|
|
112
|
+
* its buttons are covered, so a tap lands on the front toast, and a covered,
|
|
113
|
+
* scaled close button fails axe's `target-size` (#1480). Its buttons are
|
|
114
|
+
* hidden until the stack opens (sonner opens it on hover, not on focus) or
|
|
115
|
+
* focus is inside the toaster, so a Tab from the front toast still reaches
|
|
116
|
+
* them. Sonner's own `opacity: 0` for these does not apply to our unstyled
|
|
117
|
+
* (`data-styled="false"`) toasts. */
|
|
118
|
+
[data-sonner-toast][data-expanded="false"][data-front="false"]
|
|
119
|
+
.nxgt-toast-buttons {
|
|
120
|
+
visibility: hidden;
|
|
121
|
+
}
|
|
122
|
+
[data-sonner-toaster]:focus-within
|
|
123
|
+
[data-sonner-toast][data-expanded="false"][data-front="false"]
|
|
124
|
+
.nxgt-toast-buttons {
|
|
125
|
+
visibility: visible;
|
|
126
|
+
}
|
|
110
127
|
.nxgt-toast-title {
|
|
111
128
|
color: var(--toast-fg);
|
|
112
129
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/* Which shapes of the backdrop show (observe-layout.ts, watch-content.ts).
|
|
2
|
+
* Plain CSS in `@layer components`, beside activity-backdrop.css. */
|
|
3
|
+
@layer components {
|
|
4
|
+
/* The theme's backdrop keeps its shapes whole (keep-shapes-inside.ts):
|
|
5
|
+
* the scene waits for the first pass, then a shape that would cross the
|
|
6
|
+
* layer's edge or reach the header band is hidden, never cut. */
|
|
7
|
+
[data-keep-inside]:not([data-checked]) .mo,
|
|
8
|
+
[data-slot="activity-layout-backdrop"]
|
|
9
|
+
[data-background]:not([data-checked])
|
|
10
|
+
.mo {
|
|
11
|
+
visibility: hidden;
|
|
12
|
+
}
|
|
13
|
+
[data-keep-inside] .mo > [data-outside],
|
|
14
|
+
[data-slot="activity-layout-backdrop"]
|
|
15
|
+
[data-background]
|
|
16
|
+
.mo
|
|
17
|
+
> [data-outside] {
|
|
18
|
+
visibility: hidden;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* A shape over the page's text or controls fades out and comes back once
|
|
22
|
+
* clear (watch-content.ts): it is hidden, never moved. A compound body (the
|
|
23
|
+
* moon, the planet, the eclipse: `.pt.mg`) is hidden whole, with its halo
|
|
24
|
+
* (`.pt.mh`). The scene stays invisible until the first check, so nothing
|
|
25
|
+
* flashes over the content. */
|
|
26
|
+
[data-keep-inside] .mo > *,
|
|
27
|
+
[data-keep-inside] .pt,
|
|
28
|
+
[data-slot="activity-layout-backdrop"] [data-background] .mo > *,
|
|
29
|
+
[data-slot="activity-layout-backdrop"] [data-background] .pt {
|
|
30
|
+
transition: opacity 150ms ease;
|
|
31
|
+
}
|
|
32
|
+
/* Important: a preset sets its own opacity on a body (`.md`, the dark
|
|
33
|
+
* moon's, a celestial body's); in a layer, `!important` beats every normal
|
|
34
|
+
* declaration of those. The 150 ms transition still runs. */
|
|
35
|
+
[data-keep-inside] .mo > [data-hidden],
|
|
36
|
+
[data-keep-inside] .pt[data-hidden],
|
|
37
|
+
[data-slot="activity-layout-backdrop"] [data-background] .mo > [data-hidden],
|
|
38
|
+
[data-slot="activity-layout-backdrop"] [data-background] .pt[data-hidden] {
|
|
39
|
+
opacity: 0 !important;
|
|
40
|
+
}
|
|
41
|
+
@media (prefers-reduced-motion: reduce) {
|
|
42
|
+
[data-keep-inside] .mo > *,
|
|
43
|
+
[data-keep-inside] .pt,
|
|
44
|
+
[data-slot="activity-layout-backdrop"] [data-background] .mo > *,
|
|
45
|
+
[data-slot="activity-layout-backdrop"] [data-background] .pt {
|
|
46
|
+
transition: none;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -77,23 +77,6 @@
|
|
|
77
77
|
display: none;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
/* The theme's backdrop keeps its shapes whole (use-keep-shapes-inside.ts):
|
|
81
|
-
* the scene waits for the first pass, then a shape that would cross the
|
|
82
|
-
* layer's edge or reach the header band is hidden, never cut. */
|
|
83
|
-
[data-keep-inside]:not([data-checked]) .mo,
|
|
84
|
-
[data-slot="activity-layout-backdrop"]
|
|
85
|
-
[data-background]:not([data-checked])
|
|
86
|
-
.mo {
|
|
87
|
-
visibility: hidden;
|
|
88
|
-
}
|
|
89
|
-
[data-keep-inside] .mo > [data-outside],
|
|
90
|
-
[data-slot="activity-layout-backdrop"]
|
|
91
|
-
[data-background]
|
|
92
|
-
.mo
|
|
93
|
-
> [data-outside] {
|
|
94
|
-
visibility: hidden;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
80
|
/* A preset fills the layer: the layer's size, not `100lvw x 100lvh`, which
|
|
98
81
|
* counts the scrollbar and a phone's toolbar. */
|
|
99
82
|
[data-slot="activity-layout-backdrop"] > [data-background] {
|
|
@@ -3,13 +3,18 @@
|
|
|
3
3
|
:root {
|
|
4
4
|
/* The active entry of the sidebar and the navigation rail: its tint, the
|
|
5
5
|
* tint hovered, the start bar (the label's colour), and the icon and label drawn on it. Translucent primary
|
|
6
|
-
* (these shares, 15 % and 20 % hovered); the label is primary
|
|
6
|
+
* (these shares, 15 % and 20 % hovered); the label is primary at 85 % of its lightness in light (a lavender of its hue in dark). The
|
|
7
7
|
* utilities (`bg-sidebar-active`...) derive them on the element, so they
|
|
8
8
|
* follow a `--primary` set below :root. To theme the rail set
|
|
9
9
|
* `--sidebar-active`, `--sidebar-active-hover` and
|
|
10
10
|
* `--sidebar-active-foreground` (all three, or the first two and the
|
|
11
11
|
* foreground); the derived colours are not declared here, on purpose. */
|
|
12
12
|
--nxgt-sidebar-active-share: 15%;
|
|
13
|
+
/* Light: primary itself reads 4.5:1 on the page but not on its own 15 % pill
|
|
14
|
+
* for every preset (corail 3.7:1), so the label is primary darkened to 85 %
|
|
15
|
+
* of its lightness (4.99:1 on the worst preset, hovered). The preset spec
|
|
16
|
+
* (sidebar-active-contrast.test.ts) reads this number back. */
|
|
17
|
+
--nxgt-sidebar-active-l: calc(l * 0.85);
|
|
13
18
|
/* The height of the app bar and of the rail's header from `md` up, so the
|
|
14
19
|
* rail's logo and the bar's title share one centre line. Phones keep the
|
|
15
20
|
* bar alone at this height; the menu sheet's header is unconstrained. */
|
|
@@ -24,7 +29,7 @@
|
|
|
24
29
|
/* The dark theme's primary is too dark to read on its surfaces (2.5:1), so
|
|
25
30
|
* the rail's label is a saturated lavender of its hue (5.3:1: lightness
|
|
26
31
|
* 0.72, chroma 0.15) on a denser pill (30 %, 35 % hovered). Light keeps the
|
|
27
|
-
*
|
|
32
|
+
* chroma of primary and 85 % of its lightness. */
|
|
28
33
|
--nxgt-sidebar-active-share: 30%;
|
|
29
34
|
--nxgt-sidebar-active-hover-share: 35%;
|
|
30
35
|
--nxgt-sidebar-active-l: 0.72;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxgt/material",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.12.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"email": "stevegires@gmail.com"
|
|
@@ -465,6 +465,7 @@
|
|
|
465
465
|
"@vitejs/plugin-react": "6.0.4",
|
|
466
466
|
"@vitest/browser-playwright": "4.1.10",
|
|
467
467
|
"ajv": "^8.20.0",
|
|
468
|
+
"axe-core": "4.12.1",
|
|
468
469
|
"globals": "^17.7.0",
|
|
469
470
|
"hls.js": "^1.7.3",
|
|
470
471
|
"leaflet": "^1.9.4",
|