@nxgt/material-vue 0.41.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.
Files changed (161) hide show
  1. package/CHANGELOG.md +124 -0
  2. package/README.md +20 -6
  3. package/dist/{Button-BG3oDkrl.js → Button-C3K6dhqd.js} +4 -4
  4. package/dist/{HoverCardTrigger-Dh1ar_W4.js → HoverCardTrigger-Bga4YjEr.js} +69 -62
  5. package/dist/backgrounds/backgrounds.css +8 -0
  6. package/dist/backgrounds/css/base.css +169 -0
  7. package/dist/backgrounds/css/gradient.css +53 -0
  8. package/dist/backgrounds/css/grain.css +19 -0
  9. package/dist/backgrounds/css/motion.css +30 -0
  10. package/dist/backgrounds.js +111 -0
  11. package/dist/{camera-DV9jcl_3.js → camera-BbF9_-ys.js} +4 -4
  12. package/dist/components/base.css +5 -2
  13. package/dist/components/components.css +5 -0
  14. package/dist/components/theme-tokens.css +21 -0
  15. package/dist/{components-C1JYYoQ4.js → components-yCdbry6Q.js} +13880 -13101
  16. package/dist/components.js +8 -8
  17. package/dist/composables.js +3 -3
  18. package/dist/dnd.js +1 -1
  19. package/dist/{form-scope-CeD0v_mw.js → form-scope-CkWJrBCu.js} +1 -1
  20. package/dist/{i18n-provider-D5XyLQ3P.js → i18n-provider-tL27-a8y.js} +1 -1
  21. package/dist/i18n.js +4 -4
  22. package/dist/{icon-button-DXrmfs6e.js → icon-button-MyFFJpXA.js} +4 -4
  23. package/dist/lib/backgrounds/background/background.vue.d.ts +22 -0
  24. package/dist/lib/backgrounds/background/index.d.ts +2 -0
  25. package/dist/lib/backgrounds/background/partials/background-layers.vue.d.ts +9 -0
  26. package/dist/lib/backgrounds/background/partials/scene-svg.d.ts +10 -0
  27. package/dist/lib/backgrounds/background/types.d.ts +24 -0
  28. package/dist/lib/backgrounds/index.d.ts +13 -0
  29. package/dist/lib/components/ui/actions/button/button-tones.d.ts +3 -3
  30. package/dist/lib/components/ui/actions/responsive-button/responsive-button.vue.d.ts +3 -3
  31. package/dist/lib/components/ui/collections/data-table/data-table-context.d.ts +4 -0
  32. package/dist/lib/components/ui/collections/data-table/data-table.vue.d.ts +8 -0
  33. package/dist/lib/components/ui/collections/data-table/partials/data-table-body.vue.d.ts +8 -0
  34. package/dist/lib/components/ui/collections/data-table/partials/data-table-empty.vue.d.ts +5 -1
  35. package/dist/lib/components/ui/collections/data-table/partials/data-table-toolbar.d.ts +16 -3
  36. package/dist/lib/components/ui/collections/data-table/select-checkbox.d.ts +36 -0
  37. package/dist/lib/components/ui/collections/data-table/types.d.ts +15 -0
  38. package/dist/lib/components/ui/collections/data-table/use-data-table.d.ts +2 -0
  39. package/dist/lib/components/ui/feedback/circular-progress/circular-progress.vue.d.ts +1 -1
  40. package/dist/lib/components/ui/feedback/empty-state/empty-state.vue.d.ts +21 -8
  41. package/dist/lib/components/ui/feedback/empty-state/index.d.ts +1 -1
  42. package/dist/lib/components/ui/feedback/error-state/error-state.vue.d.ts +54 -0
  43. package/dist/lib/components/ui/feedback/error-state/index.d.ts +3 -0
  44. package/dist/lib/components/ui/feedback/error-state/partials/error-state-details.vue.d.ts +7 -0
  45. package/dist/lib/components/ui/feedback/index.d.ts +1 -0
  46. package/dist/lib/components/ui/feedback/state-frame/state-frame.vue.d.ts +36 -0
  47. package/dist/lib/components/ui/feedback/state-frame/state-icon.vue.d.ts +7 -0
  48. package/dist/lib/components/ui/feedback/state-frame/state-illustration.vue.d.ts +8 -0
  49. package/dist/lib/components/ui/feedback/state-frame/state-tones.d.ts +11 -0
  50. package/dist/lib/components/ui/fields/contacts/consts.d.ts +1 -1
  51. package/dist/lib/components/ui/layout/account-menu/account-menu-avatar.vue.d.ts +9 -0
  52. package/dist/lib/components/ui/layout/account-menu/account-menu.vue.d.ts +38 -0
  53. package/dist/lib/components/ui/layout/account-menu/index.d.ts +3 -0
  54. package/dist/lib/components/ui/layout/account-menu/types.d.ts +10 -0
  55. package/dist/lib/components/ui/layout/app-shell/activity-layout.vue.d.ts +21 -5
  56. package/dist/lib/components/ui/layout/app-shell/app-brand-logo.vue.d.ts +20 -0
  57. package/dist/lib/components/ui/layout/app-shell/app-brand.vue.d.ts +30 -0
  58. package/dist/lib/components/ui/layout/app-shell/app-header.vue.d.ts +13 -5
  59. package/dist/lib/components/ui/layout/app-shell/app-sidebar.vue.d.ts +12 -2
  60. package/dist/lib/components/ui/layout/app-shell/index.d.ts +1 -1
  61. package/dist/lib/components/ui/layout/app-shell/types.d.ts +10 -0
  62. package/dist/lib/components/ui/layout/app-shell/use-truncated-title.d.ts +10 -0
  63. package/dist/lib/components/ui/layout/background/background.vue.d.ts +18 -3
  64. package/dist/lib/components/ui/layout/doc/partials/doc-empty.vue.d.ts +7 -4
  65. package/dist/lib/components/ui/layout/index.d.ts +1 -0
  66. package/dist/lib/components/ui/media/stx-player/stx-player-viewport.vue.d.ts +2 -2
  67. package/dist/lib/components/ui/overlays/command/CommandSeparator.vue.d.ts +3 -1
  68. package/dist/lib/components/ui/overlays/dialog/in-dialog.d.ts +1 -1
  69. package/dist/lib/components/ui/overlays/dialog/use-escape-forward.d.ts +13 -0
  70. package/dist/lib/components/ui/overlays/dialog/use-modal-content.d.ts +2 -0
  71. package/dist/lib/components/ui/primitives/nonce-provider/nonce-provider.vue.d.ts +4 -1
  72. package/dist/lib/components/ui/selection/autocomplete/autocomplete-keyboard.d.ts +22 -4
  73. package/dist/lib/components/ui/selection/autocomplete/partials/autocomplete-box.vue.d.ts +6 -2
  74. package/dist/lib/components/ui/selection/autocomplete/partials/autocomplete-list.vue.d.ts +7 -0
  75. package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-active.d.ts +17 -0
  76. package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-keydown.d.ts +21 -0
  77. package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-open.d.ts +3 -3
  78. package/dist/lib/components/ui/selection/autocomplete/use-autocomplete.d.ts +2 -1
  79. package/dist/lib/components/ui/selection/autocomplete/use-drawer-room.d.ts +11 -0
  80. package/dist/lib/components/ui/selection/autocomplete/use-query-change.d.ts +8 -0
  81. package/dist/lib/components/ui/widgets/charts/chart-container.vue.d.ts +2 -2
  82. package/dist/lib/components/ui/widgets/kanban/partials/kanban-empty.vue.d.ts +1 -0
  83. package/dist/lib/composables/use-background-activity.d.ts +8 -0
  84. package/dist/lib/i18n/resources/en/en.d.ts +28 -0
  85. package/dist/lib/i18n/resources/en/material.json.d.ts +29 -1
  86. package/dist/lib/i18n/resources/fr/fr.d.ts +28 -0
  87. package/dist/lib/i18n/resources/fr/material.json.d.ts +29 -1
  88. package/dist/lib/i18n/resources/resources.d.ts +56 -0
  89. package/dist/lib/i18n/translator.d.ts +1 -1
  90. package/dist/lib/lib/index.d.ts +1 -0
  91. package/dist/lib/nuxt/component-names.d.ts +1 -1
  92. package/dist/lib/nuxt/module.d.ts +2 -11
  93. package/dist/lib/nuxt/pdf-fonts.d.ts +1 -1
  94. package/dist/lib/nuxt/plugin-options.d.ts +58 -0
  95. package/dist/lib/vite/index.d.ts +4 -8
  96. package/dist/lib.js +52 -51
  97. package/dist/main.js +17 -17
  98. package/dist/map.js +4 -4
  99. package/dist/material-vue.css +1 -1
  100. package/dist/{pdf-outline-sidebar-CgmJjhUz.js → pdf-outline-sidebar-Kqsfd_cO.js} +2 -2
  101. package/dist/{pdf-page-BjQNWV72.js → pdf-page-Dx3jwxu6.js} +1 -1
  102. package/dist/{pdf-page-list-nG0l4SjH.js → pdf-page-list-BsQmEHaY.js} +2 -2
  103. package/dist/{pdf-reader-Dekd4Ymw.js → pdf-reader-CJo8SQVl.js} +15 -15
  104. package/dist/{pdf-sidebar-gDj63dRH.js → pdf-sidebar-CcCUATl-.js} +3 -3
  105. package/dist/{pdf-thumbnail-sidebar-C5ikii1Z.js → pdf-thumbnail-sidebar-BRxNRGfG.js} +4 -4
  106. package/dist/{pdf-toolbar-DmmrSlCq.js → pdf-toolbar-B0ckYBJ2.js} +4 -4
  107. package/dist/pdf.js +2 -2
  108. package/dist/{print-format.utils-CiUiQE-i.js → print-format.utils-51joHpFb.js} +2 -2
  109. package/dist/{sortable-provider-lK0srHJF.js → sortable-provider-BEx1B0Ky.js} +2 -2
  110. package/dist/{stored-language-dM2e5qaS.js → stored-language-DVP9KzRu.js} +58 -2
  111. package/dist/{stx-player-C2VQCXFc.js → stx-player-KPp9c7vS.js} +5 -5
  112. package/dist/styles.css +4 -0
  113. package/dist/{translator-DXL5RDRg.js → translator-Bm7i7dv-.js} +1 -1
  114. package/dist/use-background-activity-DdTBbNpX.js +17 -0
  115. package/dist/{use-heading-spy-Btwc0yad.js → use-heading-spy-jn8f2ioE.js} +13 -13
  116. package/dist/{use-material-translation-Du3Wvsk1.js → use-material-translation-CfFZPSe0.js} +3 -3
  117. package/dist/vite.js +7 -2
  118. package/docs/README.md +3 -0
  119. package/docs/components/README.md +2 -1
  120. package/docs/components/buttons-and-actions.md +2 -0
  121. package/docs/components/data-table-filter-and-lists.md +59 -8
  122. package/docs/components/feedback-and-status.md +79 -8
  123. package/docs/components/media-and-files.md +2 -0
  124. package/docs/components/navigation-and-layout.md +68 -5
  125. package/docs/components/overlays-and-menus.md +20 -3
  126. package/docs/components/primitives.md +5 -3
  127. package/docs/components/rich-widgets.md +1 -1
  128. package/docs/components/selection-and-choice.md +14 -4
  129. package/docs/getting-started.md +22 -3
  130. package/docs/guides/app-shell.md +99 -19
  131. package/docs/guides/backgrounds.md +143 -0
  132. package/docs/guides/csp.md +48 -6
  133. package/docs/guides/helpers.md +1 -0
  134. package/docs/guides/i18n.md +52 -2
  135. package/docs/guides/migrate-to-vite-plugin.md +259 -0
  136. package/docs/guides/pdf.md +11 -4
  137. package/docs/guides/ssr.md +1 -1
  138. package/docs/guides/theming.md +52 -4
  139. package/docs/guides/vite-plugin.md +194 -0
  140. package/docs/previews/backgrounds-matrix.webp +0 -0
  141. package/docs/previews/feedback-status-emptystate.webp +0 -0
  142. package/docs/previews/feedback-status-errorstate.webp +0 -0
  143. package/docs/previews/navigation-layout-accountmenu.webp +0 -0
  144. package/docs/roadmap.md +69 -32
  145. package/docs/troubleshooting.md +251 -7
  146. package/lib/backgrounds/backgrounds.css +8 -0
  147. package/lib/backgrounds/css/base.css +169 -0
  148. package/lib/backgrounds/css/gradient.css +53 -0
  149. package/lib/backgrounds/css/grain.css +19 -0
  150. package/lib/backgrounds/css/motion.css +30 -0
  151. package/lib/components/base.css +5 -2
  152. package/lib/components/components.css +5 -0
  153. package/lib/components/theme-tokens.css +21 -0
  154. package/lib/nuxt/component-names.ts +2 -0
  155. package/lib/nuxt/module.ts +29 -15
  156. package/lib/nuxt/pdf-fonts.ts +1 -1
  157. package/lib/nuxt/plugin-options.ts +92 -0
  158. package/lib/nuxt/runtime/inter.css +6 -0
  159. package/lib/styles.css +4 -0
  160. package/lib/vite/index.ts +19 -2
  161. package/package.json +24 -2
