@nxgt/material 2.10.0 → 2.11.1

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 (61) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/dist/{blockDiagram-BEXU5L5S-B8_AJyBb.js → blockDiagram-BEXU5L5S-CDfO7C_5.js} +1 -1
  3. package/dist/{chunk-3FUC2YCW-AfTluaJD.js → chunk-3FUC2YCW-DX6GTgi6.js} +1 -1
  4. package/dist/{chunk-7M6MHVWA-B2FTOUoo.js → chunk-7M6MHVWA-DTELAJpl.js} +1 -1
  5. package/dist/{chunk-GNY47TPC-CscSFMWi.js → chunk-GNY47TPC-DHf7k1Oo.js} +4 -4
  6. package/dist/{chunk-NETBCI7D-iu8QN4Le.js → chunk-NETBCI7D-CxCnFTsw.js} +1 -1
  7. package/dist/{chunk-Z7XXMR3K-BICnZMWs.js → chunk-Z7XXMR3K-B7iHRZcb.js} +1 -1
  8. package/dist/{classDiagram-v2-NBCMYWYE-Cg2qBPTZ.js → classDiagram-v2-NBCMYWYE-CSaKhTA1.js} +2 -2
  9. package/dist/components/theme-tokens.css +2 -2
  10. package/dist/components/ui/primitives/label/label.css +14 -21
  11. package/dist/{components-BfXTx1uS.js → components-Z9FC6CY-.js} +23353 -23335
  12. package/dist/components.js +4 -4
  13. package/dist/{dagre-6A5THRUB-B9m4hqWN.js → dagre-6A5THRUB-Bk4P7elf.js} +3 -3
  14. package/dist/{diagram-22UHCM2B-ChYYeBFB.js → diagram-22UHCM2B-CcBy66NF.js} +2 -2
  15. package/dist/{diagram-3UASUU5V-BoEmx9Kd.js → diagram-3UASUU5V-DauW2GeC.js} +1 -1
  16. package/dist/dnd.js +1 -1
  17. package/dist/{elk-276RUBZZ-CxNgT7Wb.js → elk-276RUBZZ-DB8zFfHZ.js} +3 -3
  18. package/dist/{erDiagram-OPXOYQCR-DiqLuL4t.js → erDiagram-OPXOYQCR-DO6SpxA4.js} +2 -2
  19. package/dist/{field-blur--x_n3oMy.js → field-blur-DgfIJceK.js} +914 -895
  20. package/dist/{flowDiagram-KWPJA3E3-CcUm8XN5.js → flowDiagram-KWPJA3E3-DewDNLd0.js} +3 -3
  21. package/dist/{ganttDiagram-FUAMR5RP-fN1MVB5K.js → ganttDiagram-FUAMR5RP-B845QbUZ.js} +1 -1
  22. package/dist/{icon-button-G9zdQ3Ix.js → icon-button-B1nN3J6B.js} +5 -5
  23. package/dist/{journeyDiagram-ZHPQQLJL-Cj7Vcat2.js → journeyDiagram-ZHPQQLJL-DVX8uCK8.js} +1 -1
  24. package/dist/lib/components/ui/collections/data-table/partials/data-table-empty-row.d.ts +3 -1
  25. package/dist/lib/components/ui/collections/data-table/partials/data-table-toolbar.d.ts +10 -6
  26. package/dist/lib/components/ui/collections/data-table/select-checkbox.d.ts +5 -2
  27. package/dist/lib/components/ui/dates/calendar/calendar-classes.d.ts +45 -0
  28. package/dist/lib/components/ui/dates/calendar/calendar-weekday.d.ts +4 -0
  29. package/dist/lib/components/ui/feedback/banner/banner.d.ts +9 -4
  30. package/dist/lib/components/ui/feedback/toast/toast-classes.d.ts +13 -0
  31. package/dist/lib/components/ui/layout/app-shell/use-navigation-sheet.d.ts +3 -2
  32. package/dist/main.js +4 -4
  33. package/dist/map.js +2 -2
  34. package/dist/material.css +1 -1
  35. package/dist/{mermaid.core-Be3u4qna.js → mermaid.core-0q3uq37S.js} +21 -21
  36. package/dist/{mindmap-definition-NLK3R4M7-Dw8Z4Lyy.js → mindmap-definition-NLK3R4M7-BU79I9UY.js} +2 -2
  37. package/dist/{pieDiagram-5QR66LMP-Cv_V3nnp.js → pieDiagram-5QR66LMP-DGVDzpXW.js} +1 -1
  38. package/dist/{quadrantDiagram-O4NWA36T-Bavlu0dy.js → quadrantDiagram-O4NWA36T-CAgci1ex.js} +1 -1
  39. package/dist/{requirementDiagram-PLB6GJNP-6fInAtPO.js → requirementDiagram-PLB6GJNP-CDOjIVlE.js} +2 -2
  40. package/dist/{sankeyDiagram-IPEJSGJF-wzaUoPP7.js → sankeyDiagram-IPEJSGJF-DCbUg-Vu.js} +1 -1
  41. package/dist/{sortable-provider-WZT5I34N.js → sortable-provider-D4S-mqLP.js} +1 -1
  42. package/dist/{stateDiagram-v2-GCMORJYK-BRIjnDU3.js → stateDiagram-v2-GCMORJYK-C3WWpb0o.js} +2 -2
  43. package/dist/{swimlanes-2SLR337P-BNephno6.js → swimlanes-2SLR337P-CtayEq5L.js} +3 -3
  44. package/dist/{swimlanesDiagram-TC7HE7FX-CUARDUGc.js → swimlanesDiagram-TC7HE7FX-yV5ARDns.js} +3 -3
  45. package/dist/{timeline-definition-EJHVYXUP-By2gWxfd.js → timeline-definition-EJHVYXUP-BKuUPBMr.js} +1 -1
  46. package/dist/{usecaseDiagram-POWQR4AR-CleiKWa8.js → usecaseDiagram-POWQR4AR-ClgRhO0q.js} +2 -2
  47. package/dist/{xychartDiagram-PMCCYNJV-gwrRH4uJ.js → xychartDiagram-PMCCYNJV-BVKAUhvv.js} +1 -1
  48. package/docs/components/data-table-filter-and-lists.md +16 -8
  49. package/docs/components/date-and-time.md +8 -1
  50. package/docs/components/feedback-and-status.md +17 -1
  51. package/docs/components/primitives.md +7 -6
  52. package/docs/guides/app-shell.md +2 -1
  53. package/docs/previews/date-time-calendar.webp +0 -0
  54. package/docs/previews/date-time-datefield.webp +0 -0
  55. package/docs/previews/date-time-daterangefield.webp +0 -0
  56. package/docs/previews/feedback-status-toast.webp +0 -0
  57. package/docs/roadmap.md +90 -47
  58. package/docs/troubleshooting.md +114 -8
  59. package/lib/components/theme-tokens.css +2 -2
  60. package/lib/components/ui/primitives/label/label.css +14 -21
  61. package/package.json +2 -2
