@nxgt/material 3.21.0 → 3.23.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 +74 -0
- package/README.md +7 -5
- package/bin/nxgt-backdrop-sprite.js +15 -0
- package/dist/background-BtSZsTvY.js +286 -0
- package/dist/backgrounds/css-react/icons-quiet.css +5 -5
- package/dist/backgrounds/css-react/icons-tint.css +98 -0
- package/dist/backgrounds/css-react/icons.css +100 -33
- package/dist/backgrounds.js +1 -1
- package/dist/{celestial-scene-DaOsRFF3.js → celestial-scene-8Muu6f7O.js} +1 -1
- package/dist/components/background-shared.css +5 -0
- package/dist/components/components.css +4 -7
- package/dist/components/ui/fields/attributes/attributes.css +3 -0
- package/dist/components/ui/layout/app-shell/activity-backdrop-shapes.css +1 -1
- package/dist/components/ui/layout/auth-card/auth-card-backdrop.css +30 -0
- package/dist/{components-CbD5WNVO.js → components-BaUx5Nax.js} +13693 -13611
- package/dist/components.js +16 -16
- package/dist/editor.js +1 -1
- package/dist/{empty-state-CJh9f2pc.js → empty-state-lj7vjYWG.js} +1 -1
- package/dist/icon-sprites-CaYXzOL4.js +46 -0
- package/dist/icons-scene-M4HBeN2c.js +54 -0
- package/dist/{illustration-BmQmvW_4.js → illustration-B4_W-OUS.js} +2 -2
- package/dist/illustrations.js +1 -1
- package/dist/lib/backgrounds/background/partials/icons-check.d.ts +2 -2
- package/dist/lib/backgrounds/background/partials/icons-keys.d.ts +21 -1
- package/dist/lib/backgrounds/background/partials/icons-scene.d.ts +15 -6
- package/dist/lib/backgrounds/background/partials/use-icons-scene.d.ts +13 -8
- package/dist/lib/backgrounds/background/types.d.ts +26 -12
- package/dist/lib/backgrounds/index.d.ts +1 -1
- package/dist/lib/components/ui/collections/data-table/partials/data-table-page-control.d.ts +7 -2
- package/dist/lib/components/ui/collections/data-table/partials/data-table-toolbar.d.ts +6 -2
- package/dist/lib/components/ui/collections/data-table/types.d.ts +4 -0
- package/dist/lib/components/ui/collections/data-table/use-pager-focus.d.ts +30 -0
- package/dist/lib/components/ui/collections/server-table/partials/server-table-band-classes.d.ts +6 -0
- package/dist/lib/components/ui/collections/server-table/partials/server-table-band.d.ts +3 -1
- package/dist/lib/components/ui/collections/server-table/types.d.ts +3 -0
- package/dist/lib/components/ui/layout/app-shell/keep-shapes-inside.d.ts +7 -2
- package/dist/lib/components/ui/layout/app-shell/shapes-clear-content.d.ts +10 -3
- package/dist/lib/components/ui/layout/app-shell/theme-backdrop.d.ts +3 -1
- package/dist/lib/components/ui/layout/app-shell/use-keep-shapes-inside.d.ts +3 -3
- package/dist/lib/components/ui/layout/app-shell/watch-content.d.ts +28 -8
- package/dist/lib/components/ui/layout/auth-card/auth-card-backdrop.d.ts +23 -0
- package/dist/lib/components/ui/layout/auth-card/auth-card.d.ts +15 -1
- package/dist/lib/components/ui/primitives/icon/icon-sprites.d.ts +40 -1
- package/dist/lib/themes/define-theme.d.ts +8 -0
- package/dist/lib/themes/index.d.ts +2 -1
- package/dist/lib/vite/index.d.ts +1 -1
- package/dist/main.js +28 -28
- package/dist/material.css +1 -1
- package/dist/pdf-reader.js +1 -1
- package/dist/{public-DWuBn4t_.js → public-DAF5SCWH.js} +1 -1
- package/dist/{scene-options-CCy3V7XS.js → scene-options-2zNF3ZkQ.js} +4 -2
- package/dist/theme-picker.js +3 -3
- package/dist/themes.js +9 -4
- package/dist/{use-chat-composer-BBFuKNqC.js → use-chat-composer-B0ze1cU9.js} +3 -3
- package/docs/components/data-table-filter-and-lists.md +24 -2
- package/docs/components/navigation-and-layout.md +22 -0
- package/docs/components/primitives.md +21 -0
- package/docs/guides/app-shell.md +9 -8
- package/docs/guides/backgrounds.md +214 -16
- package/docs/guides/icon-motifs.md +30 -12
- package/docs/guides/themes.md +25 -4
- package/docs/guides/theming.md +7 -0
- package/docs/guides/vite-plugin.md +26 -0
- package/docs/previews/backgrounds-background-from-theme.webp +0 -0
- package/docs/previews/backgrounds-icons.webp +0 -0
- package/docs/previews/backgrounds-matrix.webp +0 -0
- package/docs/previews/foundations-themes-icon-motifs.webp +0 -0
- package/docs/roadmap.md +6 -0
- package/docs/troubleshooting.md +65 -3
- package/lib/backgrounds/css-react/icons-quiet.css +5 -5
- package/lib/backgrounds/css-react/icons-tint.css +98 -0
- package/lib/backgrounds/css-react/icons.css +100 -33
- package/lib/components/background-shared.css +5 -0
- package/lib/components/components.css +4 -7
- package/lib/components/ui/fields/attributes/attributes.css +3 -0
- package/lib/components/ui/layout/app-shell/activity-backdrop-shapes.css +1 -1
- package/lib/components/ui/layout/auth-card/auth-card-backdrop.css +30 -0
- package/package.json +8 -3
- package/dist/background-Ce7M5V42.js +0 -250
- package/dist/icon-sprites-FxHyqyhg.js +0 -30
- package/dist/icons-scene-BpIauJ5d.js +0 -27
package/dist/pdf-reader.js
CHANGED
|
@@ -17,7 +17,7 @@ import { n as xe, t as Se } from "./toggle-group-BMJNcFAC.js";
|
|
|
17
17
|
import { o as Ce } from "./escape-owner-C4W_sOAU.js";
|
|
18
18
|
import { n as we } from "./use-slice-reducer-BGkeGtML.js";
|
|
19
19
|
import { r as Te } from "./use-csp-nonce-ZKs9k-ov.js";
|
|
20
|
-
import { n as Ee, t as De } from "./empty-state-
|
|
20
|
+
import { n as Ee, t as De } from "./empty-state-lj7vjYWG.js";
|
|
21
21
|
import { t as Oe } from "./client-only.utils-3nHNzCki.js";
|
|
22
22
|
import { t as ke } from "./use-scroll-away-PQRFZkT2.js";
|
|
23
23
|
import { ArrowLeft as Ae, BookOpenText as je, ChevronDown as Me, ChevronLeft as Ne, ChevronRight as Pe, ChevronUp as Fe, Download as Ie, Eraser as Le, FileText as Re, FileWarning as ze, FileX2 as Be, Highlighter as Ve, KeyRound as He, LayoutGrid as Ue, List as We, Loader2 as Ge, Maximize as Ke, MessageSquareText as qe, Minimize as Je, MoreVertical as Ye, MousePointer2 as Xe, MoveHorizontal as Ze, PanelLeft as Qe, Pen as $e, PencilLine as et, Printer as tt, RotateCw as nt, Search as rt, StickyNote as it, Type as at, Underline as ot, Undo2 as st, Volume2 as ct, X as lt, XIcon as ut, ZoomIn as dt, ZoomOut as ft } from "lucide-react";
|
|
@@ -3,11 +3,13 @@ import { BODY_DEFAULTS as e } from "@nxgt/material-core/backgrounds";
|
|
|
3
3
|
var t = Object.keys({
|
|
4
4
|
shapes: 1,
|
|
5
5
|
density: 1,
|
|
6
|
+
layout: 1,
|
|
6
7
|
size: 1,
|
|
7
8
|
rotate: 1,
|
|
8
9
|
shapeFill: 1,
|
|
9
|
-
opacitySpread: 1
|
|
10
|
-
|
|
10
|
+
opacitySpread: 1,
|
|
11
|
+
icons: 1
|
|
12
|
+
}).filter((e) => e !== "shapes" && e !== "icons");
|
|
11
13
|
function n(e) {
|
|
12
14
|
return t.map((t) => e?.[t]);
|
|
13
15
|
}
|
package/dist/theme-picker.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as e } from "./utils-BLfntITJ.js";
|
|
2
2
|
import { a as t, i as n, n as r, o as i, r as a, t as o } from "./bubble-input-B7BVd9fX.js";
|
|
3
3
|
import { t as s } from "./use-material-translation-BFQODuQz.js";
|
|
4
|
-
import {
|
|
4
|
+
import { a as c, r as l } from "./icon-sprites-CaYXzOL4.js";
|
|
5
5
|
import { t as u } from "./use-theme-DMcyJ52N.js";
|
|
6
6
|
import { a as d, r as f } from "./theme-context-Cofk6vgs.js";
|
|
7
7
|
import { t as p } from "./theme-toggle-mQzIR1hw.js";
|
|
@@ -37,7 +37,7 @@ function D() {
|
|
|
37
37
|
var O = 4;
|
|
38
38
|
function k(e, t, n = "pro") {
|
|
39
39
|
try {
|
|
40
|
-
let r = S(e.iconStyle), i = e.icons ?? "school";
|
|
40
|
+
let r = S([e.iconStyle ?? []].flat().map((e) => typeof e == "object" && "style" in e ? e.style : e)), i = e.icons ?? "school";
|
|
41
41
|
return C(i), (typeof i == "string" ? b[i] : i).slice(0, O).map((e, i) => {
|
|
42
42
|
let [a] = C([e], r[i % r.length]), o = a;
|
|
43
43
|
return w(n === "free" ? T(o) : o, t);
|
|
@@ -50,7 +50,7 @@ function k(e, t, n = "pro") {
|
|
|
50
50
|
//#endregion
|
|
51
51
|
//#region lib/theme-picker/partials/mini-background.tsx
|
|
52
52
|
function A({ background: e }) {
|
|
53
|
-
let t = e ? e.preset : "none", n =
|
|
53
|
+
let t = e ? e.preset : "none", n = c(), r = l(), i = e && t === "icons" ? k(e, n, r) : [];
|
|
54
54
|
return /* @__PURE__ */ v("span", {
|
|
55
55
|
"aria-hidden": !0,
|
|
56
56
|
"data-slot": "theme-tile-background",
|
package/dist/themes.js
CHANGED
|
@@ -9,10 +9,15 @@ import { jsx as y } from "react/jsx-runtime";
|
|
|
9
9
|
import { THEME_BOOT_SCRIPT as b, themeBootScript as x } from "@nxgt/material-core/theme/boot";
|
|
10
10
|
import { ThemeDefinitionError as S, defineTheme as C } from "@nxgt/material-core/theme/define";
|
|
11
11
|
import { THEME_PRESETS as w, THEME_PRESETS as T, ambre as E, ardoise as D, bleumono as O, bluemoon as k, corail as A, foret as j, lavande as M, menthe as N, ocean as P, pink as F, prune as I, sable as L, slate as R, teal as z } from "@nxgt/material-core/theme/presets";
|
|
12
|
+
//#region lib/themes/define-theme.ts
|
|
13
|
+
function B(t) {
|
|
14
|
+
return C(t, { warn: e() ? void 0 : (e) => console.warn(e) });
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
12
17
|
//#region lib/themes/nxgt-theme-provider.tsx
|
|
13
|
-
var
|
|
14
|
-
function
|
|
15
|
-
let S = v(c.subscribe, c.getSnapshot, c.getServerSnapshot), C =
|
|
18
|
+
var V = () => () => {}, H = () => v(V, () => !1, () => !0);
|
|
19
|
+
function U({ themes: l = T, defaultTheme: m = o, defaultMode: b, children: x }) {
|
|
20
|
+
let S = v(c.subscribe, c.getSnapshot, c.getServerSnapshot), C = H(), w = _(() => l.map((e) => e.name), [l]);
|
|
16
21
|
!e() && !w.includes(m) && f(`defaultTheme "${m}" is not in themes (${w.join(", ")}).`, "NxgtThemeProvider");
|
|
17
22
|
let E = s(S, w, m), D = l.find((e) => e.name === E), O = b ?? D?.colorScheme ?? "system";
|
|
18
23
|
g(() => c.claim({
|
|
@@ -39,4 +44,4 @@ function H({ themes: l = T, defaultTheme: m = o, defaultMode: b, children: x })
|
|
|
39
44
|
});
|
|
40
45
|
}
|
|
41
46
|
//#endregion
|
|
42
|
-
export { m as NxgtTheme,
|
|
47
|
+
export { m as NxgtTheme, U as NxgtThemeProvider, b as THEME_BOOT_SCRIPT, w as THEME_PRESETS, S as ThemeDefinitionError, E as ambre, D as ardoise, O as bleumono, k as bluemoon, A as corail, B as defineTheme, j as foret, M as lavande, N as menthe, P as ocean, F as pink, I as prune, L as sable, R as slate, z as teal, x as themeBootScript, l as useThemeContext };
|
|
@@ -23,7 +23,7 @@ import { t as Mt } from "./spinner-B7HCQMFr.js";
|
|
|
23
23
|
import { a as Nt } from "./button-tones-BwuVvAmd.js";
|
|
24
24
|
import { a as Pt, i as Ft, n as It, r as Lt, t as Rt } from "./button-rdIhY2BT.js";
|
|
25
25
|
import { t as R } from "./icon-button-ifTpXt0A.js";
|
|
26
|
-
import {
|
|
26
|
+
import { a as zt, r as Bt } from "./icon-sprites-CaYXzOL4.js";
|
|
27
27
|
import { t as Vt } from "./use-ssr-media-query-C6M5m8sf.js";
|
|
28
28
|
import { n as Ht, t as Ut } from "./util-4heV2LPI.js";
|
|
29
29
|
import { t as Wt } from "./useEvent-2fKq-6w8.js";
|
|
@@ -38,7 +38,7 @@ import { t as sn } from "./compose-refs-DZ0iIVF-.js";
|
|
|
38
38
|
import { n as cn, r as ln, t as un } from "./chip-CEau88ZV.js";
|
|
39
39
|
import { n as dn, t as fn } from "./icon-badge-CiWXvKCs.js";
|
|
40
40
|
import { r as pn } from "./use-csp-nonce-ZKs9k-ov.js";
|
|
41
|
-
import { t as mn } from "./empty-state-
|
|
41
|
+
import { t as mn } from "./empty-state-lj7vjYWG.js";
|
|
42
42
|
import { t as hn } from "./skeleton-DSdtr2qc.js";
|
|
43
43
|
import { H as gn, V as _n, X as vn, bt as yn, it as bn, lt as xn, n as Sn, t as Cn, xt as wn, yt as Tn } from "./dist-moutbfbd.js";
|
|
44
44
|
import { t as En } from "./markdown-Dw_uH7j7.js";
|
|
@@ -1140,7 +1140,7 @@ function pu({ items: e, variant: t, tone: n, trigger: r, onCloseAutoFocus: i })
|
|
|
1140
1140
|
//#endregion
|
|
1141
1141
|
//#region lib/components/ui/primitives/icon/icon.tsx
|
|
1142
1142
|
function mu({ name: e, family: t = "classic", type: r = "duotone", className: i, ...a }) {
|
|
1143
|
-
let o =
|
|
1143
|
+
let o = zt(), s = Bt(), c = $a(Nt) && a["aria-label"] === void 0 && a["aria-labelledby"] === void 0;
|
|
1144
1144
|
return /* @__PURE__ */ G("svg", {
|
|
1145
1145
|
"data-slot": "icon",
|
|
1146
1146
|
role: c ? void 0 : "img",
|
|
@@ -122,6 +122,7 @@ table enables, for typing your own column defs.
|
|
|
122
122
|
| `getRowHref` | `(row) => string \| undefined` | — | the first data cell links to the row's page; a click elsewhere on the row follows it |
|
|
123
123
|
| `onRowClick` | `(row, event) => void` | — | a click on the row (not on a control); alone (without `getRowHref`), it makes the row focusable and Enter calls it |
|
|
124
124
|
| `linkComponent` | `ComponentType<{ href, className?, children?, rel?, 'aria-label'?, onClick? }>` | `<a>` | the link for `getRowHref` and the page links: wrap your router's. It must forward `className` and `ref` (the past-end page's "Back to the first page" is a `Button asChild` over it) |
|
|
125
|
+
| `reserveActions` | `boolean` | `true` | with `actions` and a `select` column: keep the toolbar's 68px band while nothing is selected, so the bar does not push the rows down; `false` keeps none and the bar arrives on the first selection (`ServerTable` passes `false`) |
|
|
125
126
|
| `focusable` | `boolean` | `true` with `actions`, else `false` | the `<table>` (or the list of cards or tiles) takes the focus by script (`tabindex="-1"`, never a tab stop): where the focus goes once the control that had it is gone. When the selection is cleared and the focused control went with it (a bulk action's button), the table takes the focus instead of the page's body ([focus after a bulk action](#focus-after-a-bulk-action)); `false` turns both off |
|
|
126
127
|
| `tableRef` | `Ref<HTMLTableElement>` | — | the `<table>`, to focus it yourself (with `focusable`); `controls.focusTable()` does the same from a toolbar slot. It stays `null` while the layout is a list (`cards`, `tiles`, or the phone side of `{ phone, wide }`): use `focusRef` |
|
|
127
128
|
| `focusRef` | `Ref<HTMLElement>` | — | the element `focusable` moves the focus to, whichever the layout: the `<table>`, or the list of cards |
|
|
@@ -273,6 +274,18 @@ wide screen; on a phone the extra lines still push them down).
|
|
|
273
274
|
/>
|
|
274
275
|
```
|
|
275
276
|
|
|
277
|
+
### Focus while paging
|
|
278
|
+
|
|
279
|
+
A page control that has the focus keeps it while the next page loads: a link
|
|
280
|
+
(or a button) stays the same element, `aria-disabled`, dimmed, and a click on
|
|
281
|
+
it does nothing, instead of being swapped for a disabled button (which drops the
|
|
282
|
+
focus to `<body>`, #1609). When a control really goes (Next on the last page),
|
|
283
|
+
the focus moves to its counterpart, or to the pagination `<nav>`
|
|
284
|
+
(`tabindex="-1"`, never a tab stop) when neither is left. A focus or a pointer
|
|
285
|
+
press elsewhere is never pulled back. The footer's selected count is a polite
|
|
286
|
+
status, so the first selection is announced ("1 row selected") when its bar
|
|
287
|
+
shows up.
|
|
288
|
+
|
|
276
289
|
### Focus after a bulk action
|
|
277
290
|
|
|
278
291
|
When a bulk action clears the selection, its buttons unmount and the focus
|
|
@@ -387,7 +400,10 @@ same minimum height once they do — so the "Delete selected" that appears on
|
|
|
387
400
|
the first selection does not push the tapped row down. The reserved height
|
|
388
401
|
is one row of default-size buttons (`h-9`); taller or wrapping actions still
|
|
389
402
|
grow the toolbar. Without the `select` column, or without `actions`, no band
|
|
390
|
-
is kept above the table.
|
|
403
|
+
is kept above the table. `reserveActions={false}` keeps none either: the bar
|
|
404
|
+
arrives on the first selection with a short grow (none under reduced motion),
|
|
405
|
+
and the rows move down by its height. `ServerTable` passes `false`, so its
|
|
406
|
+
bulk bar leaves no empty row under the band while nothing is selected.
|
|
391
407
|
|
|
392
408
|
```tsx
|
|
393
409
|
<DataTable
|
|
@@ -868,7 +884,8 @@ Its props are `ServerTableProps<T>`; `filter` is a `ServerTableFilter`,
|
|
|
868
884
|
| `description` | `ReactNode` | — | a muted line under the `title`, or alone above the band's controls without one; the `actions` stay at the band's end |
|
|
869
885
|
| `actions` | `ReactNode` | — | the page's actions, at the band's end |
|
|
870
886
|
| `bandDensity` | `'comfortable' \| 'compact'` | `'comfortable'` | `'compact'` halves the room above the band (16px to 8px) and under it (12px to 6px), for a list under a sticky app bar and a breadcrumb; the controls keep their size (the Filter's own `density` is the controls') |
|
|
871
|
-
| `pageControls` | `'labelled' \| 'icon'` | `'labelled'` | the footer's Previous / Next: `'icon'` is icon-only, with a tooltip and the names "Previous page" / "Next page" (`DataTable`'s default is unchanged) |
|
|
887
|
+
| `pageControls` | `'labelled' \| 'icon'` | `'labelled'` | the footer's Previous / Next: `'icon'` is icon-only, with a tooltip and the names "Previous page" / "Next page" (`DataTable`'s default is unchanged); on a coarse pointer the icon controls are 44x44 boxes (still painted 32px) |
|
|
888
|
+
| `bandActions` | `'stacked' \| 'inline'` | `'stacked'` | with `filter`, where `actions` sit below 768px: `'stacked'` gives them a row above the search; `'inline'` keeps them on the search's row (the search takes the room left), for a lone icon action such as a "+". An action wider than 3.5rem wraps under the search. From 768px both are the same |
|
|
872
889
|
| `filterRef` | `Ref<ServerTableFilterHandle>` | — | with `filter`: `filterRef.current.focus()` focuses the Filter's first control, the search, else the first quick filter, "Add filter" or "Filters (N)" the width shows |
|
|
873
890
|
| `sorting` | `createSorting(…)` | — | headers sorted from the URL |
|
|
874
891
|
| `filters` | `tableFilters(model)` | — | without `filter`: "nothing matches" and its clear |
|
|
@@ -883,6 +900,11 @@ Its props are `ServerTableProps<T>`; `filter` is a `ServerTableFilter`,
|
|
|
883
900
|
| `resultsLabel` | `(count) => string` | "N results" | without `filter`: what the list's region says. The page past the last has its own words: `material.server-table.past-end`, `past-end-hint`, `first-page` and `past-end-status` |
|
|
884
901
|
| `className` | `string` | — | on the list's surface |
|
|
885
902
|
|
|
903
|
+
```tsx
|
|
904
|
+
// A lone icon action stays on the search's row on a phone.
|
|
905
|
+
<ServerTable {...props} filter={{ model }} bandActions="inline" actions={<IconButton aria-label="New" tooltip="New"><Icon name="plus" /></IconButton>} />
|
|
906
|
+
```
|
|
907
|
+
|
|
886
908
|
**Where the focus goes.** After "Clear" on an empty page, or the last chip
|
|
887
909
|
removed, the focus returns to the Filter's search; a bar without one falls to
|
|
888
910
|
its first shown control (a quick filter, "Add filter", or "Filters (N)" on a
|
|
@@ -205,6 +205,28 @@ import { AuthCard, Button, EmailField, LanguageSwitcher, PasswordField } from '@
|
|
|
205
205
|
| `brand`, `toolbar` | `ReactNode` | — | the row above the card, left and right; it renders only when one is given |
|
|
206
206
|
| `banner` | `ReactNode` | — | above that row (an update notice) |
|
|
207
207
|
| `footer` | `ReactNode` | — | under the card |
|
|
208
|
+
| `backdrop` | `ReactNode \| false` | the theme's background | what is drawn behind the page: a `Background` preset or any node; `false` for none. Without a theme provider nothing is drawn |
|
|
209
|
+
| `keepClear` | `boolean` | `true` | glyphs (shapes or icons) of the backdrop that would meet the brand row, the card or the page's surfaces, text or controls are dropped (never moved); `false` lets them run under the card |
|
|
210
|
+
|
|
211
|
+
```tsx
|
|
212
|
+
import { Background } from '@nxgt/material/backgrounds';
|
|
213
|
+
|
|
214
|
+
<AuthCard title="Sign in" backdrop={<Background preset="shapes" />}>
|
|
215
|
+
{form}
|
|
216
|
+
</AuthCard>
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The backdrop sits in an absolute layer (`data-slot="auth-card-backdrop"`)
|
|
220
|
+
behind the content: `aria-hidden`, `inert`, no pointer events, so it is
|
|
221
|
+
decoration only and never takes a click meant for the form. Under a theme
|
|
222
|
+
provider it is the theme's background (Lavande's gradient included), loaded
|
|
223
|
+
on demand like `ActivityLayout`'s. The keep-clear pass is the one
|
|
224
|
+
`ActivityLayout` uses: after layout, and when the page resizes or changes, a
|
|
225
|
+
glyph (a shape or an icon) whose box meets the card, the brand block or a
|
|
226
|
+
surface, a text or a control of the page (8 px margin) is dropped with a short
|
|
227
|
+
fade, a glyph crossing the layer's edge too; none is cut or moved. Where `ResizeObserver` is missing the scene
|
|
228
|
+
is drawn as it is. The server renders the layer and no measurement, so there
|
|
229
|
+
is no hydration mismatch. Under `prefers-reduced-motion` the fade is off.
|
|
208
230
|
|
|
209
231
|
A non-error notice goes into a `role="status"` region that is always
|
|
210
232
|
rendered (zero height while empty), so a screen reader announces a notice
|
|
@@ -504,6 +504,27 @@ name to its Free twin (`chevrons-left` to `angles-left`). `useIconEdition()` rea
|
|
|
504
504
|
// → /static/icons/solid.svg#bell
|
|
505
505
|
```
|
|
506
506
|
|
|
507
|
+
`IconSpritesProvider` also takes `backdropSprite`, the reduced sprite that only `Background` reads
|
|
508
|
+
(`nxgt-backdrop-sprite` or `nxgtMaterial({ backdropSprite })` builds it; see
|
|
509
|
+
[the backgrounds guide](../guides/backgrounds.md#a-reduced-sprite-for-the-backdrop)). It has two forms, and a nested
|
|
510
|
+
provider without one keeps its parent's. `useBackdropSprite()` returns its `href`, or `undefined` when none is set. A
|
|
511
|
+
path that is not root-relative or does not end in `.svg` throws `IconRefError` (`base`).
|
|
512
|
+
|
|
513
|
+
```tsx
|
|
514
|
+
// the object form: the sprite's styles count as served
|
|
515
|
+
<IconSpritesProvider
|
|
516
|
+
base="/static/icons/"
|
|
517
|
+
backdropSprite={{ href: '/sprites/backdrop.svg', styles: ['light', 'thin'] }}
|
|
518
|
+
>
|
|
519
|
+
<Background preset="icons" />
|
|
520
|
+
</IconSpritesProvider>;
|
|
521
|
+
|
|
522
|
+
// the string form: served styles still come from servedStyles or the edition
|
|
523
|
+
<IconSpritesProvider base="/static/icons/" edition="free" backdropSprite="/sprites/backdrop.svg">
|
|
524
|
+
<Background preset="icons" />
|
|
525
|
+
</IconSpritesProvider>;
|
|
526
|
+
```
|
|
527
|
+
|
|
507
528
|
**Storybook:** `Primitives/Icon` · **Vue:** `Icon`
|
|
508
529
|
|
|
509
530
|
---
|
package/docs/guides/app-shell.md
CHANGED
|
@@ -516,14 +516,15 @@ or an `NxgtTheme` zone around it; see [Themes](./theming.md#the-provider-and-zon
|
|
|
516
516
|
| `backdrop={<Background … />}` | that node, for this screen |
|
|
517
517
|
| `backdrop={false}` | no background and no blobs: a plain page (`data-plain` on the root) |
|
|
518
518
|
|
|
519
|
-
**Where the shapes go.** The theme's shapes (the motif
|
|
520
|
-
are drawn whole or not at all, and only in empty space. A
|
|
521
|
-
or reach the app bar, the breadcrumb, the header
|
|
522
|
-
not drawn (a permanent exclusion). A
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
519
|
+
**Where the shapes go (the drop rule).** The theme's shapes and icons (the motif,
|
|
520
|
+
icons and illustrated presets) are drawn whole or not at all, and only in empty space. A
|
|
521
|
+
glyph that would cross the layer's edge, or reach the app bar, the breadcrumb, the header
|
|
522
|
+
card or an `EntityHeader`, is not drawn (a permanent exclusion). A glyph that lies over (or
|
|
523
|
+
within 8px of) a surface (the whole box of a card, a stat card, a table, a data grid, a list,
|
|
524
|
+
an alert or a dialog), a text (every line) or a control (inputs, buttons, links, images) is
|
|
525
|
+
dropped: it fades out in 150ms (none under `prefers-reduced-motion`) and fades back once the
|
|
526
|
+
content moves away, for instance when the page scrolls or a list changes. Glyphs are dropped,
|
|
527
|
+
never moved or resized, so no glyph is left half hidden at a card's edge. The check reads the content when it changes or
|
|
527
528
|
resizes and keeps the boxes, so a scroll only shifts them (one `requestAnimationFrame`-throttled
|
|
528
529
|
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
530
|
check has run, so no shape flashes over the content. On a phone, where the
|
|
@@ -50,12 +50,12 @@ import '@nxgt/material/backgrounds.css';
|
|
|
50
50
|
| `animated` | `boolean`: drifts the layers, see Motion | `false` |
|
|
51
51
|
| `grain` | `boolean`: film noise over the preset; needs `grain.css` | `false` |
|
|
52
52
|
| `seed` | `number`: moves a seeded preset's geometry and the grain | `0` |
|
|
53
|
-
| `motif` | a `MotifOptions` object (`shapes`, `density`, `size`, `rotate`, `shapeFill`, `opacitySpread`), see [Motif presets](#motif-presets) | all fields optional |
|
|
53
|
+
| `motif` | a `MotifOptions` object (`shapes`, `density`, `layout`, `size`, `rotate`, `shapeFill`, `opacitySpread`), see [Motif presets](#motif-presets) | all fields optional |
|
|
54
54
|
| `moon` | a `MoonOptions` object for `preset="moon"`, the celestial presets and a `sky`: `position` (`{ x, y }`, fractions of the container), `phase`, `halo`; see [Moon](#moon) | `{ position: { x: 0.8, y: 0.2 }, phase: 0.78, halo: 2.6 }` |
|
|
55
55
|
| `sky` | `'moon'`, `'eclipse'`, `'meteor-shower'`, `'ringed-planet'` or `'northern-lights'`: a second scene drawn over any preset, before the grain; see [Celestial presets and the sky overlay](#celestial-presets-and-the-sky-overlay) | none |
|
|
56
56
|
| `icons` | for `preset="icons"`: a pack name (`school`, `health`, `finance`, `travel`, `nature`, `tech`) or up to 48 icon names and `{ name, family?, type? }` references; see [Icons](#icons) | the `school` pack |
|
|
57
|
-
| `iconStyle` | for `preset="icons"
|
|
58
|
-
| `iconMotif` | for `preset="icons"
|
|
57
|
+
| `iconStyle` | for `preset="icons"` (and `shapes` with `motif.icons`): one style (`{ family?, type? }` or a sprite name such as `'sharp-light'`), a list of at most 8, or a weighted list `[{ style, weight? }]`; one drawn per icon. A weighted list drops the styles whose sprite is not served | light 3, thin 3, sharp-regular 2, sharp-light 2, duotone-thin 1 |
|
|
58
|
+
| `iconMotif` | for `preset="icons"` (and `shapes` with `motif.icons`): `layout` (`even`: shapes and a few icons; `scatter`, `grid`, `constellation`, `drift`: icons only), `colors` (`one`, `palette`, `classic`), `sizeRange`, `rotation`, `duotone` (`opacity` or `tones`), `secondaryOpacity` (0.4 or, with `tones`, 0.85 unless set), `calm` (icons-only layouts), `quiet` (`start`, `end`, `center`: a band the icons fade out of) | `{ layout: 'even', colors: 'one', duotone: 'opacity' }` (`secondaryOpacity` unset: 0.4, or 0.85 with `tones`) |
|
|
59
59
|
| `fill` | `viewport` (the viewport's size) or `container` (its parent's size: the parent needs a definite height) | `viewport` |
|
|
60
60
|
|
|
61
61
|
|
|
@@ -181,7 +181,7 @@ 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
|
|
184
|
+
**Behind an `ActivityLayout`** a preset's discrete shapes and icons are dropped wherever their box would meet a surface (a card, a table, a list), a text or a control, and fade back when the content moves away (see [The drop rule](#the-drop-rule)). A `Background` used on its own, on a page you lay out, draws all its glyphs.
|
|
185
185
|
|
|
186
186
|
`shapes`, `confetti-grid`, `constellation`, `halftone`, `truchet`, `floating` and `scatter-icons` draw seeded, deterministic geometry from
|
|
187
187
|
the tone's three colours: the same `seed` gives the same composition on the server and in the
|
|
@@ -203,19 +203,26 @@ browser, and a new `seed` redraws it.
|
|
|
203
203
|
|
|
204
204
|
| Field | Values | Default | Read by |
|
|
205
205
|
| --- | --- | --- | --- |
|
|
206
|
-
| `shapes` | any of `MOTIF_SHAPES_ALL`: `circle`, `ring`, `square`, `triangle`, `plus`, `line`, `arc`, then `star`, `heart`, `hexagon`, `diamond`, `wave`, `zigzag`, `spiral`, then the Material 3 Expressive ones `m3-cookie-6`, `m3-cookie-9`, `m3-flower`, `m3-clover-4`, `m3-soft-burst`, `m3-sunny`, `m3-gem`, `m3-puffy`; none chosen draws circles | `MOTIF_SHAPES`: the first seven | `shapes`, `confetti-grid`, `halftone`, `truchet`, `floating` (`halftone` takes `circle`, else the first of `square`, `ring`, `plus`; `truchet` draws diagonals for `line` without `arc`; `floating` turns `circle`/`triangle` into blobs, `ring`/`arc` into rings, `square` into squircles and ignores `plus`/`line`; `shapes` and `confetti-grid` are the only presets that draw the added shapes, the others ignore them) |
|
|
207
|
-
| `density` | `low`, `default`, `high
|
|
206
|
+
| `shapes` | any of `MOTIF_SHAPES_ALL`: `circle`, `ring`, `square`, `triangle`, `plus`, `line`, `arc`, then `star`, `heart`, `hexagon`, `diamond`, `wave`, `zigzag`, `spiral`, then the Material 3 Expressive ones `m3-cookie-6`, `m3-cookie-9`, `m3-flower`, `m3-clover-4`, `m3-soft-burst`, `m3-sunny`, `m3-gem`, `m3-puffy`, then the radial ones `rays`, `dotted-ring`, `concentric`; none chosen draws circles | `MOTIF_SHAPES`: the first seven | `shapes`, `confetti-grid`, `halftone`, `truchet`, `floating` (`halftone` takes `circle`, else the first of `square`, `ring`, `plus`; `truchet` draws diagonals for `line` without `arc`; `floating` turns `circle`/`triangle` into blobs, `ring`/`arc` into rings, `square` into squircles and ignores `plus`/`line`; `shapes` and `confetti-grid` are the only presets that draw the added shapes, the others ignore them) |
|
|
207
|
+
| `density` | `low`, `default`, `high` (0.55, 1 and 1.7 times the preset's count), `medium` (about 160 items on the scene, more than `high` for `shapes`) or a number, the item count on the scene (up to 600, scaled with the frame's area); `moon` and the celestial presets draw `medium` and numbers as `default` | `default` | all fifteen |
|
|
208
|
+
| `layout` | `scatter` (thrown darts that keep clear of each other) or `even` (a jittered grid over the whole frame, one item per cell: no empty corner, no cluster) | `scatter` | `shapes` |
|
|
208
209
|
| `size` | `small`, `mixed`, `large` | `mixed` | all fifteen |
|
|
209
210
|
| `rotate` | `boolean`: turns the items | `false` | `shapes`, `confetti-grid`, `scatter-icons` (tilts up to 35 degrees), the illustrated scenes (up to 35 degrees instead of 8) |
|
|
210
211
|
| `shapeFill` | `filled`, `outline`, `mixed` | `mixed` | `shapes`, `confetti-grid`, `halftone`, `floating` |
|
|
211
212
|
| `opacitySpread` | `uniform`, `varied` | `varied` | all but `halftone` |
|
|
212
213
|
|
|
213
|
-
`MOTIF_SHAPES` (the default) stays the first seven; pass `shapes: MOTIF_SHAPES_ALL`, or a hand-picked list such as `['star', 'm3-flower', 'wave']`, for the rest. In `shapes` and `confetti-grid` the 15 added shapes are drawn once per scene as a repeated pattern, so their tone, fill and (in `confetti-grid`) turn and size are fixed per shape; the first seven
|
|
214
|
+
`MOTIF_SHAPES` (the default) stays the first seven; pass `shapes: MOTIF_SHAPES_ALL`, or a hand-picked list such as `['star', 'm3-flower', 'wave']`, for the rest. In `shapes` and `confetti-grid` the 15 added shapes before the radial ones are drawn once per scene as a repeated pattern, so their tone, fill and (in `confetti-grid`) turn and size are fixed per shape; the first seven and the three radial ones are drawn item by item. The Storybook story `Backgrounds/Motif shapes/ShapeCatalogue` draws every shape filled and outlined.
|
|
214
215
|
|
|
215
|
-
<img src="../previews/backgrounds-motif-shapes.webp" width="480" alt="The
|
|
216
|
+
<img src="../previews/backgrounds-motif-shapes.webp" width="480" alt="The motif shapes, each filled and outlined">
|
|
216
217
|
|
|
217
218
|
```tsx
|
|
218
219
|
<Background preset="shapes" motif={{ shapes: ['star', 'm3-flower', 'wave'] }} />
|
|
220
|
+
|
|
221
|
+
// The radial shapes, spread evenly over the whole frame at medium density
|
|
222
|
+
<Background
|
|
223
|
+
preset="shapes"
|
|
224
|
+
motif={{ shapes: ['rays', 'dotted-ring', 'concentric'], layout: 'even', density: 'medium' }}
|
|
225
|
+
/>
|
|
219
226
|
```
|
|
220
227
|
|
|
221
228
|
A field a preset does not read is ignored. `rotate` defaults to `false`: motifs stay upright
|
|
@@ -326,7 +333,7 @@ them. The theme picker draws a small tile for a theme with a celestial preset.
|
|
|
326
333
|
|
|
327
334
|
## Icons
|
|
328
335
|
|
|
329
|
-
`preset="icons"`
|
|
336
|
+
`preset="icons"` draws a calm mix over a faint wash: motif shapes and, in about one cell in five, a Font Awesome icon, all in one soft tint on an even grid over the whole frame. The library ships **no icon artwork**: each icon is a
|
|
330
337
|
`<use>` of a sprite file your app serves (`/assets/icons/sprites/` by default; `IconSpritesProvider base="/static/icons/"`
|
|
331
338
|
moves it, as for `Icon`; `edition="free"` draws the Free sheets, see the [Free edition](icon-motifs.md#the-free-edition)). Font Awesome Pro is licensed to you, not to this package. The [icon motifs guide](icon-motifs.md) puts icons, skies, illustrations and themes together.
|
|
332
339
|
|
|
@@ -338,12 +345,146 @@ import { Background } from '@nxgt/material/backgrounds';
|
|
|
338
345
|
preset="icons"
|
|
339
346
|
tone="mixed"
|
|
340
347
|
icons="health" // a pack, or ['rocket', 'star', { name: 'bolt', family: 'sharp', type: 'solid' }]
|
|
341
|
-
iconStyle={['
|
|
342
|
-
iconMotif={{
|
|
348
|
+
iconStyle={[{ style: 'light', weight: 3 }, { style: 'duotone-thin' }]} // a weighted list; an icon's own family/type wins
|
|
349
|
+
iconMotif={{ colors: 'one' }}
|
|
350
|
+
motif={{ icons: { ratio: 0.2 }, density: 'medium' }}
|
|
343
351
|
animated
|
|
344
352
|
/>;
|
|
345
353
|
```
|
|
346
354
|
|
|
355
|
+
Since 3.22 the preset is the mix by default; none of this is needed for it:
|
|
356
|
+
|
|
357
|
+
- **The mix**: `motif.icons` is `{ pack?, style?, ratio }`. Each cell of an even, jittered grid is an icon with the
|
|
358
|
+
probability `ratio` (`0.2` for `icons`; `0` is shapes only, `1` icons only), else a motif shape: the `shapes`
|
|
359
|
+
default set, or `motif.shapes`, with the radial ones (`rays`, `dotted-ring`, `concentric`) counted twice so they show.
|
|
360
|
+
`motif.density` counts shapes and icons together (`'medium'` by default: about 160 on a 1600 by 1000 scene;
|
|
361
|
+
`low`, `high` or a number work too). `pack` and `style` default to the `icons` and `iconStyle` props. The same
|
|
362
|
+
mix is available on `preset="shapes"` with `motif={{ icons: { ratio: 0.3 } }}` (default `medium` density, no
|
|
363
|
+
quiet mask); it needs `backgrounds/icons.css` (in `backgrounds.css`). The layout is always even there: `motif.layout` is not read.
|
|
364
|
+
- **One tint**: `iconMotif.colors` is `'one'` (default: the primary tone at low contrast, soft in dark, never bright;
|
|
365
|
+
the shapes sit at about 0.8 of it), `'palette'` (primary, secondary and tertiary at the same contrast, best with
|
|
366
|
+
`tone="mixed"`) or `'classic'` (the three tones at full strength, as before 3.22). It is a class on the scene root
|
|
367
|
+
(`ic-c-one`, `ic-c-palette`, `ic-c-classic`); the tint sets `color` as well as `--fa-primary-color`, so flat styles
|
|
368
|
+
follow.
|
|
369
|
+
- **Styles**: the weighted default is light 3, thin 3, sharp-regular 2, sharp-light 2, duotone-thin 1. A weighted
|
|
370
|
+
list (the default included) is cut to the sprites your app serves: `IconSpritesProvider servedStyles={['light',
|
|
371
|
+
'thin']}` lists the files, `edition="free"` serves `regular` and `solid`; the weights of what is left keep their
|
|
372
|
+
proportions, and with nothing left it draws Free `regular` and `solid`. A plain style or list is never cut.
|
|
373
|
+
|
|
374
|
+
### A reduced sprite for the backdrop
|
|
375
|
+
|
|
376
|
+
The backdrop never needs the full per-style sheets (hundreds of KB each). Build one small sprite that holds only the
|
|
377
|
+
backdrop's icon names in the styles you choose, from your own Font Awesome install, and point the provider at it:
|
|
378
|
+
|
|
379
|
+
```sh
|
|
380
|
+
# reads @fortawesome/fontawesome-pro, else @fortawesome/fontawesome-free, from node_modules
|
|
381
|
+
npx nxgt-backdrop-sprite --pack health --out public/sprites/backdrop.svg --base /sprites/
|
|
382
|
+
# nxgt-backdrop-sprite --pack school --pack health --style light --style thin (more packs, explicit styles)
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
```tsx
|
|
386
|
+
<IconSpritesProvider
|
|
387
|
+
base="/assets/icons/sprites/" // still used by Icon and Illustration
|
|
388
|
+
backdropSprite={{ href: '/sprites/backdrop.svg', styles: ['light', 'thin', 'sharp-regular', 'sharp-light', 'duotone-thin'] }}
|
|
389
|
+
>
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
The command prints the exact `backdropSprite` value. With it set, the icon glyphs of `Background` reference
|
|
393
|
+
`/sprites/backdrop.svg#<file>--<name>` (`thin--book-open`) instead of the per-style sheets, and the styles it holds
|
|
394
|
+
count as served (no `servedStyles` needed). The string form, `backdropSprite="/sprites/backdrop.svg"`, leaves
|
|
395
|
+
which styles are served to `servedStyles` (or `edition="free"`).
|
|
396
|
+
|
|
397
|
+
- `Icon`, `Illustration` and the theme tiles still read `base`; only `Background` reads the reduced sprite
|
|
398
|
+
(`useBackdropSprite()` returns its `href`).
|
|
399
|
+
- The `href` must be root-relative and end in `.svg`, else `IconSpritesProvider` throws `IconRefError` (`base`).
|
|
400
|
+
`--base` / `base` is the path from your web root, not Vite's `base`.
|
|
401
|
+
- The command warns when `--out` is not under the `--base` path (`/sprites/` when you give no `--base`).
|
|
402
|
+
|
|
403
|
+
#### The Free edition
|
|
404
|
+
|
|
405
|
+
With `edition="free"` the styles served are `regular` and `solid` whatever the object lists, and the weighted default
|
|
406
|
+
falls back to those two. Build the sprite with `--style regular --style solid` for a Free app. Do not combine
|
|
407
|
+
`edition="free"` with a sprite built from a Pro install that holds no `regular` and `solid` symbols: the Free edition
|
|
408
|
+
draws exactly those two styles, so the icons are blank. A Free install alone gives `regular` and `solid`.
|
|
409
|
+
|
|
410
|
+
#### A Pro-only sprite
|
|
411
|
+
|
|
412
|
+
A sprite built from a Pro install holds Pro glyphs under your licence. Serve it from your app; never commit it to a
|
|
413
|
+
public repository, publish it in a package or a public Storybook. Keep it out of version control
|
|
414
|
+
(`public/sprites/backdrop.svg` in `.gitignore`) and build it in CI or at `vite build`, from the install you license.
|
|
415
|
+
|
|
416
|
+
- **CLI or plugin, not both**: pick one. A `public/sprites/backdrop.svg` the command wrote is a static file that Vite serves before the plugin's dev route, so it shadows the plugin's sprite (and goes stale); delete it when you move to the plugin.
|
|
417
|
+
- **Free install**: a Pro-only name (`backpack`, `chevrons-left`) built from a Free install is written under its Free twin's id, and the command and the plugin warn: set `edition="free"` on `IconSpritesProvider`. - **Packs and names**: `--pack` (repeat it) for the icon packs your backdrops draw, and `--icon` for the names of your
|
|
418
|
+
own `icons` list. **There is no default pack**: with neither, the command fails and shows the fix. Put every pack or
|
|
419
|
+
name your backdrops draw: a name missing from the sprite shows nothing.
|
|
420
|
+
- **Styles**: `--style` (repeat it). Unset, the default mix cut to what your install has: `light`, `thin`,
|
|
421
|
+
`sharp-regular`, `sharp-light` and `duotone-thin` come from Font Awesome Pro, and Free alone holds `regular` and
|
|
422
|
+
`solid`, which the sprite then holds. A style the install lacks, named with `--style`, is an error. A style that does
|
|
423
|
+
not hold a name takes the glyph of the next style that does, so no symbol is blank.
|
|
424
|
+
- **Where it reads**: each style's `sprites/<file>.svg` in the package (or `svgs/<file>/<name>.svg`), one `<symbol>`
|
|
425
|
+
per name and style. `--from <folder>` names a Font Awesome package folder when it is not in `node_modules`.
|
|
426
|
+
- **Size**: about 10–14 KB gzipped per pack with the five default Pro styles; a Free pack in `regular` and `solid` is
|
|
427
|
+
about 3 KB gzipped.
|
|
428
|
+
- **Licence**: the sprite holds glyphs of your own install, under its licence. Serve it from your app, and never
|
|
429
|
+
commit a Pro sprite to a public repository or a published Storybook.
|
|
430
|
+
|
|
431
|
+
Or, instead of the command, the Vite plugin runs the same build with the app, and the dev server serves it:
|
|
432
|
+
|
|
433
|
+
```ts
|
|
434
|
+
// vite.config.ts
|
|
435
|
+
nxgtMaterial({ backdropSprite: { packs: ['health'], styles: ['light', 'thin'], base: '/sprites/', outFile: 'backdrop.svg' } })
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
See [the Vite plugin guide](vite-plugin.md#the-reduced-backdrop-sprite).
|
|
439
|
+
|
|
440
|
+
#### The sprites each style needs
|
|
441
|
+
|
|
442
|
+
Without a reduced sprite, the backdrop draws from per-style sheets at the `IconSpritesProvider` `base`:
|
|
443
|
+
|
|
444
|
+
| Style (`iconStyle`) | Sprite file | Share in the default mix | Font Awesome |
|
|
445
|
+
| --- | --- | --- | --- |
|
|
446
|
+
| `light` | `light.svg` | 3 | Pro |
|
|
447
|
+
| `thin` | `thin.svg` | 3 | Pro |
|
|
448
|
+
| `sharp-regular` | `sharp-regular.svg` | 2 | Pro (Sharp) |
|
|
449
|
+
| `sharp-light` | `sharp-light.svg` | 2 | Pro (Sharp) |
|
|
450
|
+
| `duotone-thin` | `duotone-thin.svg` | 1 | Pro (Duotone) |
|
|
451
|
+
| `regular`, `solid` | `regular.svg`, `solid.svg` | the fallback when none of the above is served, and the only two served with `edition="free"` | Free |
|
|
452
|
+
|
|
453
|
+
A plain `iconStyle` (one style, or a list without weights) is drawn as written and needs its own sheets; only a
|
|
454
|
+
weighted list, the default included, is cut to what is served. A reduced sprite replaces all these files for the
|
|
455
|
+
backdrop (`Icon` still reads them).
|
|
456
|
+
|
|
457
|
+
### Density
|
|
458
|
+
|
|
459
|
+
`motif.density` counts shapes and icons together.
|
|
460
|
+
|
|
461
|
+
| Value | Meaning |
|
|
462
|
+
| --- | --- |
|
|
463
|
+
| `'low'`, `'default'`, `'high'` | 0.55, 1 and 1.7 times the preset's own count (as before 3.22) |
|
|
464
|
+
| `'medium'` | the calm mix: about 160 glyphs on a 1600 by 1000 scene; the default of the mix |
|
|
465
|
+
| a number | the count on that scene, up to 600, scaled with the frame's area |
|
|
466
|
+
|
|
467
|
+
A small frame draws fewer glyphs, a large one more, so the spread stays the same. The drop rule then thins what meets
|
|
468
|
+
content.
|
|
469
|
+
|
|
470
|
+
### The look before 3.22
|
|
471
|
+
|
|
472
|
+
The previous look (icons only, scattered, one duotone style, the three tones at full strength) is three explicit props,
|
|
473
|
+
and nothing else changes:
|
|
474
|
+
|
|
475
|
+
```tsx
|
|
476
|
+
<Background
|
|
477
|
+
preset="icons"
|
|
478
|
+
iconStyle="duotone-thin"
|
|
479
|
+
iconMotif={{ layout: 'scatter', colors: 'classic' }}
|
|
480
|
+
/>
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
Any layout but `even` draws icons only, `calm` and `quiet` apply to them as before, and `density` and `motif.icons` are
|
|
484
|
+
not read there. In a theme, the same three keys do it: `background: { preset: 'icons', iconStyle: 'duotone-thin',
|
|
485
|
+
iconMotif: { layout: 'scatter', colors: 'classic' } }`. An app that serves only some Pro sprites sets `servedStyles`
|
|
486
|
+
(or `edition="free"`) on its `IconSpritesProvider`, since the new default draws five styles.
|
|
487
|
+
|
|
347
488
|
- The icons come from a builder in its own chunk, loaded on demand (`@nxgt/material-core/backgrounds/icons`), so the
|
|
348
489
|
preset registry does not grow. A streaming server render has the icons in the first HTML; with `renderToString` the
|
|
349
490
|
scene layer is empty until the chunk has loaded, on the server and on the client alike (no hydration mismatch).
|
|
@@ -352,12 +493,13 @@ import { Background } from '@nxgt/material/backgrounds';
|
|
|
352
493
|
- Colours: the primary layer takes the tone's colours; the second layer of a duotone icon is the same colour at
|
|
353
494
|
`secondaryOpacity` (no default of its own: 0.4 with `duotone: 'opacity'`, 0.85 with `'tones'`, where the second layer takes the next tone; a value you set, 0.4 included, is used in both). Flat styles (`solid`,
|
|
354
495
|
`regular`) take the tone through `currentColor`. A name missing from the sprite of the style drawn shows
|
|
355
|
-
nothing, so keep a list of styles to ones that share your names. The default
|
|
356
|
-
|
|
496
|
+
nothing, so keep a list of styles to ones that share your names. The default mix needs `light.svg`, `thin.svg`,
|
|
497
|
+
`sharp-regular.svg`, `sharp-light.svg` and `duotone-thin.svg` at the sprite base, or `servedStyles` for the ones you
|
|
498
|
+
have (see the sprite 404 entry in [troubleshooting](../troubleshooting.md)).
|
|
357
499
|
- `animated` drifts each icon a few pixels on `translate` (never under `prefers-reduced-motion`, paused while hidden).
|
|
358
|
-
- In an `ActivityLayout` the keep-inside pass
|
|
500
|
+
- In an `ActivityLayout` the keep-inside pass drops an icon or a shape that would cross the layer's edge or the header band (see [the drop rule](#the-drop-rule)).
|
|
359
501
|
- A theme can name the preset with `background: { preset: 'icons', icons, iconStyle, iconMotif, sky }`; the props win. The theme picker draws the first four icons in the theme's tile ([themes](themes.md#icon-motifs-in-a-theme)).
|
|
360
|
-
- CSS: `backgrounds/icons.css` (or `backgrounds.css`). Both pull in `backgrounds/icons-quiet.css`, the mask behind `iconMotif.quiet`,
|
|
502
|
+
- CSS: `backgrounds/icons.css` (or `backgrounds.css`). Both pull in `backgrounds/icons-quiet.css`, the mask behind `iconMotif.quiet`, and `backgrounds/icons-tint.css`, the `colors` tints; each can also be imported alone. React only: `@nxgt/material-vue` does not draw it.
|
|
361
503
|
|
|
362
504
|
### Keeping text clear of the icons: `quiet`
|
|
363
505
|
|
|
@@ -392,6 +534,23 @@ text sits on the wash alone.
|
|
|
392
534
|
tone of the wash, light and dark.
|
|
393
535
|
- A theme sets it too: `background: { preset: 'icons', iconMotif: { quiet: 'end' } }`.
|
|
394
536
|
|
|
537
|
+
## The drop rule
|
|
538
|
+
|
|
539
|
+
Behind an `ActivityLayout` or an `AuthCard` (the only two that run the pass; a bare `Background` never does), a glyph (a shape or an icon)
|
|
540
|
+
whose box meets the content is **dropped**, never moved or resized. The content is a surface (the whole box of a card,
|
|
541
|
+
a stat card, a table, a data grid, a list, an alert or a dialog), a text or a control, with an 8 px margin, so no glyph
|
|
542
|
+
is left half hidden at a card's edge. A glyph that crosses the layer's edge or reaches the header band is dropped for
|
|
543
|
+
good.
|
|
544
|
+
|
|
545
|
+
- The server renders every glyph. The drop happens after the first measure, on the client, in one frame (boxes read
|
|
546
|
+
before any write), so the markup never differs between server and client.
|
|
547
|
+
- A dropped glyph fades out in 150 ms (none under `prefers-reduced-motion`) and fades back when the content moves
|
|
548
|
+
away. It runs again on resize and when the content changes.
|
|
549
|
+
- The open areas keep their glyphs; the space behind and between cards is empty. `keepClear={false}` on an `AuthCard`
|
|
550
|
+
lets the glyphs run under the card.
|
|
551
|
+
- See [The theme's background](./app-shell.md#the-themes-background) for the shell and `AuthCard` in
|
|
552
|
+
[navigation and layout](../components/navigation-and-layout.md#authcard) for the sign-in page.
|
|
553
|
+
|
|
395
554
|
## Dark mode
|
|
396
555
|
|
|
397
556
|
In dark the drawn colours are lifted to a lightness floor: the dark `--primary` is as dim as the light one, which made the
|
|
@@ -432,7 +591,7 @@ The gradient ends on `--nxgt-bg-cp`, not `--nxgt-bg-c2`: to recolour it, set
|
|
|
432
591
|
## Defaults from the theme
|
|
433
592
|
|
|
434
593
|
The `Background` of `@nxgt/material/backgrounds` takes what you leave out
|
|
435
|
-
(`preset`, `tone`, `intensity`, `seed`, `motif`, `moon`, `sky`, `grain`, `animated`) from the
|
|
594
|
+
(`preset`, `tone`, `intensity`, `seed`, `motif`, `moon`, `sky`, `grain`, `animated`, and for `icons` also `icons`, `iconStyle`, `iconMotif` (`layout`, `colors`), `motif.icons` and `motif.density`; `ActivityLayout` and `AuthCard` draw it too) from the
|
|
436
595
|
nearest theme (`NxgtThemeProvider` or an `NxgtTheme` zone, see
|
|
437
596
|
[the provider and zones](./theming.md#the-provider-and-zones)), then from the
|
|
438
597
|
defaults above. A prop you pass always wins; the others still come from the theme.
|
|
@@ -447,6 +606,45 @@ import { Background } from '@nxgt/material/backgrounds';
|
|
|
447
606
|
</NxgtTheme>
|
|
448
607
|
```
|
|
449
608
|
|
|
609
|
+
### The calm backdrop in a theme
|
|
610
|
+
|
|
611
|
+
A theme's `background` takes the same knobs as the props, checked by `defineTheme` (a bad icon key or value throws). A bad `motif` key or value is dropped, not thrown: `defineTheme` from `@nxgt/material/themes` warns for each one in development, the `@nxgt/material-core/theme/define` one only through its `{ warn }` option (4.0: an error; see [troubleshooting](../troubleshooting.md#definetheme-theme-x-backgroundmotif-unknown-key-spin-ignored)).
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
```ts
|
|
615
|
+
import { defineTheme } from '@nxgt/material/themes';
|
|
616
|
+
|
|
617
|
+
export const ecole = defineTheme({
|
|
618
|
+
name: 'ecole',
|
|
619
|
+
colors: { primary: '#0b7a75' },
|
|
620
|
+
background: {
|
|
621
|
+
preset: 'icons',
|
|
622
|
+
icons: 'school',
|
|
623
|
+
// weighted families: a share each (default 1); a style the app does not serve is dropped
|
|
624
|
+
iconStyle: [
|
|
625
|
+
{ style: 'light', weight: 3 },
|
|
626
|
+
{ style: 'sharp-regular', weight: 2 },
|
|
627
|
+
{ style: 'duotone-thin', weight: 1 },
|
|
628
|
+
],
|
|
629
|
+
iconMotif: { layout: 'even', colors: 'one' }, // 'palette' | 'classic'
|
|
630
|
+
motif: { density: 'medium', icons: { ratio: 0.2 } }, // 'low' | 'high' | a number
|
|
631
|
+
},
|
|
632
|
+
});
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
| Key | Values |
|
|
636
|
+
| --- | --- |
|
|
637
|
+
| `iconMotif.layout` | `even` (the mix), `scatter`, `grid`, `constellation`, `drift` |
|
|
638
|
+
| `iconMotif.colors` | `one`, `palette`, `classic` |
|
|
639
|
+
| `iconStyle` | a style, a list (at most 8), or a weighted list `[{ style, weight? }]`, `weight` from 0 to 1000 |
|
|
640
|
+
| `motif.density` | `low`, `default`, `medium`, `high`, or a number from 1 to 600 |
|
|
641
|
+
| `motif.layout` | `scatter`, `even` (the `shapes` preset) |
|
|
642
|
+
| `motif.icons` | `{ pack?, style?, ratio }`, `ratio` from 0 to 1 (`icons` packs and styles as the props) |
|
|
643
|
+
|
|
644
|
+
`extends` merges `motif` and `iconMotif` key by key; `iconStyle` and `motif.icons` are replaced whole. `definePlainTheme`
|
|
645
|
+
(no icon check) refuses the other icon keys and ignores `motif.icons`. The theme's `iconStyle` list and the app's
|
|
646
|
+
`servedStyles` or `backdropSprite` work together as for the props.
|
|
647
|
+
|
|
450
648
|
Outside any theme nothing changes. The preset's stylesheet is still yours to
|
|
451
649
|
import. The `/components` `Background` is the gradient alone and does not read
|
|
452
650
|
the theme.
|