@@ -40,6 +40,10 @@ build:
40
40
  and `[data-theme="dark"]`;
41
41
  - a `@theme` block that maps each token to a Tailwind colour, so `bg-primary`,
42
42
  `text-error-foreground`, `border-border` are real utilities;
43
+ - the **background presets' base, gradient and motion CSS** (`lib/backgrounds/css/`),
44
+ which `Background` from `/components` draws with: plain CSS keyed on
45
+ `data-background`, in `@layer components`, so a utility on the element wins.
46
+ The other presets are opt-in ([backgrounds](backgrounds.md));
43
47
  - a `dark` custom variant, base styles (scrollbars, borders, selection, body
44
48
  colours), and a few utilities: `center`, `size-lv`, `col-start-full`,
45
49
  `h-fill`, `w-fill`, `size-fill`.
@@ -51,10 +55,12 @@ build:
51
55
  | `--background`, `--foreground` | `bg-background`, `text-foreground` | page and text |
52
56
  | `--primary`, `--primary-foreground` | `bg-primary`, `text-primary-foreground` | filled buttons, active states, focus |
53
57
  | `--secondary`, `--secondary-foreground` | `bg-secondary`, … | the secondary colour |
58
+ | `--tertiary`, `--tertiary-foreground` | `bg-tertiary`, `text-tertiary`, `bg-tertiary-container`, … | the tertiary colour (strix violet) |
54
59
  | `--muted`, `--muted-foreground` | `bg-muted`, `text-muted-foreground` | quiet surfaces, hints, placeholders |