package/docs/roadmap.md CHANGED
@@ -1,15 +1,65 @@
1
1
  # Roadmap
2
2
 
3
3
  What is being worked on, what is likely to follow, and what has been decided
4
- against. This is a direction, not a commitment: the only number on this page is
5
- the version something shipped in.
4
+ against. This is a direction, not a commitment: shipped entries carry the
5
+ version they shipped in, and planned entries may name the major they target.
6
6
 
7
7
  ## Now
8
8
 
9
- Nothing in progress right now; see Next.
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` prop still replaces it), `PdfReader`, `CommandEmpty`, the gallery, the
15
+ kanban board and chat. Ships in `@nxgt/material` 3.0, with the rest of the
16
+ redesign.
10
17
 
11
18
  ## Next
12
19
 
20
+ - **One media player, video and audio** — `MediaPlayer`, `MediaPlayerMini`,
21
+ `useMediaPlayer` and `AudioPlayer`, built on the native `<video>` / `<audio>`.
22
+ `StxPlayer` and `VideoPlayer` are removed in `@nxgt/material` 3.0, with a
23
+ migration guide. It plays streams — native HLS, plus `hls.js` with a quality
24
+ menu when you install it (an optional peer, loaded only when a stream needs
25
+ it) — shows chapters and a transcript that follows the playback, a waveform
26
+ for audio, thumbnails on hover (VTT sprites), resumes where the viewer left
27
+ off, and adds gestures, shuffle, a shortcut-help panel and a screen that stays
28
+ awake. It runs embedded at an aspect ratio, fullscreen, floating (a corner
29
+ mini player, and an audio bar that persists across pages) or in a modal
30
+ lightbox. Built for WCAG 2.2 AA and selected AAA criteria: controls at 7:1,
31
+ captions plus a transcript, a full keyboard, screen-reader announcements,
32
+ `aria-valuetext` on sliders, reduced motion and forced colors.
33
+ - **PDFs generated without React** — a framework-free engine on pdfkit replaces
34
+ `@react-pdf/renderer`, so a document renders the same in React, Vue, Node, Bun
35
+ and the browser. One data API, `definePdfDocument`, everywhere; the `Print*`
36
+ JSX components are removed in `@nxgt/material` 3.0. Output is tagged for
37
+ accessibility (PDF/UA), archival (PDF/A-3) and electronic invoicing (Factur-X,
38
+ EN 16931). It also brings the items below.
39
+ - **Your brand on the page** — a print theme whose defaults follow the screen
40
+ theme, and your own font files registered in one call (the library bundles
41
+ no fonts).
42
+ - **QR codes, barcodes and charts in a PDF** — pass the value or the chart
43
+ options and get vector output, no data URL to prepare.
44
+ - **Rich text and Markdown in a PDF** — the rich-text editor's content and
45
+ Markdown rendered as PDF headings, paragraphs, lists, tables and links.
46
+ - **A live preview inside `PdfReader`** — `PdfReader` takes a document and
47
+ renders it itself, refreshing a moment after its data stops changing.
48
+ - **Test helpers for generated PDFs** — read a generated PDF's text, page count
49
+ and metadata in a unit test, without a browser.
50
+ - **Archive-ready PDFs** — PDF/A-3 output through one option, for invoices and
51
+ records that must be kept.
52
+ - **Electronic invoices (Factur-X)** — an invoice PDF that carries its
53
+ machine-readable data (EN 16931), built on archive-ready PDFs and the
54
+ invoice template, for e-invoicing mandates such as France's.
55
+ - **A PDF reader that annotates and fills forms** — pdf.js used directly in
56
+ both libraries (React drops `react-pdf`), with one `PdfReader` whose `mode`
57
+ is `embedded`, `dialog` or `page` and whose toolbar adapts to the width of
58
+ its container. Viewers add their own annotations, held as JSON in
59
+ `value` / `onChange` and exportable into the PDF; fill AcroForms; read two
60
+ pages side by side; scroll continuously or page by page; hear the text read
61
+ aloud; or switch to a reflowed text mode. Annotation export loads `pdf-lib`
62
+ only when used.
13
63
  - **Dark-mode previews** — each component preview also shown in the dark theme,
14
64
  so you can check contrast without running it.
15
65
  - **Previews that cannot go stale** — a CI check that fails when a story changes
@@ -18,6 +68,11 @@ Nothing in progress right now; see Next.
18
68
 
19
69
  ## Later
20
70
 
71
+ - **Next major: an error `Banner` interrupts** — `tone="error"` becomes
72
+ `role="alert"` `aria-live="assertive"`, so a screen reader reads it out at
73
+ once. Every tone is `role="status"` `aria-live="polite"` today; pass
74
+ `role="alert"` for an urgent banner until then. Breaking for a test that
75
+ finds an error `Banner` with `getByRole('status')`.
21
76
  - **Next major: `ListTile`'s `data-slot` becomes `list-tile`** — it is
22
77
  `item-detail` today, so the name matches the component like every other
23
78
  slot. Breaking for a selector or a test that looks for `item-detail`.
@@ -44,23 +99,6 @@ Nothing in progress right now; see Next.
44
99
  - **Ready-made documents** — invoice, quote, receipt, report, certificate and
45
100
  label-sheet templates that take your data and return a document, their labels
46
101
  translated.
47
- - **Your brand on the page** — a print theme whose defaults follow the screen
48
- theme, and your own font files registered in one call (the library bundles
49
- no fonts).
50
- - **QR codes, barcodes and charts in a PDF** — pass the value or the chart
51
- options and get vector output, no data URL to prepare.
52
- - **Rich text and Markdown in a PDF** — the rich-text editor's content and
53
- Markdown rendered as PDF headings, paragraphs, lists, tables and links.
54
- - **A live preview inside `PdfReader`** — `PdfReader` takes a document and
55
- renders it itself, refreshing a moment after its data stops changing. Today
56
- `usePdfFile` and a `PdfReader` given its `url` make the preview by hand.
57
- - **Test helpers for generated PDFs** — read a generated PDF's text, page count
58
- and metadata in a unit test, without a browser.
59
- - **Archive-ready PDFs** — PDF/A output through one option, for invoices and
60
- records that must be kept.
61
- - **Electronic invoices (Factur-X)** — an invoice PDF that carries its
62
- machine-readable data, built on archive-ready PDFs and the invoice template,
63
- for e-invoicing mandates such as France's.
64
102
 
65
103
  ## Not planned
66
104
 
@@ -73,13 +111,17 @@ Nothing in progress right now; see Next.
73
111
  could not upgrade. Browser-only and node-only dependencies are contained in
74
112
  this package, behind their own entry and optional peers or behind
75
113
  `clientOnly`.
76
- - **Print components back on the main entry** — `@react-pdf/renderer` reaches a
77
- dependency that crashes any SSR server that bundles it, on every request.
78
- Print stays on `@nxgt/material/print`, with its dependencies as optional
79
- peers.
80
- - **Right-to-left and complex-script PDFs** — `@react-pdf/renderer` does not
81
- shape Arabic or Hebrew text reliably. Render such a document as HTML and print
82
- it to PDF with a browser on a server, such as Playwright or Gotenberg.
114
+ - **Print components back on the main entry** — the PDF engine reaches
115
+ pdfkit, which needs Node-style dependencies a server bundle should not pull
116
+ in on every request. The PDF engine and `definePdfDocument` stay on
117
+ `@nxgt/material/print`, not the main entry, with their dependencies as
118
+ optional peers. The `Print*` JSX components go away in 3.0.
119
+ - **Right-to-left and complex-script PDFs** — pdfkit does not lay out
120
+ right-to-left text or shape Arabic and Hebrew reliably, so the new engine
121
+ does not promise it. Render such a document as HTML and print it to PDF with
122
+ a browser on a server, such as Playwright or Gotenberg.
123
+ - **Two PDF engines** — pdfkit replaces `@react-pdf/renderer`; they do not
124
+ coexist. One engine and one document model serve both libraries.
83
125
  - **Importing `@nxgt/material/map` without a DOM** — leaflet reads `window` as
84
126
  it is evaluated. An SSR app renders the map client-side, behind `clientOnly`.
85
127
  - **An app shell that builds its own menu** — `items` stays a prop and labels
@@ -159,8 +201,8 @@ Nothing in progress right now; see Next.
159
201
  - **PDFs from a Next.js route handler in one line of config** —
160
202
  `withNxgtPrint(nextConfig)` from `@nxgt/material/next` adds what `/print`
161
203
  needs to `next.config.ts` — the library in `serverExternalPackages`, and
162
- pdfkit's fonts in the `output: 'standalone'` trace wherever your package manager
163
- installed them — merged with your own config. 2.4.0.
204
+ pdfkit's fonts in the `output: 'standalone'` trace wherever your package
205
+ manager installed them — merged with your own config. 2.4.0.
164
206
  ([printing](guides/printing.md#nextjs-serverexternalpackages-and-output-standalone))
165
207
  - **One copy of the framework-free code, shared with `@nxgt/material-vue`** —
166
208
  the PDF document model and renderer, the print formatters, the PDF file
@@ -178,14 +220,15 @@ Nothing in progress right now; see Next.
178
220
  2.2.0. ([forms](guides/forms.md#touched-on-blur))
179
221
  - **Accessible names, ids and focus across the catalogue** — every field is
180
222
  named by its label and says it is required on the element a screen reader
181
- lands on (the input, the picker's `combobox`, the slider thumbs, the
182
- group), with the asterisk drawn by CSS, so in no name or label text; groups are named by their question
183
- and describe their error instead of carrying `aria-invalid` where ARIA does
184
- not allow it; ids are generated rather than taken from a field's `name` or
185
- an option's `value`, so two forms on one page no longer collide; icon
186
- buttons, crumbs, carousel arrows, codes and the camera have names; and a
187
- dialog, sheet or drawer takes focus when it opens and gives it back when it
188
- closes. 2.2.0. ([upgrading](getting-started.md#220-ids-names-and-aria-invalid-moved))
223
+ lands on (the input, the picker's `combobox`, the slider thumbs, the group),
224
+ with the asterisk drawn by CSS, so in no name or label text; groups are named
225
+ by their question and describe their error instead of carrying `aria-invalid`
226
+ where ARIA does not allow it; ids are generated rather than taken from a
227
+ field's `name` or an option's `value`, so two forms on one page no longer
228
+ collide; icon buttons, crumbs, carousel arrows, codes and the camera have
229
+ names; and a dialog, sheet or drawer takes focus when it opens and gives it
230
+ back when it closes. 2.2.0.
231
+ ([upgrading](getting-started.md#220-ids-names-and-aria-invalid-moved))
189
232
  - **PdfReader, turned pages and a steady zoom** — a page the PDF turns
190
233
  (`/Rotate`) shows the right way up, the fit zoom settles once when the
191
234
  document opens instead of moving while you read, `onPageChange` also reports
@@ -197,8 +240,8 @@ Nothing in progress right now; see Next.
197
240
  documents written as plain data (`pdfDocument` and its builders,
198
241
  `definePdfDocument` templates, `dataTable` from column definitions) alongside
199
242
  the JSX components, and `@nxgt/material-vue/pdf` (0.22.0) renders the same
200
- model, so a template renders the same pages in both libraries (experimental: the model
201
- may still change in a minor release). 2.2.0.
243
+ model, so a template renders the same pages in both libraries (experimental:
244
+ the model may still change in a minor release). 2.2.0.
202
245
  ([printing](guides/printing.md#documents-as-data))
203
246
  - **One call to a PDF file** — `downloadPdf` and `openPdf` in the browser (a
204
247
  blocked tab falls back to a download), `usePdfFile` for a preview that keeps
@@ -231,10 +274,10 @@ Nothing in progress right now; see Next.
231
274
  its zoom menu works from the keyboard and in fullscreen. 2.2.0.
232
275
  ([PdfReader](components/media-and-files.md#pdfreader))
233
276
  - **Amounts and dates in your locale** — `formatAmount` and `formatDate` take a
234
- locale and time zone, use each currency's own decimals (`{ fractionDigits: 2 }`
235
- keeps two) and keep a date-only value on its day; `useFormatters()` binds them
236
- to the `I18nProvider` language and `createFormatters()` to a locale you pass,
237
- so a document rendered on a server reads like the screen. 2.2.0.
277
+ locale and time zone, use each currency's own decimals (`{ fractionDigits: 2
278
+ }` keeps two) and keep a date-only value on its day; `useFormatters()` binds
279
+ them to the `I18nProvider` language and `createFormatters()` to a locale you
280
+ pass, so a document rendered on a server reads like the screen. 2.2.0.
238
281
  ([printing](guides/printing.md#formatamount-and-formatdate))
239
282
  - **More built-in labels follow the language** — calendar navigation,
240
283
  the data table's selection checkboxes, filter presets and
@@ -269,10 +312,10 @@ Nothing in progress right now; see Next.
269
312
  server's guess while hydrating. 2.2.0.
270
313
  - **Declaration files that always resolve** — a packed or linked build types
271
314
  `react` like the npm one, instead of as `any`. 2.2.0.
272
- - **Component documentation with previews** — every component has an entry
273
- with a picture of how it renders, an example that compiles and the options
274
- that matter, under the same twelve categories in the README and `docs/`. A forms guide covers React Hook Form, Zod, composite values and
275
- server errors. 2.2.0.
315
+ - **Component documentation with previews** — every component has an entry with
316
+ a picture of how it renders, an example that compiles and the options that
317
+ matter, under the same twelve categories in the README and `docs/`. A forms
318
+ guide covers React Hook Form, Zod, composite values and server errors. 2.2.0.
276
319
  - **`@nxgt/material/map`** — the leaflet map, markers, location field and
277
320
  `useGeolocation` as an entry of this package, with leaflet as optional peers.
278
321
  2.1.0.
@@ -24,6 +24,7 @@ words in your error.
24
24
  - [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
25
  - [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)
26
26
  - [Dialogs, toasts or buttons no longer animate](#dialogs-toasts-or-buttons-no-longer-animate)
27
+ - [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)
27
28
  - [A Button's label overflows a card on a phone](#a-buttons-label-overflows-a-card-on-a-phone)
28
29
 
29
30
  **Feedback**
@@ -33,6 +34,8 @@ words in your error.
33
34
  - [A toast never goes away after a tap](#a-toast-never-goes-away-after-a-tap)
34
35
  - [A toast's action cannot be clicked while a dialog is open](#a-toasts-action-cannot-be-clicked-while-a-dialog-is-open)
35
36
  - [`toasts.success(message, { action })` shows no action button](#toastssuccessmessage--action--shows-no-action-button)
37
+ - [An accessibility audit reports low contrast on a toast's action](#an-accessibility-audit-reports-low-contrast-on-a-toasts-action)
38
+ - [A `Banner` is not announced while a dialog is open](#a-banner-is-not-announced-while-a-dialog-is-open)
36
39
  - [The focus leaves a dialog that a toast's action opened](#the-focus-leaves-a-dialog-that-a-toasts-action-opened)
37
40
  - [A `ConfirmationDialog` reopened with new content blocks clicks](#a-confirmationdialog-reopened-with-new-content-blocks-clicks)
38
41
  - [The first Escape after closing a nested dialog does nothing](#the-first-escape-after-closing-a-nested-dialog-does-nothing)
@@ -40,6 +43,7 @@ words in your error.
40
43
  **Navigation**
41
44
 
42
45
  - [A `ListTile` link reloads the whole page](#a-listtile-link-reloads-the-whole-page)
46
+ - [After a link in the phone menu, the focus goes to `<main>`, not to the page's heading](#after-a-link-in-the-phone-menu-the-focus-goes-to-main-not-to-the-pages-heading)
43
47
  - [The phone menu needs two Escapes to close, or a tooltip shows over it](#the-phone-menu-needs-two-escapes-to-close-or-a-tooltip-shows-over-it)
44
48
  - [`Tabs` look different after upgrading](#tabs-look-different-after-upgrading)
45
49
  - [A screen reader announces an icon-only tab as "tab", with no name](#a-screen-reader-announces-an-icon-only-tab-as-tab-with-no-name)
@@ -99,6 +103,7 @@ words in your error.
99
103
  - [Zod messages ignore the language, or your `material.zod.*` strings](#zod-messages-ignore-the-language-or-your-materialzod-strings)
100
104
  - [A test asserting the old Zod string length text fails](#a-test-asserting-the-old-zod-string-length-text-fails)
101
105
  - [A calendar's weekday row is in English on a French page](#a-calendars-weekday-row-is-in-english-on-a-french-page)
106
+ - [An accessibility audit reports low contrast on a calendar's weekday row or its days](#an-accessibility-audit-reports-low-contrast-on-a-calendars-weekday-row-or-its-days)
102
107
 
103
108
  **Runtime**
104
109
 
@@ -164,6 +169,8 @@ words in your error.
164
169
  - [A `DataTable` shows its header row on a phone](#a-datatable-shows-its-header-row-on-a-phone)
165
170
  - [A test's `getByRole('region')` finds more than one region](#a-tests-getbyroleregion-finds-more-than-one-region)
166
171
  - [A `DataTable` toolbar is gone](#a-datatable-toolbar-is-gone)
172
+ - [axe reports `target-size` on a `DataTable`'s "Select all"](#axe-reports-target-size-on-a-datatables-select-all)
173
+ - [A `DataTable`'s empty state is cut off at its right edge](#a-datatables-empty-state-is-cut-off-at-its-right-edge)
167
174
 
168
175
  **PDF reader**
169
176
 
@@ -435,8 +442,8 @@ In the dark theme, a filled `secondary` `Button` gave its near-white text
435
442
  `success`, `info` or `warning` one 3.5:1, 2.5:1 and 3.4:1. The dark
436
443
  `--secondary` is brighter (`oklch(0.66 ...)`, 6.5:1 as text on
437
444
  `--background`), and the filled `secondary`, `success`, `info` and `warning`
438
- `Button`s and `IconButton`s are tinted in the dark theme
439
- (`dark:bg-secondary/70`, `dark:bg-success/60`, …) so their text keeps 4.5:1.
445
+ `Button`s and `IconButton`s are filled in the dark theme with an opaque mix
446
+ toward `--background` (about 70 %), so their text keeps 4.5:1 over any backdrop.
440
447
  A filled button given a background of your own through `className` loses the
441
448
  tint in dark mode: add a matching `dark:` one.
442
449
 
@@ -453,8 +460,8 @@ is open and leaves it focusable; a control the tab order reaches inside an
453
460
  reports it predates the inert background.
454
461
  **Fix:** upgrade: the page behind a modal layer of this library is `inert`
455
462
  as well — out of the tab order and of the accessibility tree — and the rule
456
- passes. Live regions (the `Toaster`) stay reachable. Do not exclude
457
- `[aria-hidden]` or `[data-aria-hidden]` from the audit: it hides real
463
+ passes. Live regions (the `Toaster`, every `Banner`) stay reachable. Do not
464
+ exclude `[aria-hidden]` or `[data-aria-hidden]` from the audit: it hides real
458
465
  failures. A layer of yours built on Radix directly (not on these
459
466
  components) still marks the page `aria-hidden` only; make the rest of the
460
467
  page inert with core's `inertOthers`, which keeps live regions and counts
@@ -667,6 +674,42 @@ toasts.success({ title: 'Staff member restored', action: { label: 'Send an invit
667
674
  `data` takes no `icon` (a preset draws its own) and no `cancel` (the X button
668
675
  beside the action stands for it).
669
676
 
677
+ ### An accessibility audit reports low contrast on a toast's action
678
+
679
+ **When:** axe (or Lighthouse) reports `color-contrast` on a toast's action
680
+ button (`[data-slot="toast-action"]`), the `{ label }` one: an
681
+ "Undo", a "Send an invitation", or the "Close" a preset shows in its place.
682
+ **Why:** the action is a `tonal` `Button`, whose tones are made for the page,
683
+ and the toast is the inverse of the page (`bg-foreground`: dark in the light
684
+ theme, light in the dark one). Its `primary` label on a 15 % primary tint over
685
+ the toast read 2.5:1 in the light theme and 1.5:1 in the dark one.
686
+ **Fix:** upgrade: the action takes the other theme's tone (the lightened
687
+ primary on the dark toast, `primary` on the light one) on an opaque mix of
688
+ primary toward the toast's surface, 9.4:1 and 5.5:1 at rest, and more hovered,
689
+ focused or pressed. Drop an exclusion of `[data-slot="toast-action"]` from
690
+ your contrast scan. A function action (`(dismiss) => <Button …>`) renders
691
+ your own buttons, which keep the page's tones: give them colours measured on
692
+ the toast, or use the `{ label, onClick }` form.
693
+
694
+ ### A `Banner` is not announced while a dialog is open
695
+
696
+ **When:** a `Banner` (an update notice, a session notice) changes or shows
697
+ while a `Dialog`, `Sheet` or another modal layer is open, and a screen reader
698
+ says nothing; or an `aria-live` you set on it is dropped.
699
+ **Why:** a modal layer makes the page behind it `inert` and keeps only live
700
+ regions (`[aria-live]`) reachable; a `Banner` was a `role="status"` without
701
+ `aria-live`, so it went inert with the page, and it did not forward its props.
702
+ **Fix:** upgrade: every `Banner`, `error` included, is a polite live region
703
+ (`role="status"` `aria-live="polite"` `aria-atomic="true"`), so the layer
704
+ keeps it reachable, and the props you pass (`id`, `data-*`, `aria-*`, `role`)
705
+ go to its root and win: pass `role="alert"` for an urgent banner.
706
+ Drop a `<div aria-live="polite">` wrapper kept for this. The page is made
707
+ inert when the layer opens: a `Banner` mounted later inside a part of the page
708
+ made inert stays inert until the layer closes, so render it before (and change
709
+ its text), or keep it in a live container that is already there. A kept
710
+ `Banner` stays interactive behind the layer: a screen reader's browse mode
711
+ reaches its action and dismiss button (Tab stays in the layer).
712
+
670
713
  ### The focus leaves a dialog that a toast's action opened
671
714
 
672
715
  **When:** a toast's action opens a `Dialog` (or an `AlertDialog`, a `Sheet`,
@@ -791,6 +834,33 @@ const RouterLink: AppShellLinkComponent = ({ href, ...props }) => (
791
834
  <ListTile title="Notifications" href="/settings/notifications" linkComponent={RouterLink} />
792
835
  ```
