@nxgt/material-vue 0.42.0 → 0.43.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 +111 -0
- package/README.md +18 -5
- package/dist/{Button-CWmzvv9p.js → Button-C3K6dhqd.js} +4 -4
- package/dist/{HoverCardTrigger-C5AbAyHE.js → HoverCardTrigger-Bga4YjEr.js} +69 -62
- package/dist/backgrounds/backgrounds.css +8 -0
- package/dist/backgrounds/css/base.css +169 -0
- package/dist/backgrounds/css/gradient.css +53 -0
- package/dist/backgrounds/css/grain.css +19 -0
- package/dist/backgrounds/css/motion.css +30 -0
- package/dist/backgrounds.js +111 -0
- package/dist/{camera-C-9slBUV.js → camera-BbF9_-ys.js} +4 -4
- package/dist/components/base.css +5 -2
- package/dist/components/components.css +5 -0
- package/dist/components/theme-tokens.css +21 -0
- package/dist/{components-DX4EycMi.js → components-yCdbry6Q.js} +14077 -13577
- package/dist/components.js +8 -8
- package/dist/composables.js +3 -3
- package/dist/dnd.js +1 -1
- package/dist/{form-scope-CN68PTXc.js → form-scope-CkWJrBCu.js} +1 -1
- package/dist/{i18n-provider-DmZSTBnp.js → i18n-provider-tL27-a8y.js} +1 -1
- package/dist/i18n.js +4 -4
- package/dist/{icon-button-DErh1aJS.js → icon-button-MyFFJpXA.js} +4 -4
- package/dist/lib/backgrounds/background/background.vue.d.ts +22 -0
- package/dist/lib/backgrounds/background/index.d.ts +2 -0
- package/dist/lib/backgrounds/background/partials/background-layers.vue.d.ts +9 -0
- package/dist/lib/backgrounds/background/partials/scene-svg.d.ts +10 -0
- package/dist/lib/backgrounds/background/types.d.ts +24 -0
- package/dist/lib/backgrounds/index.d.ts +13 -0
- package/dist/lib/components/ui/actions/button/button-tones.d.ts +3 -3
- package/dist/lib/components/ui/actions/responsive-button/responsive-button.vue.d.ts +3 -3
- package/dist/lib/components/ui/collections/data-table/data-table-context.d.ts +4 -0
- package/dist/lib/components/ui/collections/data-table/data-table.vue.d.ts +8 -0
- package/dist/lib/components/ui/collections/data-table/partials/data-table-body.vue.d.ts +8 -0
- package/dist/lib/components/ui/collections/data-table/partials/data-table-empty.vue.d.ts +5 -1
- package/dist/lib/components/ui/collections/data-table/partials/data-table-toolbar.d.ts +16 -3
- package/dist/lib/components/ui/collections/data-table/select-checkbox.d.ts +36 -0
- package/dist/lib/components/ui/collections/data-table/types.d.ts +15 -0
- package/dist/lib/components/ui/collections/data-table/use-data-table.d.ts +2 -0
- package/dist/lib/components/ui/feedback/circular-progress/circular-progress.vue.d.ts +1 -1
- package/dist/lib/components/ui/feedback/empty-state/empty-state.vue.d.ts +21 -8
- package/dist/lib/components/ui/feedback/empty-state/index.d.ts +1 -1
- package/dist/lib/components/ui/feedback/error-state/error-state.vue.d.ts +54 -0
- package/dist/lib/components/ui/feedback/error-state/index.d.ts +3 -0
- package/dist/lib/components/ui/feedback/error-state/partials/error-state-details.vue.d.ts +7 -0
- package/dist/lib/components/ui/feedback/index.d.ts +1 -0
- package/dist/lib/components/ui/feedback/state-frame/state-frame.vue.d.ts +36 -0
- package/dist/lib/components/ui/feedback/state-frame/state-icon.vue.d.ts +7 -0
- package/dist/lib/components/ui/feedback/state-frame/state-illustration.vue.d.ts +8 -0
- package/dist/lib/components/ui/feedback/state-frame/state-tones.d.ts +11 -0
- package/dist/lib/components/ui/fields/contacts/consts.d.ts +1 -1
- package/dist/lib/components/ui/layout/app-shell/use-truncated-title.d.ts +10 -0
- package/dist/lib/components/ui/layout/background/background.vue.d.ts +18 -3
- package/dist/lib/components/ui/layout/doc/partials/doc-empty.vue.d.ts +7 -4
- package/dist/lib/components/ui/media/stx-player/stx-player-viewport.vue.d.ts +2 -2
- package/dist/lib/components/ui/overlays/command/CommandSeparator.vue.d.ts +3 -1
- package/dist/lib/components/ui/overlays/dialog/in-dialog.d.ts +1 -1
- package/dist/lib/components/ui/overlays/dialog/use-escape-forward.d.ts +13 -0
- package/dist/lib/components/ui/overlays/dialog/use-modal-content.d.ts +2 -0
- package/dist/lib/components/ui/primitives/nonce-provider/nonce-provider.vue.d.ts +4 -1
- package/dist/lib/components/ui/selection/autocomplete/autocomplete-keyboard.d.ts +22 -4
- package/dist/lib/components/ui/selection/autocomplete/partials/autocomplete-box.vue.d.ts +6 -2
- package/dist/lib/components/ui/selection/autocomplete/partials/autocomplete-list.vue.d.ts +7 -0
- package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-active.d.ts +17 -0
- package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-keydown.d.ts +21 -0
- package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-open.d.ts +3 -3
- package/dist/lib/components/ui/selection/autocomplete/use-autocomplete.d.ts +2 -1
- package/dist/lib/components/ui/selection/autocomplete/use-drawer-room.d.ts +11 -0
- package/dist/lib/components/ui/selection/autocomplete/use-query-change.d.ts +8 -0
- package/dist/lib/components/ui/widgets/charts/chart-container.vue.d.ts +2 -2
- package/dist/lib/components/ui/widgets/kanban/partials/kanban-empty.vue.d.ts +1 -0
- package/dist/lib/composables/use-background-activity.d.ts +8 -0
- package/dist/lib/i18n/resources/en/en.d.ts +24 -0
- package/dist/lib/i18n/resources/en/material.json.d.ts +25 -1
- package/dist/lib/i18n/resources/fr/fr.d.ts +24 -0
- package/dist/lib/i18n/resources/fr/material.json.d.ts +25 -1
- package/dist/lib/i18n/resources/resources.d.ts +48 -0
- package/dist/lib/i18n/translator.d.ts +1 -1
- package/dist/lib/lib/index.d.ts +1 -0
- package/dist/lib/nuxt/component-names.d.ts +1 -1
- package/dist/lib/nuxt/module.d.ts +2 -11
- package/dist/lib/nuxt/pdf-fonts.d.ts +1 -1
- package/dist/lib/nuxt/plugin-options.d.ts +58 -0
- package/dist/lib/vite/index.d.ts +4 -8
- package/dist/lib.js +52 -51
- package/dist/main.js +17 -17
- package/dist/map.js +4 -4
- package/dist/material-vue.css +1 -1
- package/dist/{pdf-outline-sidebar-BU4o_hBI.js → pdf-outline-sidebar-Kqsfd_cO.js} +2 -2
- package/dist/{pdf-page-anNe78Lc.js → pdf-page-Dx3jwxu6.js} +1 -1
- package/dist/{pdf-page-list-CSY76HH6.js → pdf-page-list-BsQmEHaY.js} +2 -2
- package/dist/{pdf-reader-BhZNrW7R.js → pdf-reader-CJo8SQVl.js} +15 -15
- package/dist/{pdf-sidebar-DcIpG7eh.js → pdf-sidebar-CcCUATl-.js} +3 -3
- package/dist/{pdf-thumbnail-sidebar-CUC0V4if.js → pdf-thumbnail-sidebar-BRxNRGfG.js} +4 -4
- package/dist/{pdf-toolbar-C373S-ul.js → pdf-toolbar-B0ckYBJ2.js} +4 -4
- package/dist/pdf.js +2 -2
- package/dist/{print-format.utils-C81c0weJ.js → print-format.utils-51joHpFb.js} +2 -2
- package/dist/{sortable-provider-CL2UGGn8.js → sortable-provider-BEx1B0Ky.js} +2 -2
- package/dist/{stored-language-Wtcj2HsU.js → stored-language-DVP9KzRu.js} +50 -2
- package/dist/{stx-player-CMmM3F5Z.js → stx-player-KPp9c7vS.js} +12 -12
- package/dist/styles.css +4 -0
- package/dist/{translator-D5Dp-dob.js → translator-Bm7i7dv-.js} +1 -1
- package/dist/use-background-activity-DdTBbNpX.js +17 -0
- package/dist/{use-heading-spy-BIBHtUXN.js → use-heading-spy-jn8f2ioE.js} +13 -13
- package/dist/{use-material-translation-COoBTGpB.js → use-material-translation-CfFZPSe0.js} +3 -3
- package/dist/vite.js +7 -2
- package/docs/README.md +3 -0
- package/docs/components/buttons-and-actions.md +2 -0
- package/docs/components/data-table-filter-and-lists.md +59 -8
- package/docs/components/feedback-and-status.md +79 -8
- package/docs/components/media-and-files.md +2 -0
- package/docs/components/navigation-and-layout.md +13 -5
- package/docs/components/overlays-and-menus.md +20 -3
- package/docs/components/primitives.md +5 -3
- package/docs/components/rich-widgets.md +1 -1
- package/docs/components/selection-and-choice.md +14 -4
- package/docs/getting-started.md +22 -3
- package/docs/guides/app-shell.md +1 -1
- package/docs/guides/backgrounds.md +143 -0
- package/docs/guides/csp.md +48 -6
- package/docs/guides/helpers.md +1 -0
- package/docs/guides/i18n.md +51 -1
- package/docs/guides/migrate-to-vite-plugin.md +259 -0
- package/docs/guides/pdf.md +11 -4
- package/docs/guides/ssr.md +1 -1
- package/docs/guides/theming.md +52 -4
- package/docs/guides/vite-plugin.md +194 -0
- package/docs/previews/backgrounds-matrix.webp +0 -0
- package/docs/previews/feedback-status-emptystate.webp +0 -0
- package/docs/previews/feedback-status-errorstate.webp +0 -0
- package/docs/roadmap.md +60 -32
- package/docs/troubleshooting.md +251 -7
- package/lib/backgrounds/backgrounds.css +8 -0
- package/lib/backgrounds/css/base.css +169 -0
- package/lib/backgrounds/css/gradient.css +53 -0
- package/lib/backgrounds/css/grain.css +19 -0
- package/lib/backgrounds/css/motion.css +30 -0
- package/lib/components/base.css +5 -2
- package/lib/components/components.css +5 -0
- package/lib/components/theme-tokens.css +21 -0
- package/lib/nuxt/component-names.ts +1 -0
- package/lib/nuxt/module.ts +29 -15
- package/lib/nuxt/pdf-fonts.ts +1 -1
- package/lib/nuxt/plugin-options.ts +92 -0
- package/lib/nuxt/runtime/inter.css +6 -0
- package/lib/styles.css +4 -0
- package/lib/vite/index.ts +19 -2
- package/package.json +24 -2
package/docs/troubleshooting.md
CHANGED
|
@@ -17,15 +17,19 @@ words in your error.
|
|
|
17
17
|
**Styles & assets**
|
|
18
18
|
|
|
19
19
|
- [Components render unstyled, with no error](#components-render-unstyled-with-no-error)
|
|
20
|
+
- [A link is blue and underlined on hover and focus](#a-link-of-mine-or-of-a-component-is-blue-and-underlined-on-hover-and-focus)
|
|
20
21
|
- [Math renders in the system fonts](#math-renders-in-the-system-fonts)
|
|
21
22
|
- [`<Icon>` renders nothing](#icon-renders-nothing)
|
|
22
23
|
- [An accessibility audit reports low contrast on a badge, a button, a toast or a menu entry](#an-accessibility-audit-reports-low-contrast-on-a-badge-a-button-a-toast-or-a-menu-entry)
|
|
23
24
|
- [`ARIA hidden element must not be focusable or contain focusable elements`](#aria-hidden-element-must-not-be-focusable-or-contain-focusable-elements)
|
|
24
25
|
- [An accessibility audit reports low contrast on a tonal or outlined button, a selected list tile or a hovered badge link](#an-accessibility-audit-reports-low-contrast-on-a-tonal-or-outlined-button-a-selected-list-tile-or-a-hovered-badge-link)
|
|
25
26
|
- [An accessibility audit reports low contrast on error text in the dark theme](#an-accessibility-audit-reports-low-contrast-on-error-text-in-the-dark-theme)
|
|
27
|
+
- [An accessibility audit reports low contrast on primary text in the dark theme](#an-accessibility-audit-reports-low-contrast-on-primary-text-in-the-dark-theme)
|
|
26
28
|
- [Dialogs, toasts or buttons no longer animate](#dialogs-toasts-or-buttons-no-longer-animate)
|
|
27
29
|
- [A Content-Security-Policy reports a `data:` image on every form with a required field](#a-content-security-policy-reports-a-data-image-on-every-form-with-a-required-field)
|
|
28
30
|
- [A Button's label overflows a card on a phone](#a-buttons-label-overflows-a-card-on-a-phone)
|
|
31
|
+
- [A ResponsiveButton no longer shows its tooltip next to its visible label](#a-responsivebutton-no-longer-shows-its-tooltip-next-to-its-visible-label)
|
|
32
|
+
- [An input now has `placeholder=" "` in the DOM](#an-input-now-has-placeholder--in-the-dom)
|
|
29
33
|
- [A dialog or sheet scrolls, or clips a child, after upgrading](#a-dialog-or-sheet-scrolls-or-clips-a-child-after-upgrading)
|
|
30
34
|
- [A form's fields no longer scroll in their own box after upgrading](#a-forms-fields-no-longer-scroll-in-their-own-box-after-upgrading)
|
|
31
35
|
- [axe reports `target-size` on a control under a form's sticky actions](#axe-reports-target-size-on-a-control-under-a-forms-sticky-actions)
|
|
@@ -39,6 +43,8 @@ words in your error.
|
|
|
39
43
|
- [A test no longer finds a date or time picker by the `button` role, or a slider thumb by the exact name `Minimum`](#a-test-no-longer-finds-a-date-or-time-picker-by-the-button-role-or-a-slider-thumb-by-the-exact-name-minimum)
|
|
40
44
|
- [An end-to-end test no longer finds a required field by a name ending in `*`](#an-end-to-end-test-no-longer-finds-a-required-field-by-a-name-ending-in-)
|
|
41
45
|
- [A test no longer finds `aria-invalid` on an upload field's group or drop zone](#a-test-no-longer-finds-aria-invalid-on-an-upload-fields-group-or-drop-zone)
|
|
46
|
+
- [An `Autocomplete` test no longer finds a `textbox` or `button`, or the list no longer opens on focus](#an-autocomplete-test-no-longer-finds-a-textbox-or-button-or-the-list-no-longer-opens-on-focus)
|
|
47
|
+
- [Enter in an `Autocomplete` submits the form instead of picking](#enter-in-an-autocomplete-submits-the-form-instead-of-picking)
|
|
42
48
|
- [A test no longer finds `aria-invalid` on a `Switch` row or an `Autocomplete` box](#a-test-no-longer-finds-aria-invalid-on-a-switch-row-or-an-autocomplete-box)
|
|
43
49
|
- [An `Autocomplete` lists more options than before, or "ecole" finds "École"](#an-autocomplete-lists-more-options-than-before-or-ecole-finds-école)
|
|
44
50
|
- [`@query-change` runs more often after upgrading](#query-change-runs-more-often-after-upgrading)
|
|
@@ -63,6 +69,8 @@ words in your error.
|
|
|
63
69
|
|
|
64
70
|
**Feedback**
|
|
65
71
|
|
|
72
|
+
- [An `EmptyState` shows "Something Went Wrong"](#an-emptystate-shows-something-went-wrong)
|
|
73
|
+
- [An `EmptyState` with an error `variant` is not announced, or `ErrorState` ignores my `empty-state` overrides](#an-emptystate-with-an-error-variant-is-not-announced-or-errorstate-ignores-my-empty-state-overrides)
|
|
66
74
|
- [A toast with an action never goes away](#a-toast-with-an-action-never-goes-away)
|
|
67
75
|
- [A toast covers the form's button on a phone](#a-toast-covers-the-forms-button-on-a-phone)
|
|
68
76
|
- [A toast never goes away after a tap](#a-toast-never-goes-away-after-a-tap)
|
|
@@ -122,6 +130,8 @@ words in your error.
|
|
|
122
130
|
- [`Cannot find module '#standard-fonts/Helvetica'`](#cannot-find-module-standard-fontshelvetica)
|
|
123
131
|
- [`Cannot find module '…/pdfkit/js/standard-fonts/Helvetica.cjs'`](#cannot-find-module-pdfkitjsstandard-fontshelveticacjs)
|
|
124
132
|
- [`still references '#standard-fonts/' after the transform`](#still-references-standard-fonts-after-the-transform)
|
|
133
|
+
- [`[nxgt-material] pdf.bundleStandardFonts is deprecated: use pdf.bundle.`](#nxgt-material-pdfbundlestandardfonts-is-deprecated-use-pdfbundle)
|
|
134
|
+
- [`[nxgt-material] @fontsource-variable/inter is not installed: keeping the system font. Install it to use Inter (bun add @fontsource-variable/inter), or drop fonts: { sans: 'inter' }.`](#nxgt-material-fontsource-variableinter-is-not-installed-keeping-the-system-font-install-it-to-use-inter-bun-add-fontsource-variableinter-or-drop-fonts--sans-inter-)
|
|
125
135
|
- [`renderToStream is a Node specific API` / `renderToBuffer is a Node specific API`](#rendertostream-is-a-node-specific-api--rendertobuffer-is-a-node-specific-api)
|
|
126
136
|
- [PDF rendering fails on an edge runtime](#pdf-rendering-fails-on-an-edge-runtime)
|
|
127
137
|
- [`[pdf] No renderer for the custom node`](#pdf-no-renderer-for-the-custom-node)
|
|
@@ -190,6 +200,7 @@ words in your error.
|
|
|
190
200
|
- [A `DataTable` shows its header row on a phone](#a-datatable-shows-its-header-row-on-a-phone)
|
|
191
201
|
- [A test's `getByRole('region')` finds more than one region](#a-tests-getbyroleregion-finds-more-than-one-region)
|
|
192
202
|
- [A `DataTable` toolbar is gone](#a-datatable-toolbar-is-gone)
|
|
203
|
+
- [A search in the `DataTable` toolbar loses focus while the list reloads](#a-search-in-the-datatable-toolbar-loses-focus-while-the-list-reloads)
|
|
193
204
|
- [axe reports `target-size` on a `DataTable`'s "Select all"](#axe-reports-target-size-on-a-datatables-select-all)
|
|
194
205
|
- [A French test no longer finds the sort menu's "Asc" or "Desc"](#a-french-test-no-longer-finds-the-sort-menus-asc-or-desc)
|
|
195
206
|
|
|
@@ -220,8 +231,19 @@ words in your error.
|
|
|
220
231
|
- [`FilePreview` shows a file card with a Download link for a PDF](#filepreview-shows-a-file-card-with-a-download-link-for-a-pdf)
|
|
221
232
|
- [`FilePreview` says "This file could not be previewed."](#filepreview-says-this-file-could-not-be-previewed)
|
|
222
233
|
|
|
234
|
+
**Backgrounds**
|
|
235
|
+
|
|
236
|
+
- [`Background: preset "<id>" is not available yet; drawing "gradient".`](#background-preset-id-is-not-available-yet-drawing-gradient)
|
|
237
|
+
- [A grain or preset `Background` looks bare, with no error](#a-grain-or-preset-background-looks-bare-with-no-error)
|
|
238
|
+
|
|
223
239
|
**Content Security Policy**
|
|
224
240
|
|
|
241
|
+
- [`[nxgt-material] html.cspNonce is set, but the CSP is in hash mode (the default): set nxgtMaterial({ csp: { mode: 'nonce' } }) or drop html.cspNonce.`](#nxgt-material-htmlcspnonce-is-set-but-the-csp-is-in-hash-mode-the-default-set-nxgtmaterial-csp--mode-nonce---or-drop-htmlcspnonce)
|
|
242
|
+
- [`Invalid CSP nonce: only A-Z, a-z, 0-9, "+", "/", "_", "=" and "-" are allowed, got …`](#invalid-csp-nonce-only-a-z-a-z-0-9---_--and---are-allowed-got-)
|
|
243
|
+
- [`The CSP nonce is the build placeholder "NXGT_CSP_NONCE", which is public: generate one per request with createCspNonce().`](#the-csp-nonce-is-the-build-placeholder-nxgt_csp_nonce-which-is-public-generate-one-per-request-with-createcspnonce)
|
|
244
|
+
- [`[nxgt-material] no index.html in this build: the CSP part did nothing. On a server, send cspPolicy({ nonce }).header and zodJitlessTag(nonce) per request, or pass csp: false.`](#nxgt-material-no-indexhtml-in-this-build-the-csp-part-did-nothing-on-a-server-send-csppolicy-nonce-header-and-zodjitlesstagnonce-per-request-or-pass-csp-false)
|
|
245
|
+
- [`[nxgt-material] @nxgt/material-vue's stylesheet is never imported`](#nxgt-material-nxgtmaterial-vues-stylesheet-is-never-imported)
|
|
246
|
+
- [`[nxgt-material] @fontsource-variable/inter is not installed`](#nxgt-material-fontsource-variableinter-is-not-installed)
|
|
225
247
|
- [`Refused to apply inline style because it violates the following Content Security Policy directive`](#refused-to-apply-inline-style-because-it-violates-the-following-content-security-policy-directive)
|
|
226
248
|
- [`Refused to load the font 'data:font/woff2…' because it violates the following Content Security Policy directive: "font-src 'self'"`](#refused-to-load-the-font-datafontwoff2-because-it-violates-the-following-content-security-policy-directive-font-src-self)
|
|
227
249
|
- [`Refused to load the font 'https://fonts.gstatic.com/…' because it violates the following Content Security Policy directive: "font-src 'self'"`](#refused-to-load-the-font-httpsfontsgstaticcom-because-it-violates-the-following-content-security-policy-directive-font-src-self)
|
|
@@ -604,6 +626,38 @@ const files = page.getByRole('group', { name: 'Attachments' });
|
|
|
604
626
|
await expect(files).toHaveAccessibleDescription('Add at least one file.');
|
|
605
627
|
```
|
|
606
628
|
|
|
629
|
+
### An `Autocomplete` test no longer finds a `textbox` or `button`, or the list no longer opens on focus
|
|
630
|
+
|
|
631
|
+
**When:** after upgrading, `getByRole('textbox')` finds no `Autocomplete` input,
|
|
632
|
+
`getByRole('button', { name: 'Paris' })` finds no option, or focusing the input
|
|
633
|
+
(a dialog that focuses its first field) no longer opens the list.
|
|
634
|
+
**Why:** the `Autocomplete` follows the ARIA 1.2 combobox pattern, as in
|
|
635
|
+
`@nxgt/material`: the input is a `combobox` (`aria-expanded`, `aria-controls`,
|
|
636
|
+
`aria-activedescendant`), the list a `listbox` of `option`s. The list opens on a
|
|
637
|
+
click or on typing, no longer on focus.
|
|
638
|
+
**Fix:** query by the new roles and open the list with a click:
|
|
639
|
+
|
|
640
|
+
```ts
|
|
641
|
+
const input = page.getByRole('combobox', { name: 'Country' });
|
|
642
|
+
await input.click();
|
|
643
|
+
await page.getByRole('option', { name: 'France' }).click();
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
### Enter in an `Autocomplete` submits the form instead of picking
|
|
647
|
+
|
|
648
|
+
**When:** after upgrading, pressing Enter in an `Autocomplete` whose list is
|
|
649
|
+
shut submits the surrounding form, where it used to pick the first option.
|
|
650
|
+
**Why:** a shut list shows nothing to pick, so Enter keeps its form meaning.
|
|
651
|
+
The list opens on a click, on typing or on ArrowDown, and Enter then picks the
|
|
652
|
+
highlighted option (the first enabled one until the arrow keys move it).
|
|
653
|
+
**Fix:** open the list first in your test or flow:
|
|
654
|
+
|
|
655
|
+
```ts
|
|
656
|
+
const input = page.getByRole('combobox', { name: 'Country' });
|
|
657
|
+
await input.press('ArrowDown');
|
|
658
|
+
await input.press('Enter');
|
|
659
|
+
```
|
|
660
|
+
|
|
607
661
|
### A test no longer finds `aria-invalid` on a `Switch` row or an `Autocomplete` box
|
|
608
662
|
|
|
609
663
|
**When:** after upgrading to 0.22.0, `[aria-invalid]` on the bordered row of a
|
|
@@ -1224,6 +1278,29 @@ on a section) changes nothing: `@theme` resolves `--color-error-text` on the
|
|
|
1224
1278
|
root. Below the root, set `--color-error-text` (and `--color-error` for fills)
|
|
1225
1279
|
on that element, or put the override on the root.
|
|
1226
1280
|
|
|
1281
|
+
### An accessibility audit reports low contrast on primary text in the dark theme
|
|
1282
|
+
|
|
1283
|
+
**When:** `Typography tone="primary"`, a link, an icon or a label of your own in
|
|
1284
|
+
the brand colour fails 4.5:1 (text) or 3:1 (icon) in the dark theme, about 2:1
|
|
1285
|
+
on the page; the catalogue's own primary text passes.
|
|
1286
|
+
**Why:** it is drawn with `text-primary`, the fill colour. The brand colour is
|
|
1287
|
+
dark so that near-white text reads on it, which makes it too dark to read as
|
|
1288
|
+
text on the dark surfaces. Primary text has its own token, `--primary-text`:
|
|
1289
|
+
the same colour in light, `--primary` mixed 60 % with white in dark.
|
|
1290
|
+
**Fix:** draw primary text with `text-primary-text`; keep `text-primary` for
|
|
1291
|
+
fills, borders and rings:
|
|
1292
|
+
|
|
1293
|
+
```vue
|
|
1294
|
+
// before
|
|
1295
|
+
<a class="text-primary underline" href="/docs">Docs</a>
|
|
1296
|
+
// after
|
|
1297
|
+
<a class="text-primary-text underline" href="/docs">Docs</a>
|
|
1298
|
+
```
|
|
1299
|
+
|
|
1300
|
+
An app that overrides `--primary` under `:root` and `.dark, [data-theme="dark"]`
|
|
1301
|
+
gets `--primary-text` for free; set `--primary-text` after it to choose another
|
|
1302
|
+
colour. On an element below the root, set `--color-primary-text`.
|
|
1303
|
+
|
|
1227
1304
|
### Dialogs, toasts or buttons no longer animate
|
|
1228
1305
|
|
|
1229
1306
|
**When:** a transition or an animation of a catalogue component does not run
|
|
@@ -1313,6 +1390,21 @@ actions where they are, make them static:
|
|
|
1313
1390
|
An app's own fixed bottom bar or floating action button now sits under the
|
|
1314
1391
|
sticky actions (`z-index: 10`): give it a z-index above 10 (`class="z-20"`).
|
|
1315
1392
|
|
|
1393
|
+
### A link of mine, or of a component, is blue and underlined on hover and focus
|
|
1394
|
+
|
|
1395
|
+
**When:** an `<a>` you render, or one a component renders, takes the info
|
|
1396
|
+
colour, an underline and an info tint on hover and focus that you did not
|
|
1397
|
+
ask for.
|
|
1398
|
+
**Why:** `styles.css` styles every `<a>` as a link in running text. The
|
|
1399
|
+
components' own links opt out; an anchor of yours that has its own look
|
|
1400
|
+
(a card, a menu entry, a button-like link) does not unless you say so.
|
|
1401
|
+
**Fix:** add the `nxgt-plain-link` class to the anchor; it then inherits its
|
|
1402
|
+
text colour and gets no underline or tint. See [Links](guides/theming.md#links).
|
|
1403
|
+
|
|
1404
|
+
```vue
|
|
1405
|
+
<a href="/pricing" class="nxgt-plain-link flex items-center gap-2 p-2">Pricing</a>
|
|
1406
|
+
```
|
|
1407
|
+
|
|
1316
1408
|
### axe reports `target-size` on a control under a form's sticky actions
|
|
1317
1409
|
|
|
1318
1410
|
**When:** since the form's actions stick to the bottom (see above), axe's
|
|
@@ -1333,8 +1425,38 @@ await page.addStyleTag({
|
|
|
1333
1425
|
await expectAccessible(page);
|
|
1334
1426
|
```
|
|
1335
1427
|
|
|
1428
|
+
### A ResponsiveButton no longer shows its tooltip next to its visible label
|
|
1429
|
+
|
|
1430
|
+
**When:** a `ResponsiveButton` with a `tooltip` on a desktop width (768 px and up, or `expanded`), where the tooltip used to appear beside the label.
|
|
1431
|
+
**Why:** the tooltip repeated the label, so it is not opened any more (it is not merely hidden: an open one would still set `aria-describedby` and take the first Escape inside a dialog). It still opens when it says something the label does not, such as `Create order (Ctrl+N)`, and always when the button is collapsed to its icon (below 768 px, or `expanded` false).
|
|
1432
|
+
**Fix:** nothing, if the tooltip repeats the label. To keep one on a wide screen, give it text that adds to the label.
|
|
1433
|
+
|
|
1434
|
+
### An input now has `placeholder=" "` in the DOM
|
|
1435
|
+
|
|
1436
|
+
**When:** a test selects `input:not([placeholder])` or compares an `InputGroupInput` or `InputGroupField` that has no `placeholder` with an attribute-less snapshot.
|
|
1437
|
+
**Why:** the "filled" tint relies on `:placeholder-shown`, which only matches a field that has a placeholder. A text field without one (or with `placeholder=""`) now renders a blank `placeholder=" "`, so an empty field is not tinted as if it held a value. It draws nothing and is not announced.
|
|
1438
|
+
**Fix:** select by role and accessible name (`getByRole('textbox', { name: ... })`) rather than by the missing attribute, and update the snapshot.
|
|
1439
|
+
|
|
1336
1440
|
## Feedback
|
|
1337
1441
|
|
|
1442
|
+
### An `EmptyState` shows "Something Went Wrong"
|
|
1443
|
+
|
|
1444
|
+
**When:** `<EmptyState />` renders the title "Something Went Wrong" (or "Une erreur est survenue") and an error illustration, for a list, a table or a page that is simply empty.
|
|
1445
|
+
**Why:** with neither `kind` nor `variant`, `EmptyState` keeps its old default, `generic`, until 1.0, when it becomes `kind="empty"`.
|
|
1446
|
+
**Fix:** pass `kind` (`empty`, `search`, `not-found` or `unauthorized`). When something did fail, use `ErrorState` instead (see [Feedback & status](components/feedback-and-status.md#errorstate)):
|
|
1447
|
+
|
|
1448
|
+
```vue
|
|
1449
|
+
<EmptyState kind="empty" />
|
|
1450
|
+
<!-- a failure, with a Retry button -->
|
|
1451
|
+
<ErrorState kind="generic" :error="error" @retry="refetch" />
|
|
1452
|
+
```
|
|
1453
|
+
|
|
1454
|
+
### An `EmptyState` with an error `variant` is not announced, or `ErrorState` ignores my `empty-state` overrides
|
|
1455
|
+
|
|
1456
|
+
**When:** an `EmptyState variant="generic"`, `"offline"` or `"maintenance"` is no longer found by `getByRole('alert')`, or the text you set under `material.empty-state.generic|offline|maintenance` does not appear in an `ErrorState`.
|
|
1457
|
+
**Why:** `variant` is deprecated and removed in 1.0. Its error values render the error illustration without `role="alert"`; `ErrorState` is the `role="alert"` block, and it reads its own keys, `material.error-state.<kind>.title` and `description`.
|
|
1458
|
+
**Fix:** replace `<EmptyState variant="offline" />` with `<ErrorState kind="offline" />`, and copy your overrides to `material.error-state.<kind>.*` (see [the i18n guide](guides/i18n.md#empty-and-error-states)).
|
|
1459
|
+
|
|
1338
1460
|
### A toast with an action never goes away
|
|
1339
1461
|
|
|
1340
1462
|
**When:** a test, or a user, waits for a toast to disappear by itself, and one
|
|
@@ -2266,7 +2388,7 @@ appears when the renderer is inlined.
|
|
|
2266
2388
|
// nuxt.config.ts
|
|
2267
2389
|
export default defineNuxtConfig({
|
|
2268
2390
|
modules: ['@nxgt/material-vue/nuxt'],
|
|
2269
|
-
nxgtMaterial: { pdf: {
|
|
2391
|
+
nxgtMaterial: { pdf: { bundle: true } },
|
|
2270
2392
|
});
|
|
2271
2393
|
```
|
|
2272
2394
|
|
|
@@ -2275,11 +2397,10 @@ In a Vite SSR server:
|
|
|
2275
2397
|
```ts
|
|
2276
2398
|
// vite.config.ts
|
|
2277
2399
|
import { defineConfig } from 'vite';
|
|
2278
|
-
import {
|
|
2400
|
+
import { nxgtMaterial } from '@nxgt/material-vue/vite';
|
|
2279
2401
|
|
|
2280
2402
|
export default defineConfig({
|
|
2281
|
-
plugins: [
|
|
2282
|
-
ssr: { noExternal: ['@nxgt/material-vue', '@nxgt/material-core'] },
|
|
2403
|
+
plugins: [...nxgtMaterial({ pdf: { bundle: true } })],
|
|
2283
2404
|
});
|
|
2284
2405
|
```
|
|
2285
2406
|
|
|
@@ -2327,9 +2448,9 @@ export default defineNitroConfig({
|
|
|
2327
2448
|
|
|
2328
2449
|
With `nitro.externals` set to a boolean, the module leaves Nitro's trace
|
|
2329
2450
|
alone: add the `traceInclude` above yourself (as an `externals` object), or
|
|
2330
|
-
inline the server and use `
|
|
2451
|
+
inline the server and use `pdf.bundle`. A server built with
|
|
2331
2452
|
`noExternals` has no `node_modules` to trace into: use
|
|
2332
|
-
`
|
|
2453
|
+
`pdf.bundle` instead ([above](#cannot-find-module-standard-fontshelvetica)).
|
|
2333
2454
|
The module resolves them with `pdfkitStandardFontFiles(rootDir, warn)` from
|
|
2334
2455
|
`@nxgt/material-core/pdfkit-fonts` (Node-only); a Nitro config can call it
|
|
2335
2456
|
too, with `@nxgt/material-core` installed as a direct dependency, in place
|
|
@@ -2338,10 +2459,27 @@ When the module cannot resolve pdfkit's fonts although `@react-pdf/renderer`
|
|
|
2338
2459
|
is installed, `nuxi build` prints one `pdf fonts:` warning naming everything
|
|
2339
2460
|
that failed.
|
|
2340
2461
|
|
|
2462
|
+
### `[nxgt-material] pdf.bundleStandardFonts is deprecated: use pdf.bundle.`
|
|
2463
|
+
|
|
2464
|
+
**When:** at `nuxi build` or `nuxi dev`, as a warning, with the old option.
|
|
2465
|
+
**Why:** the Nuxt module now takes the same `pdf` option as `nxgtMaterial()`.
|
|
2466
|
+
**Fix:** rename it; nothing else changes.
|
|
2467
|
+
|
|
2468
|
+
```ts
|
|
2469
|
+
nxgtMaterial: { pdf: { bundle: true } }, // was pdf: { bundleStandardFonts: true }
|
|
2470
|
+
```
|
|
2471
|
+
|
|
2472
|
+
### `[nxgt-material] @fontsource-variable/inter is not installed: keeping the system font. Install it to use Inter (bun add @fontsource-variable/inter), or drop fonts: { sans: 'inter' }.`
|
|
2473
|
+
|
|
2474
|
+
**When:** at `nuxi build` or `nuxi dev`, after setting `nxgtMaterial: { fonts: { sans: 'inter' } }`. A warning, once; the build succeeds and the page keeps the system font.
|
|
2475
|
+
**Why:** the module loads Inter through `css`, and the package is an optional peer.
|
|
2476
|
+
**Fix:** `bun add @fontsource-variable/inter` to use Inter, or drop the option.
|
|
2477
|
+
In a plain Vite app the same package gives [a different message](#nxgt-material-fontsource-variableinter-is-not-installed).
|
|
2478
|
+
|
|
2341
2479
|
### `still references '#standard-fonts/' after the transform`
|
|
2342
2480
|
|
|
2343
2481
|
**When:** at build, from the `nxgt-material:pdfkit-standard-fonts` plugin
|
|
2344
|
-
(added by `pdfkitStandardFonts()` or the Nuxt `pdf.
|
|
2482
|
+
(added by `nxgtMaterial()`, `pdfkitStandardFonts()` or the Nuxt `pdf.bundle`
|
|
2345
2483
|
option).
|
|
2346
2484
|
**Why:** a pdfkit release changed how it loads its standard fonts, and the
|
|
2347
2485
|
plugin could not rewrite every call. It fails the build on purpose, rather
|
|
@@ -3036,6 +3174,7 @@ await page.getByRole('button', { name: /^(Close|Fermer)$/ }).click();
|
|
|
3036
3174
|
| `breadcrumb` (the `nav`'s name), `More` | `Fil d'Ariane`, `Plus` | `breadcrumb.label`, `breadcrumb.more` |
|
|
3037
3175
|
| `Breadcrumb` (`DocHeader`) | `Fil d'Ariane` | `doc.breadcrumb` |
|
|
3038
3176
|
| `Command Palette`, `Search for a command to run...` (`CommandDialog`) | `Palette de commandes`, `Recherchez une commande à exécuter...` | `command.title`, `command.description` |
|
|
3177
|
+
| `Suggestions` (`CommandList`'s name) | `Commandes suggérées` | `command.list` |
|
|
3039
3178
|
| `carousel`, `slide` (`aria-roledescription`) | `carrousel`, `diapositive` | `carousel.roledescription`, `carousel.slide` |
|
|
3040
3179
|
| `No filters available`, `+2 more` | `Aucun filtre disponible`, `+2 de plus` | `filter.empty`, `filter.more` |
|
|
3041
3180
|
| `Yes` / `No`, `From 5`, `Until …`, `Up to 9` (filter chips) | `Oui` / `Non`, `À partir de 5`, `Jusqu'au …`, `Jusqu'à 9` | `filter.value.*` |
|
|
@@ -3596,6 +3735,19 @@ page.getByRole('region', { name: 'Invoices' });
|
|
|
3596
3735
|
**Why:** the toolbar is rendered only when it has content (yours, or `show-view-options`): an empty `<fieldset>` is a nameless group to a screen reader.
|
|
3597
3736
|
**Fix:** look for what the toolbar holds (a filter by its label, the view options by "View"), not for the `fieldset` itself. A component of yours that renders nothing still counts as content: render it conditionally instead.
|
|
3598
3737
|
|
|
3738
|
+
### A search in the `DataTable` toolbar loses focus while the list reloads
|
|
3739
|
+
|
|
3740
|
+
**When:** a search or filter input is rendered by `actions` or the `#actions` slot, and the list reloads (`loading` turns `true`) while you type: the input loses focus and the keystrokes stop.
|
|
3741
|
+
**Why:** the `actions` band is a `<fieldset disabled={loading}>`, so every native control in it is disabled while the table loads, and a disabled input cannot keep the focus.
|
|
3742
|
+
**Fix:** move the search to the `#filters` slot (or `filters`), which sits beside that fieldset and stays enabled; keep only the actions that act on rows in `#actions`.
|
|
3743
|
+
|
|
3744
|
+
```vue
|
|
3745
|
+
<DataTable :loading="isFetching">
|
|
3746
|
+
<template #filters><SearchField v-model="q" /></template>
|
|
3747
|
+
<template #actions="{ table }">...</template>
|
|
3748
|
+
</DataTable>
|
|
3749
|
+
```
|
|
3750
|
+
|
|
3599
3751
|
### axe reports `target-size` on a `DataTable`'s "Select all"
|
|
3600
3752
|
|
|
3601
3753
|
**When:** axe (WCAG 2.5.8) flags the header row's "Select all" checkbox (`thead [data-slot="checkbox-field"]`) when the column after the `select` column is a sortable `DataTableColumnHeader`, mostly on a phone, where the columns are narrowest. The rows' checkboxes pass.
|
|
@@ -4032,10 +4184,102 @@ beginning is shown, with "Showing the first … of this file." below.
|
|
|
4032
4184
|
|
|
4033
4185
|
---
|
|
4034
4186
|
|
|
4187
|
+
## Backgrounds
|
|
4188
|
+
|
|
4189
|
+
How the presets work is in the [Backgrounds guide](guides/backgrounds.md).
|
|
4190
|
+
|
|
4191
|
+
### `Background: preset "<id>" is not available yet; drawing "gradient".`
|
|
4192
|
+
|
|
4193
|
+
**When:** in development (not when `NODE_ENV` is `production`), the first time a `Background` from `@nxgt/material-vue/backgrounds` gets a `preset` that does not ship yet, or a typo from JavaScript. It is printed once per id.
|
|
4194
|
+
**Why:** `BACKGROUND_PRESETS` lists the planned presets; only the ones in `SHIPPED_BACKGROUND_PRESETS` are drawn today. Any other id draws the gradient.
|
|
4195
|
+
**Fix:** use a shipped preset:
|
|
4196
|
+
|
|
4197
|
+
```ts
|
|
4198
|
+
import { SHIPPED_BACKGROUND_PRESETS } from '@nxgt/material-vue/backgrounds';
|
|
4199
|
+
|
|
4200
|
+
console.log(SHIPPED_BACKGROUND_PRESETS); // ['gradient', 'grain']
|
|
4201
|
+
```
|
|
4202
|
+
|
|
4203
|
+
### A grain or preset `Background` looks bare, with no error
|
|
4204
|
+
|
|
4205
|
+
**When:** `grain` or `preset="grain"` shows no film noise, or a `Background` from `/backgrounds` has only the plain gradient.
|
|
4206
|
+
**Why:** the presets' CSS is opt-in and not part of `styles.css`; without it the grain overlay is not shown, and nothing is reported.
|
|
4207
|
+
**Fix:** import the stylesheet of the presets you use, once:
|
|
4208
|
+
|
|
4209
|
+
```ts
|
|
4210
|
+
import '@nxgt/material-vue/backgrounds.css'; // every preset
|
|
4211
|
+
// or one file: '@nxgt/material-vue/backgrounds/grain.css'
|
|
4212
|
+
```
|
|
4213
|
+
|
|
4214
|
+
---
|
|
4215
|
+
|
|
4035
4216
|
## Content Security Policy
|
|
4036
4217
|
|
|
4037
4218
|
How to set up the policy is in the [Content Security Policy guide](guides/csp.md).
|
|
4038
4219
|
|
|
4220
|
+
### `[nxgt-material] html.cspNonce is set, but the CSP is in hash mode (the default): set nxgtMaterial({ csp: { mode: 'nonce' } }) or drop html.cspNonce.`
|
|
4221
|
+
|
|
4222
|
+
**When:** `vite build` or `vite` with `nxgtMaterial()` in its default hash mode, while your config sets Vite's `html.cspNonce`.
|
|
4223
|
+
|
|
4224
|
+
**Why:** hash mode writes no nonce into the HTML; a `html.cspNonce` would put a fixed value there that the plugin's header does not match.
|
|
4225
|
+
|
|
4226
|
+
**Fix:** drop `html.cspNonce`, or choose nonce mode, where the plugin sets it for you:
|
|
4227
|
+
|
|
4228
|
+
```ts
|
|
4229
|
+
nxgtMaterial({ csp: { mode: 'nonce' } })
|
|
4230
|
+
```
|
|
4231
|
+
|
|
4232
|
+
### `Invalid CSP nonce: only A-Z, a-z, 0-9, "+", "/", "_", "=" and "-" are allowed, got …`
|
|
4233
|
+
|
|
4234
|
+
**When:** `nxgtMaterial({ csp: { mode: 'nonce' } })` with a `csp.placeholder` (or an `html.cspNonce`) that is empty or holds a character outside the base64 alphabet, such as a quote or a space.
|
|
4235
|
+
|
|
4236
|
+
**Fix:** leave `csp.placeholder` out (the default is `NXGT_CSP_NONCE`), or use only letters, digits, `+`, `/`, `_`, `-` and `=`.
|
|
4237
|
+
|
|
4238
|
+
### `The CSP nonce is the build placeholder "NXGT_CSP_NONCE", which is public: generate one per request with createCspNonce().`
|
|
4239
|
+
|
|
4240
|
+
**When:** a server hands `applyCspNonce` or `cspPolicy` the nonce the build left in the HTML (`NXGT_CSP_NONCE`, or your `csp.placeholder`) instead of a fresh one.
|
|
4241
|
+
|
|
4242
|
+
**Why:** the placeholder is written in the built HTML, so anyone can read it; a policy that trusts it trusts anything.
|
|
4243
|
+
|
|
4244
|
+
**Fix:** generate a nonce per request and use the same one in the page and in the header:
|
|
4245
|
+
|
|
4246
|
+
```ts
|
|
4247
|
+
import { applyCspNonce, createCspNonce, cspPolicy } from '@nxgt/material-vue/lib';
|
|
4248
|
+
|
|
4249
|
+
const nonce = createCspNonce();
|
|
4250
|
+
const page = applyCspNonce(builtHtml, nonce);
|
|
4251
|
+
const { header } = cspPolicy({ nonce });
|
|
4252
|
+
```
|
|
4253
|
+
|
|
4254
|
+
The server helpers' other messages (`Invalid CSP hash`, `Invalid CSP directive name` and `Invalid CSP source`, `A CSP nonce needs at least 16 random bytes`, `applyCspNonce: the HTML holds no "NXGT_CSP_NONCE" placeholder`) are in `@nxgt/material-core`'s [troubleshooting page](https://cdn.jsdelivr.net/npm/@nxgt/material-core@0/docs/troubleshooting.md#content-security-policy).
|
|
4255
|
+
|
|
4256
|
+
### `[nxgt-material] no index.html in this build: the CSP part did nothing. On a server, send cspPolicy({ nonce }).header and zodJitlessTag(nonce) per request, or pass csp: false.`
|
|
4257
|
+
|
|
4258
|
+
**When:** a client `vite build` that emits no `index.html` (Nuxt, a library build, an SSR framework's client build). It is a warning, printed once, not an error.
|
|
4259
|
+
|
|
4260
|
+
**Why:** the CSP part hashes the inline scripts of the built HTML, or leaves a nonce placeholder in it. With no HTML there is nothing to do. The fonts part still works.
|
|
4261
|
+
|
|
4262
|
+
**Fix:** on a server, send `cspPolicy({ nonce }).header` and put `zodJitlessTag(nonce)` in the head of every response (both from `@nxgt/material-vue/lib`), or silence the warning with `nxgtMaterial({ csp: false })`.
|
|
4263
|
+
|
|
4264
|
+
### `[nxgt-material] @nxgt/material-vue's stylesheet is never imported`
|
|
4265
|
+
|
|
4266
|
+
**When:** `vite build` with `nxgtMaterial()` finds no stylesheet or module that imports `@nxgt/material-vue/styles.css`, and no stylesheet that imports the compiled one (`@import "@nxgt/material-vue";`); every component would render unstyled.
|
|
4267
|
+
|
|
4268
|
+
**Fix:** add the line the message prints to your main stylesheet, after Tailwind's own import:
|
|
4269
|
+
|
|
4270
|
+
```css
|
|
4271
|
+
@import "@nxgt/material-vue/styles.css";
|
|
4272
|
+
```
|
|
4273
|
+
|
|
4274
|
+
or import `@nxgt/material-vue/styles.css` from your entry. An app without Tailwind imports the compiled stylesheet instead, `@import "@nxgt/material-vue";`, which the check accepts too. A build that has no use for the stylesheet (a Storybook build, say) opts out with `nxgtMaterial({ styles: false })`.
|
|
4275
|
+
|
|
4276
|
+
### `[nxgt-material] @fontsource-variable/inter is not installed`
|
|
4277
|
+
|
|
4278
|
+
**When:** `nxgtMaterial()` loads Inter only when the optional peer is installed, and it is not. A warning, once, in `vite dev` and in `vite build`; the build still succeeds and the page keeps the system font stack.
|
|
4279
|
+
In Nuxt the module reports [another message](#nxgt-material-fontsource-variableinter-is-not-installed-keeping-the-system-font-install-it-to-use-inter-bun-add-fontsource-variableinter-or-drop-fonts--sans-inter-).
|
|
4280
|
+
|
|
4281
|
+
**Fix:** `bun add @fontsource-variable/inter` to use Inter, or keep the system font and silence the warning with `nxgtMaterial({ fonts: { sans: false } })`.
|
|
4282
|
+
|
|
4039
4283
|
### `Refused to apply inline style because it violates the following Content Security Policy directive`
|
|
4040
4284
|
|
|
4041
4285
|
**When:** a component mounts, or a modal opens, on a page that sends
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/* Every background preset's CSS, opt-in: `import "@nxgt/material/backgrounds.css"`
|
|
2
|
+
* (the same file in @nxgt/material-vue). Not part of styles.css, which carries
|
|
3
|
+
* only the base, the gradient and the motion `Background` (from /components)
|
|
4
|
+
* needs. One preset alone: `@nxgt/material/backgrounds/<preset>.css`. */
|
|
5
|
+
@import "./css/base.css";
|
|
6
|
+
@import "./css/gradient.css";
|
|
7
|
+
@import "./css/grain.css";
|
|
8
|
+
@import "./css/motion.css";
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/* Background presets, the base: the frame, the tone and intensity variables,
|
|
2
|
+
* the dark lift, the paused state and forced colours. Plain CSS keyed on
|
|
3
|
+
* `data-background`, which `Background` sets itself, after the caller's
|
|
4
|
+
* props, so a wrapper's own `data-slot` (ThreadLayout, ActivityLayout) cannot
|
|
5
|
+
* unhook it. No Tailwind classes and no inline `style`, so a strict CSP
|
|
6
|
+
* blocks nothing. Shared byte for byte by @nxgt/material and
|
|
7
|
+
* @nxgt/material-vue (scripts/shared-paths.ts).
|
|
8
|
+
*
|
|
9
|
+
* Everything sits in `@layer components`: a utility the app puts on the
|
|
10
|
+
* element (`h-96 w-full`, `overflow-hidden`) is in a later layer and wins.
|
|
11
|
+
*
|
|
12
|
+
* Tones become `--nxgt-bg-t1..t3` (the tokens a tone draws from) and the
|
|
13
|
+
* drawn colours `--nxgt-bg-c1..c3`; an app may override the latter on the
|
|
14
|
+
* element. In dark the drawn colours are lifted to a lightness floor: the dark
|
|
15
|
+
* `--primary` is as dim as the light one, which made the gradient dull. An app
|
|
16
|
+
* sets `--nxgt-bg-custom-1..3` in its own CSS for `tone="custom"`. */
|
|
17
|
+
@layer components {
|
|
18
|
+
[data-background] {
|
|
19
|
+
--nxgt-bg-floor: 0.64;
|
|
20
|
+
--nxgt-bg-k: 1;
|
|
21
|
+
--nxgt-bg-blob: 1;
|
|
22
|
+
--nxgt-bg-t1: var(--primary);
|
|
23
|
+
--nxgt-bg-t2: var(--secondary);
|
|
24
|
+
--nxgt-bg-t3: var(--primary);
|
|
25
|
+
--nxgt-bg-c1: var(--nxgt-bg-t1);
|
|
26
|
+
--nxgt-bg-c2: var(--nxgt-bg-t2);
|
|
27
|
+
--nxgt-bg-c3: var(--nxgt-bg-t3);
|
|
28
|
+
position: relative;
|
|
29
|
+
display: flex;
|
|
30
|
+
flex-direction: column;
|
|
31
|
+
width: 100lvw;
|
|
32
|
+
height: 100lvh;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* `fill="container"`: the size of the parent, not of the viewport. */
|
|
36
|
+
[data-background][data-fill="container"] {
|
|
37
|
+
width: 100%;
|
|
38
|
+
height: 100%;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
[data-background][data-tone="secondary"] {
|
|
42
|
+
--nxgt-bg-t1: var(--secondary);
|
|
43
|
+
--nxgt-bg-t2: var(--primary);
|
|
44
|
+
--nxgt-bg-t3: var(--secondary);
|
|
45
|
+
}
|
|
46
|
+
[data-background][data-tone="tertiary"] {
|
|
47
|
+
--nxgt-bg-t1: var(--tertiary);
|
|
48
|
+
--nxgt-bg-t2: var(--primary);
|
|
49
|
+
--nxgt-bg-t3: var(--tertiary);
|
|
50
|
+
}
|
|
51
|
+
[data-background][data-tone="neutral"] {
|
|
52
|
+
--nxgt-bg-t1: var(--muted-foreground);
|
|
53
|
+
--nxgt-bg-t2: var(--foreground);
|
|
54
|
+
--nxgt-bg-t3: var(--muted-foreground);
|
|
55
|
+
}
|
|
56
|
+
[data-background][data-tone="success"] {
|
|
57
|
+
--nxgt-bg-t1: var(--success);
|
|
58
|
+
--nxgt-bg-t2: var(--info);
|
|
59
|
+
--nxgt-bg-t3: var(--success);
|
|
60
|
+
}
|
|
61
|
+
[data-background][data-tone="warning"] {
|
|
62
|
+
--nxgt-bg-t1: var(--warning);
|
|
63
|
+
--nxgt-bg-t2: var(--error);
|
|
64
|
+
--nxgt-bg-t3: var(--warning);
|
|
65
|
+
}
|
|
66
|
+
[data-background][data-tone="error"] {
|
|
67
|
+
--nxgt-bg-t1: var(--error);
|
|
68
|
+
--nxgt-bg-t2: var(--warning);
|
|
69
|
+
--nxgt-bg-t3: var(--error);
|
|
70
|
+
}
|
|
71
|
+
[data-background][data-tone="info"] {
|
|
72
|
+
--nxgt-bg-t1: var(--info);
|
|
73
|
+
--nxgt-bg-t2: var(--primary);
|
|
74
|
+
--nxgt-bg-t3: var(--info);
|
|
75
|
+
}
|
|
76
|
+
[data-background][data-tone="mixed"] {
|
|
77
|
+
--nxgt-bg-t1: var(--primary);
|
|
78
|
+
--nxgt-bg-t2: var(--secondary);
|
|
79
|
+
--nxgt-bg-t3: var(--tertiary);
|
|
80
|
+
}
|
|
81
|
+
[data-background][data-tone="custom"] {
|
|
82
|
+
--nxgt-bg-t1: var(--nxgt-bg-custom-1, var(--primary));
|
|
83
|
+
--nxgt-bg-t2: var(--nxgt-bg-custom-2, var(--secondary));
|
|
84
|
+
--nxgt-bg-t3: var(--nxgt-bg-custom-3, var(--primary));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* Intensity: `--nxgt-bg-k` scales the frame's tint, `--nxgt-bg-blob` the blobs'
|
|
88
|
+
* opacity. The default is 1 and 1, today's look. */
|
|
89
|
+
[data-background][data-intensity="subtle"] {
|
|
90
|
+
--nxgt-bg-k: 0.5;
|
|
91
|
+
--nxgt-bg-blob: 0.6;
|
|
92
|
+
}
|
|
93
|
+
[data-background][data-intensity="vivid"] {
|
|
94
|
+
--nxgt-bg-k: 1.6;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Dark: lift the drawn colours. The fallback mixes with white; the
|
|
98
|
+
* relative colour syntax (Chrome 119, Safari 18, Firefox 128) replaces it
|
|
99
|
+
* where it is supported. */
|
|
100
|
+
:is(.dark, [data-theme="dark"]) [data-background],
|
|
101
|
+
[data-background]:is(.dark, [data-theme="dark"]) {
|
|
102
|
+
--nxgt-bg-c1: color-mix(in oklab, var(--nxgt-bg-t1) 70%, white);
|
|
103
|
+
--nxgt-bg-c2: color-mix(in oklab, var(--nxgt-bg-t2) 70%, white);
|
|
104
|
+
--nxgt-bg-c3: color-mix(in oklab, var(--nxgt-bg-t3) 70%, white);
|
|
105
|
+
}
|
|
106
|
+
@supports (color: oklch(from red max(l, 0.5) c h)) {
|
|
107
|
+
:is(.dark, [data-theme="dark"]) [data-background],
|
|
108
|
+
[data-background]:is(.dark, [data-theme="dark"]) {
|
|
109
|
+
--nxgt-bg-c1: oklch(
|
|
110
|
+
from var(--nxgt-bg-t1) max(l, var(--nxgt-bg-floor)) c h
|
|
111
|
+
);
|
|
112
|
+
--nxgt-bg-c2: oklch(
|
|
113
|
+
from var(--nxgt-bg-t2) max(l, var(--nxgt-bg-floor)) c h
|
|
114
|
+
);
|
|
115
|
+
--nxgt-bg-c3: oklch(
|
|
116
|
+
from var(--nxgt-bg-t3) max(l, var(--nxgt-bg-floor)) c h
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* The decorative layers never take a pointer; the content keeps its own. */
|
|
122
|
+
[data-background] > [aria-hidden="true"] {
|
|
123
|
+
pointer-events: none;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* An SVG scene (`data-scene`) and an overlay (`data-overlay`, the grain)
|
|
127
|
+
* fill the frame, under the content. The SVG slices to cover it. An
|
|
128
|
+
* overlay stays hidden until its own stylesheet shows it (grain.css), so
|
|
129
|
+
* without that file it paints nothing. */
|
|
130
|
+
[data-background] > [data-overlay] {
|
|
131
|
+
display: none;
|
|
132
|
+
}
|
|
133
|
+
[data-background] > :is([data-scene], [data-overlay]) {
|
|
134
|
+
position: absolute;
|
|
135
|
+
inset: 0;
|
|
136
|
+
}
|
|
137
|
+
[data-background] > :is([data-scene], [data-overlay]) > svg {
|
|
138
|
+
display: block;
|
|
139
|
+
width: 100%;
|
|
140
|
+
height: 100%;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
[data-background] > [data-slot="background-content"] {
|
|
144
|
+
display: flex;
|
|
145
|
+
flex: 1 1 0%;
|
|
146
|
+
width: 100%;
|
|
147
|
+
height: 100%;
|
|
148
|
+
z-index: 5;
|
|
149
|
+
align-items: center;
|
|
150
|
+
justify-content: center;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/* Paused (hidden page or off screen): the motion rules' `animation`
|
|
154
|
+
* shorthands read `--nxgt-bg-play`, so this one declaration stops the
|
|
155
|
+
* decoration whatever the specificity. The content does not read it: an
|
|
156
|
+
* app's own animations under `background-content` keep running. */
|
|
157
|
+
[data-background][data-paused] {
|
|
158
|
+
--nxgt-bg-play: paused;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
@media (forced-colors: active) {
|
|
162
|
+
[data-background] {
|
|
163
|
+
background-image: none;
|
|
164
|
+
}
|
|
165
|
+
[data-background] > [aria-hidden="true"] {
|
|
166
|
+
display: none;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/* The `gradient` preset: the frame's tint and three blurred blobs, what
|
|
2
|
+
* `Background` always looked like (a primary tint from 5% to 20%, blobs
|
|
3
|
+
* going primary to secondary), now drawn from the tone's colours so it lifts
|
|
4
|
+
* in dark. The layers are `aria-hidden` divs, see base.css. */
|
|
5
|
+
@layer components {
|
|
6
|
+
[data-background="gradient"] {
|
|
7
|
+
background-image: linear-gradient(
|
|
8
|
+
to right in oklab,
|
|
9
|
+
color-mix(
|
|
10
|
+
in oklab,
|
|
11
|
+
var(--nxgt-bg-c1) calc(5% * var(--nxgt-bg-k)),
|
|
12
|
+
transparent
|
|
13
|
+
),
|
|
14
|
+
color-mix(
|
|
15
|
+
in oklab,
|
|
16
|
+
var(--nxgt-bg-c1) calc(20% * var(--nxgt-bg-k)),
|
|
17
|
+
transparent
|
|
18
|
+
)
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
[data-background="gradient"] > [data-layer] {
|
|
23
|
+
position: absolute;
|
|
24
|
+
aspect-ratio: 1 / 1;
|
|
25
|
+
border-radius: 9999px;
|
|
26
|
+
filter: blur(64px);
|
|
27
|
+
opacity: var(--nxgt-bg-blob);
|
|
28
|
+
background-image: linear-gradient(
|
|
29
|
+
to right in oklab,
|
|
30
|
+
var(--nxgt-bg-c1),
|
|
31
|
+
var(--nxgt-bg-c2)
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
[data-background="gradient"] > [data-layer="blob-1"] {
|
|
35
|
+
bottom: 33.333333%;
|
|
36
|
+
width: 8rem;
|
|
37
|
+
}
|
|
38
|
+
[data-background="gradient"] > [data-layer="blob-2"] {
|
|
39
|
+
right: 0;
|
|
40
|
+
bottom: 0;
|
|
41
|
+
width: 8rem;
|
|
42
|
+
max-width: 28rem;
|
|
43
|
+
}
|
|
44
|
+
[data-background="gradient"] > [data-layer="blob-3"] {
|
|
45
|
+
right: 50%;
|
|
46
|
+
width: 10rem;
|
|
47
|
+
background-image: linear-gradient(
|
|
48
|
+
to right in oklab,
|
|
49
|
+
color-mix(in oklab, var(--nxgt-bg-c1) 60%, transparent),
|
|
50
|
+
color-mix(in oklab, var(--nxgt-bg-c2) 60%, transparent)
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/* The grain: film noise over any preset. `Background` draws it as an
|
|
2
|
+
* `aria-hidden` SVG (an `feTurbulence` filter) with `data-overlay="grain"`
|
|
3
|
+
* when `grain` is set, or for `preset="grain"`, which is the gradient plus
|
|
4
|
+
* this overlay. It sits above the preset's layers and under the content; the
|
|
5
|
+
* intensity scales its opacity. Plain CSS, no inline `style`. The look may
|
|
6
|
+
* still change. */
|
|
7
|
+
@layer components {
|
|
8
|
+
[data-background][data-grain] > [data-overlay="grain"] {
|
|
9
|
+
display: block;
|
|
10
|
+
opacity: calc(0.16 * var(--nxgt-bg-k));
|
|
11
|
+
mix-blend-mode: soft-light;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@media (forced-colors: active) {
|
|
15
|
+
[data-background][data-grain] > [data-overlay="grain"] {
|
|
16
|
+
display: none;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|