55
60
  | `--track`, `--track-fill` | `bg-track`, `bg-track-fill`, `border-track-fill` | the empty part of a slider or meter, the drawer handle; the slider range and thumb |
56
61
  | `--accent`, `--accent-foreground` | `bg-accent`, … | hover surfaces |
57
62
  | `--error`, `--success`, `--info`, `--warning` (+ `-foreground`) | `bg-error`, `text-success`, … | the status colours of `Button`, `Alert`, `Badge`, toasts |
63
+ | `--primary-text` | `text-primary-text` | primary drawn as text or an icon on a page, card or popover surface: `Typography tone="primary"`, links, the `Alert` primary icon, selected labels. Equal to `--primary` in light; in dark, `--primary` mixed 60 % with white (`color-mix(in oklab, var(--primary) 40%, white)`), well over 4.5:1 on `--background` and `--card` |
58
64
  | `--error-text` | `text-error-text` | error drawn as text: field messages, `HelperText` in its error state, form and dialog errors, `variant="error"` menu items |
59
65
  | `--destructive` | `bg-destructive`, `text-destructive` | deprecated alias of `--error`: the light theme sets it to `var(--error)`, the dark theme keeps its own value; use `--error` |
60
66
  | `--card`, `--popover`, `--tooltip` (+ `-foreground`) | `bg-card`, `bg-popover`, `bg-tooltip` | surfaces |
@@ -130,6 +136,26 @@ import { Button } from '@nxgt/material-vue/components';
130
136
  </template>