793
836
 
837
+ ### After a link in the phone menu, the focus goes to `<main>`, not to the page's heading
838
+
839
+ **When:** on a phone, a link followed in the menu sheet of `ActivityLayout` or
840
+ `AppHeader` (or a navigation while it is open) opens a screen that moves the
841
+ focus to its own element as it mounts (an error page's `h1`, a step's title),
842
+ and the focus ends on `<main>` instead.
843
+ **Why:** as the sheet closed after a navigation, it always focused `<main>`,
844
+ over what the new screen had focused.
845
+ **Fix:** update `@nxgt/material`. The sheet now leaves the focus where the screen put it
846
+ inside `<main>`, and focuses `<main>` only when the focus is elsewhere (on the
847
+ body, or still in the closing sheet). A controlled modal layer (the menu
848
+ sheet is one) releases the page from `inert` in the same commit that closes
849
+ it, so the focus your screen moves as it mounts is not refused. Code that gave the focus back
850
+ to the screen's element after the sheet closed can go.
851
+
852
+ ### A Content-Security-Policy reports a `data:` image on every form with a required field
853
+
854
+ **When:** a page with a required field (`Label` with the asterisk, or a field
855
+ of this library with `required`) reports a CSP violation for a
856
+ `data:image/svg+xml` image under a policy whose `img-src` refuses `data:`
857
+ (`img-src 'self'`), and the red asterisk is missing.
858
+ **Why:** the mark was a CSS mask whose image was a `data:` URI.
859
+ **Fix:** update `@nxgt/material`. The mark is now an `aria-hidden` SVG in the label's
860
+ markup, which loads no image. An override of `--label-required-mark` (or a copy
861
+ of the mark served by your app) can go; restyle the mark with
862
+ `[data-slot="label-required-mark"]` (see [Label](components/primitives.md#label)).
863
+
794
864
  ### The phone menu needs two Escapes to close, or a tooltip shows over it
795
865
 
796
866
  **When:** on a phone, the menu sheet of `ActivityLayout` or `AppHeader` opens
@@ -1688,6 +1758,31 @@ z.config({ customError: zodLocaleError({ storageKey: 'lang', translate }) });
1688
1758
  z.config({ customError: zodLocaleError({ language: () => requestLanguage() }) });
1689
1759
  ```
1690
1760
 
1761
+ ### An accessibility audit reports low contrast on a calendar's weekday row or its days
1762
+
1763
+ **When:** axe (or Lighthouse) reports `color-contrast` on the weekday row
1764
+ (`th.rdp-weekday`) of a `Calendar`, `DateField`, `DateRangeField` or
1765
+ `EventCalendar`'s date picker, in the light theme, on the page or in its
1766
+ popover; or on the days of the neighbouring months (`.rdp-outside`, such as
1767
+ 31 August on September's grid), the middle days of a range, or the chosen
1768
+ day.
1769
+ **Why:** the base stylesheet tints every table header (`th`,
1770
+ `bg-primary/15`) and every other row (`bg-primary/5`), and a calendar is a
1771
+ table. Before this fix, the weekday row drew `--muted-foreground` on that
1772
+ header tint (4.4:1), a range's middle days drew it on their `bg-primary/15`,
1773
+ and every day's label was drawn at 70% opacity: 2.9:1 for a neighbouring
1774
+ month's day (they can be picked, so they need 4.5:1), 2.6:1 for a range's
1775
+ middle day and 4.4:1 for the chosen day's near-white label.
1776
+ **Fix:** update. The weekday row and a range's middle days are muted toward
1777
+ the foreground (5.6:1 light, 6.2:1 dark on the popover), the neighbouring
1778
+ months' days keep `--muted-foreground` without the fade (5.1:1 light, 5.6:1
1779
+ dark at least) and the other days are drawn at full strength. Drop an
1780
+ exclusion your accessibility test kept for `.rdp-weekday` or `.rdp-outside`.
1781
+ If the audit still fails, your stylesheet overrides `--muted-foreground`,
1782
+ `--primary` or the table rules, or a `classNames` of your own sets the
1783
+ weekday or outside colour back to `text-muted-foreground`; check it at 4.5:1
1784
+ on `bg-primary/15` over `--popover`.
1785
+
1691
1786
  ### A calendar's weekday row is in English on a French page
1692
1787
 
1693
1788
  **When:** under `<I18nProvider>` set to French, a `DateField`,
@@ -2238,10 +2333,9 @@ await expect(page.getByRole('combobox', { name: 'Delivery date' })).toHaveAttrib
2238
2333
  ```
2239
2334
 
2240
2335
  Custom CSS that targeted the old `<span class="text-error">` should target
2241
- `[data-slot="label-text"][data-required]::after` instead; the recipe is in
2242
- [Label](components/primitives.md#label). The mark is drawn by the package stylesheet, so a
2243
- snapshot of the DOM no longer contains it, and it stays visible in
2244
- forced-colors mode.
2336
+ `[data-slot="label-required-mark"]` instead; the recipe is in
2337
+ [Label](components/primitives.md#label). The mark is an `aria-hidden` SVG with
2338
+ no text, and it stays visible in forced-colors mode.
2245
2339
 
2246
2340
  ### An end-to-end test no longer finds `aria-expanded` on a `ResponsiveButton`
2247
2341
 
@@ -3172,6 +3266,18 @@ page.getByRole('region', { name: 'Invoices' });
3172
3266
  **Why:** the toolbar is rendered only when it has content (yours, or `showViewOptions`): an empty `<fieldset>` is a nameless group to a screen reader.
3173
3267
  **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.
3174
3268
 
3269
+ ### axe reports `target-size` on a `DataTable`'s "Select all"
3270
+
3271
+ **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.
3272
+ **Why:** axe measures the 16px box itself, not its 24px `::before`, so it asks for a 24px circle around the box clear of every other control. The sort button reaches 12px back into its cell's padding (`-ml-3`), and the `select` cell had no padding after its checkbox: the circle met the button.
3273
+ **Fix:** upgrade: the `select` column's checkboxes now keep a 12px margin after them. Drop any axe `exclude` of `thead [data-slot="checkbox-field"]` you kept for it. A `select` column of your own (not `predefinedColumns().select`) needs the same: `me-3` on its checkboxes.
3274
+
3275
+ ### A `DataTable`'s empty state is cut off at its right edge
3276
+
3277
+ **When:** a custom `empty` as wide as its cell (a paragraph, a `w-full` box) on a phone: its last 4px sit under the edge of the table's scroll box, and look cut off.
3278
+ **Why:** the empty state was sized to the box's visible width (`--table-visible-width`) but drawn inside the table container's 4px padding, so it stuck out by that padding.
3279
+ **Fix:** upgrade: the empty state now takes the padding off its width and sticks 4px in from the box's edge. Nothing to change in your `empty`.
3280
+
3175
3281
  ---
3176
3282
 
3177
3283
  ---
@@ -70,8 +70,8 @@
70
70
  --primary: oklch(46% 0.1135 276.35);
71
71
  --primary-foreground: oklch(0.984 0.003 247.858);
72
72
  /* Brighter than the light one: it is read as text on the dark surfaces (6.5:1
73
- on --background, 5.8:1 on --card); a filled secondary surface tints it
74
- (dark:bg-secondary/70) to keep 5.2:1 under its near-white text. */
73
+ on --background, 5.8:1 on --card); a filled secondary surface mixes it toward --background
74
+ (the opaque 70 % mix) to keep 5.2:1 under its near-white text. */
75
75
  --secondary: oklch(0.66 0.1135 245.65);
76
76
  --secondary-foreground: oklch(0.984 0.003 247.858);
77
77
  --muted: oklch(0.279 0.041 260.031);
@@ -1,33 +1,26 @@
1
- /* The required mark of `Label withAsterisk`. Drawn, not typed: a "*" in the
2
- * label's text joined its name ("Email *") for `getByLabel(…, { exact })`,
3
- * `getByLabelText` and screen readers, and generated text (`content: "*"`)
4
- * joins the accessible name too. An empty `content` painted by a mask adds
5
- * nothing to the name or the text. The field's own `required` /
6
- * `aria-required` is what is announced. Restyle it from your own CSS:
7
- * `background-color` for the colour, `width` / `height` for the size. */
8
- [data-slot="label-text"][data-required]::after {
9
- content: "";
1
+ /* The required mark of `Label withAsterisk`: an `aria-hidden` inline SVG in
2
+ * the label's markup, not a "*" in its text. A typed "*" joined the label's
3
+ * name ("Email *") for `getByLabel(…, { exact })`, `getByLabelText` and
4
+ * screen readers, and so does generated text (`content: "*"`). The field's
5
+ * own `required` / `aria-required` is what is announced. Drawn by the markup,
6
+ * it loads no image: a Content-Security-Policy whose `img-src` refuses
7
+ * `data:` draws it too. Restyle it from your own CSS: `color` for the colour,
8
+ * `width` / `height` for the size. */
9
+ [data-slot="label-required-mark"] {
10
10
  display: inline-block;
11
11
  flex: none;
12
12
  align-self: flex-start;
13
13
  width: 0.5em;
14
14
  height: 0.5em;
15
15
  margin-block-start: 0.05em;
16
- background-color: var(--color-error);
17
- --label-required-mark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Cpath d='M5 1v8M1.5 3l7 4M8.5 3l-7 4' stroke='black' stroke-width='1.75' stroke-linecap='round' fill='none'/%3E%3C/svg%3E");
18
- mask-image: var(--label-required-mark);
19
- mask-size: contain;
20
- mask-repeat: no-repeat;
21
- mask-position: center;
22
- /* A browser's print stylesheet drops backgrounds; this one is the mark. */
23
- print-color-adjust: exact;
24
- -webkit-print-color-adjust: exact;
16
+ color: var(--color-error);
17
+ overflow: visible;
25
18
  }
26
19
 
27
- /* Forced colours replace background-color with the system palette's own;
20
+ /* Forced colours keep an SVG's `currentColor` stroke as authored;
28
21
  * CanvasText keeps the mark visible on any theme. */
29
22
  @media (forced-colors: active) {
30
- [data-slot="label-text"][data-required]::after {
31
- background-color: CanvasText;
23
+ [data-slot="label-required-mark"] {
24
+ color: CanvasText;
32
25
  }
33
26
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxgt/material",
3
- "version": "2.10.0",
3
+ "version": "2.11.1",
4
4
  "license": "MIT",
5
5
  "bugs": {
6
6
  "email": "stevegires@gmail.com"
@@ -114,7 +114,7 @@
114
114
  "docs:previews": "vitest run --project previews"
115
115
  },
116
116
  "dependencies": {
117
- "@nxgt/material-core": "^0.7.0",
117
+ "@nxgt/material-core": "^0.7.1",
118
118
  "@base-ui/react": "^1.6.0",
119
119
  "@dnd-kit/core": "^6.3.1",
120
120
  "@dnd-kit/sortable": "^10.0.0",