131
137
  ```
132
138
 
139
+ ## Links
140
+
141
+ `styles.css` styles every bare `<a>` as a link in running text: the info
142
+ colour, an underline on hover and focus, an info tint behind it. Links the
143
+ components render with a look of their own (`Toc`, `SeeAlso`, `Pager`,
144
+ `LinkCard`, `ListTile`, the doc shell's links, `BreadcrumbLink`, `SkipLink`,
145
+ the sidebar entries) opt out of it. Do the same for an anchor of yours that
146
+ has its own look, such as a card, a menu entry or a button-like link: add the
147
+ `nxgt-plain-link` class.
148
+
149
+ ```vue
150
+ <a href="/pricing" class="nxgt-plain-link flex items-center gap-2 rounded p-2 hover:bg-muted">
151
+ Pricing
152
+ </a>
153
+ ```
154
+
155
+ The class only removes the prose style (colour, underline, tint, pointer);
156
+ the anchor then inherits its text colour like any other element, so style
157
+ the rest yourself. Without the class the anchor keeps the prose style.
158
+
133
159
  ## Contrast and reduced motion
134
160
 
135
161
  **Contrast.** The shipped light palette keeps the text of every filled status
@@ -139,7 +165,10 @@ colour at 4.5:1 or more (WCAG 1.4.3): `--secondary` 5.7:1, `--success` 5.6:1,
139
165
  `--background`, 4.8:1 on its own 15 % `tonal` tint.
140
166
 
141
167
  The dark theme keeps brighter values, read as text on the dark surfaces: the
142
- dark `--secondary` (`oklch(0.66 0.1135 245.65)`) is 6.5:1 on `--background`
168
+ dark `--tertiary` (`oklch(0.7 0.15 310.75)`) is 7.0:1 on `--background` and
169
+ 6.3:1 on `--card`, with a dark `--tertiary-foreground` that keeps 7.0:1 on
170
+ a filled tertiary surface (light: 7.6:1 under near-white). The dark
171
+ `--secondary` (`oklch(0.66 0.1135 245.65)`) is 6.5:1 on `--background`
143
172
  and 5.8:1 on `--card` (a `tonal` secondary `Button` 5.4:1 and 4.6:1). Under
144
173
  near-white text those brights are too light, so every filled surface built on
145
174
  them is tinted in the dark theme: a filled `secondary` `Button`, `IconButton`
@@ -162,12 +191,12 @@ their text and icon in one tone per colour, read on `--background`, on
162
191
  the light theme `primary`, `secondary`, `info` and `success` use the token
163
192
  itself (5.8, 4.8, 4.9 and 4.7:1 at their lowest) and `warning` and `error` the
164
193
  token mixed with 15 % black (6.4 and 5.4:1). In the dark theme `primary` is
165
- mixed with 60 % white (8.1:1 on `--card`: the token itself is a dark indigo,
194
+ mixed with 60 % white, the `--primary-text` token (8.1:1 on `--card`: the token itself is a dark indigo,
166
195
  2.2:1), `success` and `warning` with 15 % white (5.0 and 6.3:1), `error` with
167
196
  30 % white (5.9:1), and `secondary` and `info` use the token (4.6 and 5.3:1).
168
197
  The tones are mixed from the tokens (`color-mix(in oklab, var(--color-error)
169
198
  85%, black)`), so an override of a token moves its tone with it; a text colour
170
- of your own on one of those buttons (`class="text-primary"`) replaces the tone
199
+ of your own on one of those buttons (`class="text-primary-text"`) replaces the tone
171
200
  and is yours to check.
172
201
 
173
202
  A `Badge` used as a link (`as-child` around an `<a>`) darkens on hover like a filled
@@ -230,12 +259,31 @@ and `.dark, [data-theme="dark"]` moves it, but setting `--error-text` or
230
259
  `--color-error-text` (and `--color-error` for fills) on that element, or put
231
260
  the override on the root.
232
261
 
262
+ **Primary text has a token of its own**, `--primary-text` (`text-primary-text`),
263
+ for the same reason: the brand colour is a dark fill (it keeps near-white text
264
+ readable on a button) and reads at only about 2:1 as text on the dark surfaces.
265
+ The light value is `var(--primary)`; the dark one is
266
+ `color-mix(in oklab, var(--primary) 40%, white)` (primary mixed 60 % with
267
+ white, the recipe of the tonal `Button` labels). `Typography tone="primary"`,
268
+ the `Alert` primary icon, links, selected labels and the other primary text of
269
+ the catalogue use it.
270
+
271
+ - **`text-primary-text`** for primary written as text, or an icon beside that
272
+ text, on a page, card or popover surface.
273
+ - **`text-primary`** (with `bg-primary`, `border-primary`, `ring-primary`) for
274
+ fills, borders and rings. Text on a primary fill is `text-primary-foreground`.
275
+
276
+ Like `--error-text`, it is computed where it is declared, on the root: an
277
+ override of `--primary` under `:root` and `.dark, [data-theme="dark"]` moves it
278
+ (override `--primary-text` itself after it to pick another dark colour); below
279
+ the root, set `--color-primary-text` on the element.
280
+
233
281
  Storybook's `Guides/Theming/Contrast` measures all of this in both themes:
234
282
  the surfaces' text, the error text (`HelperText` and a field's message on
235
283
  `--background` and `--card`), the `Alert`s and the `Timeline`, `Button` and
236
284
  `IconButton` in every variant and colour, and a `Badge` link in every colour,
237
285
  at rest, hovered, focused and pressed, the tracks and the tabs;
238
- `Data display/ListTile` measures the selected tile, `Data table, filter & lists/DataTable/On a card` the header, the cells, the links and the selected rows on a page and on a `Card` (also right-to-left), and `Feedback & status/Alert/Tones contrast` the warning, error and success alerts there. Run them against your own
286
+ `Data display/ListTile` measures the selected tile, `Data table, filter & lists/DataTable/On a card` the header, the cells, the links and the selected rows on a page and on a `Card` (also right-to-left), and `Feedback & status/Alert/Tones contrast` every alert tone there, and `Primitives/Typography/Tones contrast` the text tones (primary included). Run them against your own
239
287
  tokens after an override.
240
288
 
241
289
  **Tabs.** An `underline` tab's active label is `--primary` in the light
@@ -0,0 +1,194 @@
1
+ # The Vite plugin: `nxgtMaterial()`
2
+
3
+ One plugin for the build-time work `@nxgt/material-vue` asks of a Vite app: a strict
4
+ Content Security Policy that needs no hand-written nonce, fonts that survive
5
+ `font-src 'self'`, and a build that fails, with the exact fix, when the
6
+ stylesheet is not wired.
7
+
8
+ ```ts
9
+ // vite.config.ts
10
+ import { nxgtMaterial } from '@nxgt/material-vue/vite';
11
+ import vue from '@vitejs/plugin-vue';
12
+ import tailwindcss from '@tailwindcss/vite';
13
+ import { defineConfig } from 'vite';
14
+
15
+ export default defineConfig({
16
+ plugins: [vue(), tailwindcss(), ...nxgtMaterial()],
17
+ });
18
+ ```
19
+
20
+ `nxgtMaterial()` returns an array of plugins, hence the spread. Put it last in `plugins` and add no HTML minifier or rewriting plugin after it: a hash covers exact text, and anything that changes the inline scripts later invalidates it.
21
+
22
+ Every part is on by default and has an opt-out. It needs `vite` 5.2 or later (6, 7, 8; on Vite 5, as under Nuxt 3, every check runs as for the client).
23
+
24
+ | Option | Default | What it does |
25
+ | --- | --- | --- |
26
+ | `csp` | on, `mode: 'hash'` | Puts zod's jitless script in `index.html`, then writes the hash of every inline script and style of the built HTML to `nxgt-csp.json`; the header uses the script hashes. `csp: false` turns it all off |
27
+ | `csp.mode` | `'hash'` | `'hash'`: no placeholder ever lands in the HTML. `'nonce'`: the HTML carries a placeholder your server replaces on every request |
28
+ | `csp.placeholder` | `CSP_NONCE_PLACEHOLDER` | Nonce mode only: your own placeholder (or set Vite's `html.cspNonce`, which the plugin then reuses) |
29
+ | `csp.zodJitless` | `true` | The inline script that switches zod's `new Function` probe off |
30
+ | `csp.dev` | `true` | A `Content-Security-Policy-Report-Only` header on the dev server |
31
+ | `csp.policy` | `{}` | `cspPolicy` options (`directives`, `hashes`) added to the dev and generated headers |
32
+ | `fonts.katex` | `true` | Never inline a font as a `data:` URI (`cspAssetsInlineLimit`), composed with your own `build.assetsInlineLimit` |
33
+ | `fonts.sans` | `'inter'` | Loads self-hosted Inter when `@fontsource-variable/inter` is installed (otherwise warns and keeps the system stack), in your root stylesheet, and sets `--font-sans`; `false` to keep the system stack and silence the warning. `fonts: false` turns both off |
34
+ | `styles.check` | `true` | Fails `vite build` when the library's stylesheet is never imported (`@nxgt/material-vue/styles.css`, or the compiled one through `@import "@nxgt/material-vue";`) |
35
+ | `pdf` | on | pdfkit's standard fonts (`pdfkitStandardFonts()`): a no-op unless pdfkit goes through the bundle. `pdf: false` turns it off |
36
+ | `pdf.bundle` | `false` | Also adds the renderer to `ssr.noExternal`, for a server whose runtime image has no `node_modules` (see [Rendering PDFs on a bundled server](#rendering-pdfs-on-a-bundled-server)) |
37
+
38
+ ## The two CSP modes
39
+
40
+ ### Hash mode (default): safe on a static host
41
+
42
+ The build computes the sha256 of every inline `<script>` and `<style>` of the
43
+ built `index.html` (zod's jitless script, and anything of yours) and writes:
44
+
45
+ - `dist/.vite/nxgt-csp.json`: `{ mode, scriptHashes, styleHashes, header }`;
46
+ - a line in the build log with the ready-to-paste header.
47
+
48
+ ```
49
+ Content-Security-Policy: default-src 'self'; script-src 'self' 'sha256-…'; style-src-elem 'self' 'unsafe-inline'; …
50
+ ```
51
+
52
+ Scripts are allowed by hash only. `<style>` elements are allowed by
53
+ `'unsafe-inline'`: the components create some at runtime (`ScrollArea` and
54
+ `Select`, the modals' scroll lock, the rich text editor), which no build can
55
+ hash, and a browser ignores `'unsafe-inline'` as soon as a hash or a nonce is
56
+ in the same directive. So the header carries no style hash; `styleHashes` in
57
+ the JSON is there for a stricter policy you write yourself (it would block
58
+ those runtime `<style>` elements). An inline style runs no code; for a
59
+ `style-src-elem` without `'unsafe-inline'`, use nonce mode.
60
+
61
+ Send that header from your host (a `_headers` file, a CDN rule, the web
62
+ server). The HTML holds no placeholder and no nonce, so nothing public can be
63
+ mistaken for a secret. Rebuild, and the hashes follow the HTML. A hash covers
64
+ exact text: an inline script a CDN or a tag manager rewrites no longer matches.
65
+
66
+ ### Nonce mode: for an app with a server
67
+
68
+ ```ts
69
+ nxgtMaterial({ csp: { mode: 'nonce' } })
70
+ ```
71
+
72
+ Vite's `html.cspNonce` is set to `NXGT_CSP_NONCE`, so every script, link and
73
+ `meta` of `index.html` carries `nonce="NXGT_CSP_NONCE"`. **That value is
74
+ public: never serve it as it is.** On every request, replace it:
75
+
76
+ ```ts
77
+ import { applyCspNonce, cspPolicy, createCspNonce } from '@nxgt/material-vue/lib';
78
+
79
+ const nonce = createCspNonce();
80
+ const html = applyCspNonce(builtHtml, nonce);
81
+ res.setHeader('Content-Security-Policy', cspPolicy({ nonce }).header);
82
+ ```
83
+
84
+ `applyCspNonce` and `cspPolicy({ nonce })` throw when given the placeholder as
85
+ the nonce. `nxgt-csp.json` still lists the header, with the placeholder in
86
+ it, as a template to read, not to serve.
87
+
88
+ Pick nonce mode when a server renders or serves your HTML and can run
89
+ `applyCspNonce`; pick hash mode for a static host. Nonce mode is also the
90
+ strict option for styles: `style-src-elem 'self' 'nonce-…'`, no
91
+ `'unsafe-inline'`.
92
+
93
+ ## The dev server
94
+
95
+ `csp.dev` sends `Content-Security-Policy-Report-Only` (hash mode: no nonce,
96
+ and the hash of zod's jitless script when `csp.zodJitless` is on; nonce mode:
97
+ the placeholder, as the dev HTML carries it) with `ws:` for HMR.
98
+ Report-Only never blocks, so Vite's own inline scripts are reported and
99
+ that is expected. Vite's dev `<style>` elements (your CSS, injected in dev
100
+ only: `vite build` emits stylesheet files) are allowed by `'unsafe-inline'` in
101
+ hash mode and carry the placeholder in nonce mode.
102
+
103
+ ## Fonts
104
+
105
+ - **`fonts.katex`**: Vite inlines an asset under 4096 bytes as a `data:` URI,
106
+ and `font-src 'self'` blocks it (KaTeX's `KaTeX_Size3-Regular.woff2` is
107
+ 3.6 KB). The plugin sets `build.assetsInlineLimit` so a font is always a
108
+ file. Your own function or number still applies to every other asset.
109
+ - **`fonts.sans: 'inter'`**: in the stylesheet that imports
110
+ `@nxgt/material-vue/styles.css`, the plugin imports `@fontsource-variable/inter` first
111
+ and appends an unlayered `:root { --font-sans: "Inter Variable", … }` that
112
+ beats Tailwind's layered theme variable. Nothing is added when that
113
+ stylesheet (or a file it imports) already imports Inter or sets
114
+ `--font-sans` itself. A JavaScript `import '@fontsource-variable/inter'` is
115
+ not detected: pair it with `fonts: { sans: false }`. The package is an optional peer
116
+ and Inter loads only when your app has it installed:
117
+ `bun add @fontsource-variable/inter`. Without it the plugin warns once, in
118
+ dev and in `vite build`, keeps the system font stack and never fails the
119
+ build; `fonts: { sans: false }` turns Inter off and silences the warning.
120
+
121
+ ## The styles check
122
+
123
+ `vite build` (client only; not under Vitest, not for `build.ssr`) fails when
124
+ the library's stylesheet is imported by no stylesheet and no module. Either
125
+ route passes: `@nxgt/material-vue/styles.css` (with Tailwind; it already
126
+ scans the package, so Vue needs no `@source`), or the compiled stylesheet of
127
+ an app without Tailwind, `@import "@nxgt/material-vue";` (the package's
128
+ `style` condition, see [theming](theming.md#without-tailwind)). The error message names the
129
+ exact `@import` line to add.
130
+
131
+ Opt out with `styles: false` (a Storybook build, for instance, may not import
132
+ it).
133
+
134
+ ## Rendering PDFs on a bundled server
135
+
136
+ `pdf` is on by default and costs nothing for an app that never renders a PDF:
137
+ the transform only touches modules whose id contains `pdfkit`. Add
138
+ `pdf: { bundle: true }` to a Vite SSR server whose runtime image ships the
139
+ bundle alone:
140
+
141
+ ```ts
142
+ // vite.config.ts (an SSR server)
143
+ export default defineConfig({
144
+ plugins: [...nxgtMaterial({ pdf: { bundle: true } })],
145
+ });
146
+ ```
147
+
148
+ `bundle: true` puts `@nxgt/material-vue`, `@nxgt/material-core`, `pdfkit`, `@react-pdf/*`
149
+ and every dependency the renderer, pdfkit and core need at runtime (fontkit, yoga, png-js… read
150
+ from your installed packages, so the list follows the renderer's releases) into
151
+ `ssr.noExternal`, merged with your own array; `ssr.noExternal: true` is left as
152
+ it is. What it does not touch is yours to bundle the way you bundle the rest of
153
+ your server: `react` (the renderer's peer) and your app's other dependencies,
154
+ for example `ssr: { noExternal: ['react'] }`. The library's own
155
+ dependencies (a router, charts) are not walked, so a second copy never splits
156
+ their contexts. Under Yarn PnP there is no `node_modules` to read: only the
157
+ fixed names apply, and you list the renderer's dependencies yourself. Without `bundle`, the renderer
158
+ stays external and loads pdfkit's fonts from `node_modules`, as designed. See
159
+ [PDF documents](pdf.md#fonts-on-a-fully-inlined-server).
160
+
161
+ ## Nuxt
162
+
163
+ The Nuxt module (`@nxgt/material-vue/nuxt`) calls `nxgtMaterial()` for you and
164
+ maps its own options onto it. Under Nuxt:
165
+
166
+ | Part | What the module does |
167
+ | --- | --- |
168
+ | `csp` | **Off.** Nuxt has no `index.html` for the plugin to hash or stamp, and a header on Vite's dev server never reaches the pages Nitro renders. The module sets no policy and sends no header: use `nuxt-security` for the CSP (its `nonce: true`, or its script hashes for prerendered pages) |
169
+ | `csp.zodJitless` | Opt-in (`nxgtMaterial: { csp: { zodJitless: true } }`): the script goes into `app.head.script` with `tagPriority: 'critical'`. It is inline: `nuxt-security` with `nonce: true` stamps it, otherwise a strict `script-src` blocks it |
170
+ | `fonts.katex` | On: a font is never inlined as a `data:` URI |
171
+ | `fonts.sans: 'inter'` | Opt-in: `@fontsource-variable/inter` and the `--font-sans` override are put first in `nuxt.options.css`. A missing package warns once and keeps the system font; the build never fails |
172
+ | `styles` | **Off** by default: Nuxt resolves `css` entries before Vite sees them. `styles: true` opts in |
173
+ | `pdf` | Nitro owns it, not Vite: `pdf.bundle` adds `pdfkitStandardFonts()` to the Nitro Rollup config (`pdf.bundleStandardFonts` is its deprecated name), and the module always traces pdfkit's font files into `.output/server/node_modules` |
174
+
175
+ Why hash mode is not used under Nuxt: a Nuxt app is server-rendered, so its
176
+ HTML is built per request, not once at build time. A policy that lists hashes
177
+ of a build-time `index.html` would describe a file that does not exist.
178
+
179
+ ## Other frameworks without an `index.html`
180
+
181
+ Other SSR frameworks have no `index.html` to transform either: the CSP part
182
+ finds nothing to hash, the build warns once (`no index.html in this build: the
183
+ CSP part did nothing`) and the dev header only fits Vite's own server. On a
184
+ server, send `cspPolicy({ nonce }).header` and `zodJitlessTag(nonce)` per
185
+ request, or pass `csp: false`. The fonts part still works there, since it
186
+ rewrites the stylesheet rather than the HTML.
187
+
188
+ ## Without the plugin
189
+
190
+ Everything above has a manual route: [CSP guide](csp.md) (KaTeX, Inter, zod,
191
+ SSR).
192
+
193
+ Migrating an app that wrote its own CSP header, nonce, zod setting or PDF bundling:
194
+ [Migrating to the Vite plugin](migrate-to-vite-plugin.md).
package/docs/roadmap.md CHANGED
@@ -6,41 +6,15 @@ version they shipped in, and planned entries may name the major they target.
6
6
 
7
7
  ## Now
8
8
 
9
- - **Empty and error states that look designed** — `EmptyState` and a new
10
- `ErrorState` in three sizes (`default`, `compact`, `inline`), with in-house
11
- inline SVG illustrations or a slot for your own. `ErrorState` is a
12
- `role="alert"` with a Retry action and collapsible technical details. Every
13
- built-in empty or error state uses them — the `DataTable` default (your
14
- `#empty` slot and `empty` prop still replace it), `PdfReader`, `CommandEmpty`,
15
- the gallery, the kanban board, chat and the partials. Ships in
16
- `@nxgt/material-vue` 1.0, with the rest of the redesign.
9
+ Nothing is in progress right now.
17
10
 
18
11
  ## Next
19
12
 
20
- - **One Vite plugin for the whole setup** — `nxgtMaterial()` from
21
- `@nxgt/material-vue/vite` is the one call that wires the library
22
- into a Vite app; it is implemented once in `@nxgt/material-core/vite`, and
23
- `@nxgt/material/vite` is the same plugin.
24
- Every option is on by default and each can be turned off or refined; the
25
- helpers that exist today stay exported.
26
- - `csp`: a nonce placeholder through `html.cspNonce` and
27
- `applyCspNonce(html, nonce)` to fill it; zod's jitless mode set by an
28
- inline nonced script in `index.html` (`ZOD_JITLESS_SCRIPT` for a server
29
- that renders the page itself); and the recommended policy sent by the dev
30
- server as Report-Only.
31
- - `fonts`: the KaTeX fonts emitted as files, and `sans: 'inter'` for a
32
- self-hosted Inter.
33
- - `styles`: a build that fails with the exact fix when the stylesheet import
34
- or the `@source` line is missing.
35
- - `pdf`: pdfkit's standard fonts handled automatically when
36
- `@react-pdf/renderer` is installed, and `bundle: true` (the Nuxt module's
37
- `pdf.bundleStandardFonts` today) for a server that has no
38
- `node_modules`.
39
- - Nuxt: the Nuxt module takes the same options.
40
- - On the server, `@nxgt/material-core` gains `createCspNonce()` (Web Crypto)
41
- and `cspPolicy({ ... })`, which returns `{ directives, header }` as the
42
- [Content Security Policy guide](guides/csp.md) describes; `NonceProvider`'s
43
- `nonce` becomes optional on the client. Ships as minors.
13
+ - **1.0: `EmptyState` is only about absence** — `variant` is removed, the
14
+ `generic`, `offline` and `maintenance` kinds leave `EmptyState` for
15
+ `ErrorState`, and `kind` defaults to `empty` (it is `generic` until then).
16
+ The upgrade is in
17
+ [Migrating from `variant`](components/feedback-and-status.md#migrating-from-variant).
44
18
  - **One media player, video and audio** — `MediaPlayer`, `MediaPlayerMini`,
45
19
  `useMediaPlayer` and `AudioPlayer`, built on the native `<video>` / `<audio>`.
46
20
  `StxPlayer` and `VideoPlayer` are removed in `@nxgt/material-vue` 1.0, with a
@@ -94,6 +68,15 @@ version they shipped in, and planned entries may name the major they target.
94
68
 
95
69
  ## Later
96
70
 
71
+ - **`PdfReader` can retry a failed load** — its empty and error blocks become
72
+ `EmptyState` and `ErrorState` (compact) with a Retry button and the error's
73
+ details, as in the React reader. Only on the owner's request:
74
+ softistx/nxgt-material#702.
75
+ - **Vue /backgrounds: more presets and motifs** — the presets and motifs added
76
+ to React after the base (more gradients, geometric patterns, motif scenes)
77
+ reach `@nxgt/material-vue/backgrounds` on the owner's explicit ask. Until
78
+ then a preset id the Vue build lacks draws the gradient (and warns in
79
+ development). Tracked in softistx/nxgt-material#706.
97
80
  - **A CSP without `style-src-attr 'unsafe-inline'`** — the four form controls'
98
81
  hidden inputs no longer need it; the checked `Checkbox` indicator,
99
82
  `SelectValue`, the `RadioGroup` root, `Slider` and popper positioning still
@@ -167,6 +150,60 @@ version they shipped in, and planned entries may name the major they target.
167
150
 
168
151
  ## Shipped
169
152
 
153
+ - **Empty and error states that look designed** — `EmptyState` with its
154
+ `kind` (`empty`, `search`, `not-found`, `unauthorized`) and a new
155
+ `ErrorState` (`generic`, `offline`, `maintenance`), each in two sizes
156
+ (`default`, `compact`), with in-house inline SVG illustrations or your own in
157
+ the `#illustration` slot. `ErrorState` is a `role="alert"` with a Retry
158
+ action and collapsible technical details. The `DataTable` default (your
159
+ `#empty` slot still replaces it, and `#empty-filtered` covers a filter that
160
+ hides every row), `CommandEmpty` and the built-in empty states that were
161
+ hand-rolled (notifications, gallery, quiz, sortable and transfer lists,
162
+ kanban, chat, charts, contacts, postal address, attributes, opening hours,
163
+ the player and the event calendar) use them. `variant` still works and is
164
+ deprecated. 0.43.0.
165
+ ([EmptyState](components/feedback-and-status.md#emptystate),
166
+ [ErrorState](components/feedback-and-status.md#errorstate))
167
+ - **Background presets, the base** — `Background` from `@nxgt/material-vue/backgrounds`,
168
+ a superset of the one in `/components`: `preset` (`gradient` or `grain`),
169
+ `tone`, `intensity`, `animated` (drifts, and pauses while the page is hidden,
170
+ the background is off screen or the user prefers reduced motion), `grain`,
171
+ `seed` and `fill`. The SVG is built from plain data, so the server and the
172
+ client render the same markup and a strict CSP accepts it. The stylesheets
173
+ (`backgrounds.css`, `backgrounds/<preset>.css`) are opt-in. The grain's look
174
+ may still change.
175
+
176
+ - **One Vite plugin for the whole setup** — `nxgtMaterial()` from
177
+ `@nxgt/material/vite` and `@nxgt/material-vue/vite` (one implementation in
178
+ `@nxgt/material-core/vite`) wires the library into a Vite app, each part on
179
+ by default and each switchable off; the helpers that existed before stay
180
+ exported ([Vite plugin guide](guides/vite-plugin.md), and the migration guide moves a hand-written setup over). You get:
181
+ - `csp`: `mode: 'hash'` (default) writes the sha256 of every inline script
182
+ and style to `dist/.vite/nxgt-csp.json` with the header, or `mode: 'nonce'`
183
+ leaves a placeholder for `applyCspNonce`; zod's jitless script; a
184
+ Report-Only header on the dev server. `createCspNonce()`, `cspPolicy()`
185
+ and `cspHash()` ship in `@nxgt/material-core/csp`.
186
+ - `fonts`: `katex` (fonts stay files) and `sans: 'inter'` (self-hosted Inter).
187
+ - `styles`: a build that fails with the exact fix when the stylesheet import
188
+ or React's `@source` line is missing.
189
+ - `pdf`: pdfkit's standard fonts handled by the same plugin, with
190
+ `bundle: true` for an SSR server with no `node_modules`. The Nuxt module
191
+ calls `nxgtMaterial()` with the same `fonts`, `styles` and `pdf` options
192
+ (`pdf.bundleStandardFonts` is deprecated for `pdf.bundle`) and adds zod's
193
+ jitless script to the head on request; its CSP stays `nuxt-security`'s.
194
+ - `NonceProvider`'s `nonce` is optional on the client (it reads the page's
195
+ nonce from its first render).
196
+ Shipped in 0.43.0.
197
+
198
+ - **The app's brand, a header title and an account menu** — the app shell
199
+ takes the app's `logo` and `name`: the logo alone in the collapsed rail,
200
+ named by the app's name and shown as its tooltip, the logo and the name side
201
+ by side expanded and in the phone's menu sheet, which now shows `brand` too;
202
+ `#header-title`, the context the app is in, right after the phone's menu
203
+ button and truncated when long; and `AccountMenu`, the avatar button named
204
+ "Account menu, <name>" that opens the account's name and e-mail, the app's
205
+ links and a busy-aware Sign out (`sign-out`).
206
+
170
207
  - **Rows a user adds, removes and reorders** — `RepeatableGroup` and
171
208
  `RepeatableGroupFormField` over the form library's field array: rows from
172
209
  the `row` slot, a list for assistive tech, Remove and move buttons named after