@nxgt/material 3.85.0 → 3.86.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 (143) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +39 -5
  3. package/dist/{alert-dialog-1t6ULy5i.js → alert-dialog-DZnt8PYE.js} +1 -1
  4. package/dist/{async-slice-xyA9Uzct.js → async-slice-D3OdV2BS.js} +1 -1
  5. package/dist/{button-DhiNGrdT.js → button-MAL5an7L.js} +1 -1
  6. package/dist/{calculator-BcgxxScL.js → calculator-CyBjPONf.js} +10 -10
  7. package/dist/calculator-field.js +6 -6
  8. package/dist/{calculator-segment-C4WQAHcN.js → calculator-segment-BigMpjhn.js} +1 -1
  9. package/dist/calculator.js +1 -1
  10. package/dist/charts.js +3 -3
  11. package/dist/{chip-m0_usuzd.js → chip-DKAKoNxq.js} +2 -2
  12. package/dist/{code-editor-BgKFrcld.js → code-editor-B6dFQSUJ.js} +4 -4
  13. package/dist/code-editor.js +4 -4
  14. package/dist/{code-language-items-BWw17W9s.js → code-language-items-CXEMiWoF.js} +2 -2
  15. package/dist/components/components.css +2 -0
  16. package/dist/components/theme-tokens.css +18 -0
  17. package/dist/components/ui/layout/app-bar/top-app-bar/top-app-bar-collapse.css +60 -0
  18. package/dist/components/ui/layout/app-bar/top-app-bar/top-app-bar.css +205 -0
  19. package/dist/components/ui/layout/app-shell/app-header.css +70 -63
  20. package/dist/{components-cgUDvVTe.js → components-BVHbzzju.js} +17330 -16928
  21. package/dist/components.js +24 -24
  22. package/dist/{conversion-variant-DultZZQm.js → conversion-variant-BT5n4-jm.js} +3 -3
  23. package/dist/{currency-field-CPbaM7ay.js → currency-field-t41sFYg_.js} +4 -4
  24. package/dist/{dialog-DmlG9opW.js → dialog-BjKiRXJg.js} +3 -3
  25. package/dist/dnd.js +1 -1
  26. package/dist/{dropdown-menu-CDKev8dk.js → dropdown-menu-IRFp4CH9.js} +1 -1
  27. package/dist/editor.js +7 -7
  28. package/dist/{empty-state-C56000TB.js → empty-state-C70prDDr.js} +1 -1
  29. package/dist/{financial-variant-BYjQ690K.js → financial-variant-DqW4HJ_v.js} +5 -5
  30. package/dist/{hooks-BTwLAy_A.js → hooks-CjO11MS7.js} +2 -2
  31. package/dist/hooks.js +2 -2
  32. package/dist/{hover-card-CDtW6utN.js → hover-card-cU_aJa18.js} +1 -1
  33. package/dist/i18n.js +3 -3
  34. package/dist/{icon-badge-ec7MmUGA.js → icon-badge-D3KO6uEQ.js} +1 -1
  35. package/dist/{icon-button-CG4aO8Gh.js → icon-button-DrqNk1Ww.js} +1 -1
  36. package/dist/lib/components/ui/fields/contacts/placeholders.d.ts +1 -1
  37. package/dist/lib/components/ui/layout/app-bar/index.d.ts +1 -1
  38. package/dist/lib/components/ui/layout/app-bar/top-app-bar/index.d.ts +1 -0
  39. package/dist/lib/components/ui/layout/app-bar/top-app-bar/partials/top-app-bar-actions.d.ts +15 -0
  40. package/dist/lib/components/ui/layout/app-bar/top-app-bar/partials/top-app-bar-headline.d.ts +4 -0
  41. package/dist/lib/components/ui/layout/app-bar/top-app-bar/partials/top-app-bar-link.d.ts +9 -0
  42. package/dist/lib/components/ui/layout/app-bar/top-app-bar/partials/top-app-bar-menu-section.d.ts +14 -0
  43. package/dist/lib/components/ui/layout/app-bar/top-app-bar/partials/top-app-bar-primary-action.d.ts +12 -0
  44. package/dist/lib/components/ui/layout/app-bar/top-app-bar/partials/top-app-bar-row.d.ts +23 -0
  45. package/dist/lib/components/ui/layout/app-bar/top-app-bar/partials/top-app-bar-title-block.d.ts +18 -0
  46. package/dist/lib/components/ui/layout/app-bar/top-app-bar/top-app-bar.actions.d.ts +43 -0
  47. package/dist/lib/components/ui/layout/app-bar/top-app-bar/top-app-bar.d.ts +9 -0
  48. package/dist/lib/components/ui/layout/app-bar/top-app-bar/top-app-bar.types.d.ts +120 -0
  49. package/dist/lib/components/ui/layout/app-bar/top-app-bar/top-app-bar.utils.d.ts +43 -0
  50. package/dist/lib/components/ui/layout/app-shell/activity-app-bar.d.ts +6 -6
  51. package/dist/lib/components/ui/layout/app-shell/activity-chrome.d.ts +6 -3
  52. package/dist/lib/components/ui/layout/app-shell/activity-column-props.d.ts +1 -1
  53. package/dist/lib/components/ui/layout/app-shell/activity-column.d.ts +2 -2
  54. package/dist/lib/components/ui/layout/app-shell/activity-layout-navigation.types.d.ts +32 -4
  55. package/dist/lib/components/ui/layout/app-shell/activity-layout.types.d.ts +12 -2
  56. package/dist/lib/components/ui/layout/app-shell/app-header.d.ts +9 -3
  57. package/dist/lib/components/ui/layout/app-shell/header-look.d.ts +14 -0
  58. package/dist/lib/components/ui/layout/app-shell/header-title.d.ts +9 -36
  59. package/dist/lib/components/ui/layout/app-shell/header.utils.d.ts +13 -3
  60. package/dist/lib/components/ui/layout/app-shell/index.d.ts +1 -1
  61. package/dist/lib/components/ui/layout/app-shell/navigation.utils.d.ts +7 -2
  62. package/dist/lib/components/ui/layout/app-shell/shell-scroll.d.ts +23 -1
  63. package/dist/lib/components/ui/layout/app-shell/shell-top-app-bar.d.ts +10 -0
  64. package/dist/lib/components/ui/layout/app-shell/shell-top-app-bar.parts.d.ts +58 -0
  65. package/dist/lib/components/ui/layout/app-shell/shell-top-app-bar.types.d.ts +26 -0
  66. package/dist/lib/components/ui/layout/app-shell/use-header-scroll.d.ts +16 -9
  67. package/dist/lib/components/ui/layout/app-shell/use-shell-header.d.ts +1 -1
  68. package/dist/lib/i18n/resources/en/en.d.ts +4 -0
  69. package/dist/lib/i18n/resources/en/material.json.d.ts +4 -0
  70. package/dist/lib/i18n/resources/fr/fr.d.ts +4 -0
  71. package/dist/lib/i18n/resources/fr/material.json.d.ts +4 -0
  72. package/dist/lib/i18n/resources/resources.d.ts +8 -0
  73. package/dist/lib/i18n/translator.d.ts +1 -1
  74. package/dist/lib.js +1 -1
  75. package/dist/main.js +28 -28
  76. package/dist/map.js +7 -7
  77. package/dist/material.css +1 -1
  78. package/dist/pdf-reader.js +1 -1
  79. package/dist/{percent-field-8_pILs15.js → percent-field-D47IlaPF.js} +1 -1
  80. package/dist/{picker-surface-pyGv-Qh9.js → picker-surface-Cg4V4-E0.js} +4 -4
  81. package/dist/{popover-3EF4q2CT.js → popover-Dzk4-l9U.js} +1 -1
  82. package/dist/{pos-types-Dv2UxKSN.js → pos-types-JOXMY4Y2.js} +1 -1
  83. package/dist/{pos-variant-DJvtN9N8.js → pos-variant-B2mS8bMB.js} +4 -4
  84. package/dist/print.js +1 -1
  85. package/dist/{programmer-variant-Bmcq2KzC.js → programmer-variant-C_Tl0P4_.js} +2 -2
  86. package/dist/{public-BNebh4z0.js → public-ByAsqBOf.js} +9 -9
  87. package/dist/{rich-code-block-DmX7d58S.js → rich-code-block-CJAfqk6b.js} +5 -5
  88. package/dist/{scientific-variant-DijUrpP8.js → scientific-variant-CskM4MLV.js} +2 -2
  89. package/dist/{sortable-provider-Cnmt_gWu.js → sortable-provider-ICS_vkLA.js} +2 -2
  90. package/dist/{spinner-C6LP2Rw8.js → spinner-CweqX5A9.js} +1 -1
  91. package/dist/styles.css +2 -0
  92. package/dist/{tabs-Bj1zOe7-.js → tabs-BOT9B4le.js} +1 -1
  93. package/dist/theme-picker.js +2 -2
  94. package/dist/{theme-toggle-Cxku5iBP.js → theme-toggle-WAFkncYx.js} +1 -1
  95. package/dist/{toolbar-items-CEIuSxU_.js → toolbar-items-BsgzHSNf.js} +4 -4
  96. package/dist/{translate-material-DI3nmQ6I.js → translate-material-DTIAPmlU.js} +1 -1
  97. package/dist/{translator-BstxG_hN.js → translator-BRZNhsy5.js} +8 -0
  98. package/dist/{use-chat-composer-BqLkk_iH.js → use-chat-composer-67MgWAHz.js} +15 -15
  99. package/dist/{use-heading-spy-nFyb9_KE.js → use-heading-spy-D8WbHJuK.js} +4 -4
  100. package/dist/{use-material-translation-1kCHtB0p.js → use-material-translation-CCy1Me0h.js} +1 -1
  101. package/dist/{use-return-focus-Hv8rn55-.js → use-return-focus-BA8G1qVU.js} +1 -1
  102. package/docs/README.md +1 -0
  103. package/docs/components/navigation-and-layout.md +85 -32
  104. package/docs/guides/app-shell.md +158 -63
  105. package/docs/guides/drawing.md +2 -0
  106. package/docs/guides/layouts.md +3 -3
  107. package/docs/guides/migrating-to-top-app-bar.md +156 -0
  108. package/docs/guides/theming.md +5 -0
  109. package/docs/previews/navigation-layout-appshell-blue-moon.webp +0 -0
  110. package/docs/previews/navigation-layout-appshell-footer-icon.webp +0 -0
  111. package/docs/previews/navigation-layout-appshell-menu-badge.webp +0 -0
  112. package/docs/previews/navigation-layout-appshell-page-ground.webp +0 -0
  113. package/docs/previews/navigation-layout-appshell-themes.webp +0 -0
  114. package/docs/previews/navigation-layout-appshell.webp +0 -0
  115. package/docs/previews/navigation-layout-topappbar.webp +0 -0
  116. package/docs/roadmap.md +17 -6
  117. package/docs/troubleshooting.md +80 -0
  118. package/lib/components/components.css +2 -0
  119. package/lib/components/theme-tokens.css +18 -0
  120. package/lib/components/ui/layout/app-bar/top-app-bar/top-app-bar-collapse.css +60 -0
  121. package/lib/components/ui/layout/app-bar/top-app-bar/top-app-bar.css +205 -0
  122. package/lib/components/ui/layout/app-shell/app-header.css +70 -63
  123. package/lib/styles.css +2 -0
  124. package/lib/themes/ambre.css +5 -1
  125. package/lib/themes/ardoise.css +5 -1
  126. package/lib/themes/bleumono.css +5 -1
  127. package/lib/themes/bluemoon.css +5 -1
  128. package/lib/themes/corail.css +5 -1
  129. package/lib/themes/foret.css +5 -1
  130. package/lib/themes/lavande.css +5 -1
  131. package/lib/themes/menthe.css +5 -1
  132. package/lib/themes/ocean.css +5 -1
  133. package/lib/themes/pink.css +5 -1
  134. package/lib/themes/prune.css +5 -1
  135. package/lib/themes/sable.css +5 -1
  136. package/lib/themes/slate.css +5 -1
  137. package/lib/themes/teal.css +5 -1
  138. package/package.json +2 -2
  139. package/dist/lib/components/ui/layout/app-bar/top-app-bar.d.ts +0 -18
  140. package/dist/lib/components/ui/layout/app-shell/app-header-row.d.ts +0 -3
  141. package/dist/lib/components/ui/layout/app-shell/header-context.d.ts +0 -10
  142. package/dist/lib/components/ui/layout/app-shell/page-title.d.ts +0 -27
  143. /package/dist/lib/components/ui/layout/{app-shell → app-bar/top-app-bar}/use-truncated-title.d.ts +0 -0
@@ -201,10 +201,11 @@ partitionMenu(items); // { content, footer }
201
201
  | `linkComponent` | `AppShellLinkComponent` | React Router `Link` | The link each menu entry renders, given `href` (see below). Defaults to the [`LinkComponentProvider`](../components/primitives.md#linkcomponentprovider)'s link when one is set. |
202
202
  | `headerTitle` | `ReactNode` | — | The header's leading title: the context the app is in (the active organisation's name), right after the phone's menu button, truncated when long; when it is cut off, its full text is the `title` tooltip (#629) |
203
203
  | `titleAfter` | `ReactNode` | — | A node right after the page's title in the app bar, outside its `h1` (a record's `Switcher`); wins over a route's `handle.titleAfter` (see [A slot after the title](#a-slot-after-the-title)) |
204
- | `headerContext` | `ReactNode` | — | A small muted context line (the tenant's name) after the page's title and its slot, from `md` up: outside the `h1`, truncated, its full text the tooltip once cut off. Not shown on a phone, where the menu sheet's `context` carries it (see [A context line](#a-context-line-after-the-title)) |
204
+ | `headerContext` | `ReactNode` | — | The bar's `subtitle`: a small muted context line (the tenant's name) under the page's title, from `md` up: outside the `h1`, truncated, its full text the tooltip once cut off. Not shown on a phone, where the menu sheet's `context` carries it (see [A context line](#a-context-line-after-the-title)) |
205
205
  | `breadcrumb` | `boolean` | `true` | `false` hides the breadcrumb trail, for an error page whose matched routes would show a misleading one (see [Breadcrumbs](#breadcrumbs)) |
206
206
  | `breadcrumbRoot` | `AppBreadcrumbRoot` (`{ label: ReactNode; to: string }`, or `{ label: string; to: string; icon: true }`) | — | The breadcrumb's first crumb, in place of the default home icon: a text link to `to`, or with `icon: true` the home icon alone, linked to `to` and named `label` (see [Breadcrumbs](#breadcrumbs)) |
207
- | `headerActions` | `ReactNode` | — | In the header, before the avatar: notifications, help, `ThemeToggle` |
207
+ | `headerActions` | `ReactNode \| TopAppBarAction[]` | — | In the header, before the avatar: notifications, help, `ThemeToggle`; an array of data draws free icon buttons, the rest in the account menu |
208
+ | `headerPrimaryAction` | `TopAppBarPrimaryAction` (`{ label, icon, onClick?, disabled? }`) | — | The screen's main action in the header: a `Button` from 768 px, a `Fab` on a phone |
208
209
  | `children` | `ReactNode` | — | The routed content, usually `<Outlet />` |
209
210
  | `contentAs` | `'main' \| 'div'` | `'main'` | The content region's element: the page's `<main>` landmark, or a `div` when your app renders its own `<main>` |
210
211
  | `scroll` | `'document' \| 'container'` | `'document'` | Who scrolls: the page (header and sidebar sticky), or the content region inside a shell one screen high |
@@ -212,7 +213,7 @@ partitionMenu(items); // { content, footer }
212
213
  | `menuOpen` | `boolean` | — | Controls the phone's menu sheet: open while `true`. Leave it out and the layout manages the sheet. Only a phone shows the sheet |
213
214
  | `onMenuOpenChange` | `(open: boolean) => void` | — | Called for every change of the sheet: the menu button, Escape, the close button, a followed link, a new location |
214
215
  | `navigation` | `ShellNavigation \| { phone?, tablet?, desktop?, hideOnScroll?, persistKey? }` | `modal-drawer` / `rail` / `rail` | The navigation per window size; see [`navigation` and `header`](#navigation-and-header) |
215
- | `header` | `'small' \| 'center' \| 'medium' \| 'large' \| 'none' \| { variant, collapse?, hideOnScroll?, tabs?, search? }` | `'small'` | The app bar's size, overridable per route with `handle.header`; see [The app bar variants](#the-app-bar-variants) |
216
+ | `header` | `'small' \| 'medium-flexible' \| 'large-flexible' \| 'search' \| 'none' \| { size?, align?, collapse?, hideOnScroll?, tabs?, search? }` | `'small'` | The app bar's size, overridable per route with `handle.header`; the old `'center'`, `'medium'`, `'large'` and `variant` are deprecated; see [Top app bar sizes](#top-app-bar-sizes) |
216
217
  | `backdrop` | `ReactNode \| false` | — | An exception to the theme's background (the page ground, [`--ground`](#the-page-ground), is painted either way), which the layout draws by default (Lavande's: the built-in gradient and blobs). A node replaces it for this screen: usually a `Background` preset from `@nxgt/material/backgrounds` (see [Use a background preset behind the shell](#use-a-background-preset-behind-the-shell)); `false` draws no background, a plain page |
217
218
  | `keepClear` | `boolean \| 'gaps'` | `true` | how the backdrop's glyphs keep clear of the page: `true` drops every glyph within 8px of a surface, a text or a control; `'gaps'` also keeps a glyph that fits in a gap of 24px or more between two surfaces; `false` keeps nothing clear (the edge and the header band still are). See [Keep the backdrop clear of the content](#keep-the-backdrop-clear-of-the-content) |
218
219
 
@@ -250,11 +251,49 @@ const MyLink: AppShellLinkComponent = ({ href, ...props }) => <Link to={href} {.
250
251
  // <ActivityLayout items={items} linkComponent={MyLink}>…</ActivityLayout>
251
252
  ```
252
253
 
254
+ Actions as data: `headerActions` also takes a `TopAppBarAction[]` (`label`,
255
+ `icon`, `onClick` or `href`, `placement: 'bar' | 'account'`, `group`, `selected`,
256
+ `disabled`). The first ones are free icon buttons in the bar (one on a phone, two
257
+ from 768 px); the others, and every `'account'` action, go to the menu opened by
258
+ the `avatar`, which then must hold no link or button. `headerPrimaryAction` is
259
+ the screen's main action:
260
+
261
+ ```tsx
262
+ <ActivityLayout
263
+ items={items}
264
+ avatar={<Avatar src={user.photo} />}
265
+ headerActions={[
266
+ { label: 'Notifications', icon: <Icon name="bell" />, onClick: openInbox },
267
+ { label: 'Help', icon: <Icon name="circle-question" />, href: '/help' },
268
+ { label: 'Sign out', placement: 'account', onClick: signOut },
269
+ ]}
270
+ headerPrimaryAction={{ label: 'New invoice', icon: <Icon name="plus" />, onClick: create }}
271
+ >
272
+ <Outlet />
273
+ </ActivityLayout>
274
+ ```
275
+
253
276
  `className` and the rest of the `div` props (`id`, `data-*`…) go to the outer
254
277
  element, `[data-slot=activity-layout]`. The sticky app bar (menu button,
255
278
  `headerTitle`, `headerActions` and avatar) is `[data-slot=app-header]`, so a
256
279
  test or an accessibility helper can name it without a structural selector;
257
- `AppHeader` carries the same slot when used on its own.
280
+ `AppHeader` (deprecated, use `TopAppBar`) carries the same slot when used on
281
+ its own. The bar is a `TopAppBar` drawn as the page's `<header>` banner; it keeps
282
+ the `app-header*` slot names below, which `TopAppBar` styles too. They become
283
+ `top-app-bar*` at the next major.
284
+
285
+ | `data-slot` | Marks |
286
+ | --- | --- |
287
+ | `app-header` | the bar's root (`<header>`) |
288
+ | `app-header-page-title` | the page's `h1` |
289
+ | `app-header-context` | the muted line under the title (`headerContext`) |
290
+ | `app-header-headline` | the second row of a medium or large bar |
291
+ | `app-header-collapsed-title` | the title's copy in the small row of a collapsing bar |
292
+ | `app-header-tabs` | the tab row under the bar |
293
+ | `app-header-title` | the leading `headerTitle` |
294
+ | `app-header-title-after` | the slot after the title (`titleAfter`) |
295
+ | `app-header-back` | the back arrow |
296
+
258
297
  `avatar` and `headerActions` are props because the shell holds no session:
259
298
  read the user wherever your app keeps it and pass the node.
260
299
 
@@ -328,7 +367,7 @@ today: the menu sheet on a phone, the rail from `md`, a small header.
328
367
  <ActivityLayout
329
368
  items={items}
330
369
  navigation={{ phone: 'modal-drawer', tablet: 'rail', desktop: 'rail' }}
331
- header={{ variant: 'small' }}
370
+ header={{ size: 'small' }}
332
371
  >
333
372
  <Outlet />
334
373
  </ActivityLayout>
@@ -346,9 +385,10 @@ today: the menu sheet on a phone, the rail from `md`, a small header.
346
385
  name alone draws them at every size, the phone included. Every name is drawn:
347
386
  `'modal-drawer'`, `'rail'`, `'rail-expanded'`, `'drawer'`, `'sidebar'`, `'top'`,
348
387
  `'two-level'`, `'floating'`, `'bar'` and `'none'`.
349
- - `header`: `'small'`, `'center'`, `'medium'`, `'large'` or `'none'`, or
350
- `{ variant, collapse, hideOnScroll, tabs, search }`. The five variants and
351
- `hideOnScroll` are drawn (see [The app bar variants](#the-app-bar-variants));
388
+ - `header`: `'small'`, `'medium-flexible'`, `'large-flexible'`, `'search'` or
389
+ `'none'`, or `{ size, align, collapse, hideOnScroll, tabs, search }` (the old
390
+ `'center'`, `'medium'`, `'large'` and `variant` are deprecated). The sizes and
391
+ `hideOnScroll` are drawn (see [Top app bar sizes](#top-app-bar-sizes));
352
392
  `search` and `tabs` are drawn (see [The app bar's search](#the-app-bars-search) and
353
393
  [Tabs under the app bar](#tabs-under-the-app-bar)). A route's `handle.back` draws a
354
394
  back arrow ([The back arrow](#the-back-arrow)).
@@ -676,71 +716,122 @@ the user asked for less motion: skip the slide and snap. The header
676
716
  (`navigation.hideOnScroll`) read it; the shell tracks the scroll only when one of
677
717
  them asks, and `NavigationToolbar` reads it by hand too.
678
718
 
679
- ### The app bar variants
719
+ ### Top app bar sizes
680
720
 
681
- `header` chooses the bar's size, the way Material 3 does. Unset is `'small'`:
682
- today's one row, byte for byte.
721
+ `header` chooses the bar's size, the way Material 3 does, and the shell draws it
722
+ with [`TopAppBar`](../components/navigation-and-layout.md#topappbar). Unset is
723
+ `'small'`: one row, the title beside the actions.
683
724
 
684
725
  ```tsx
685
726
  import { ActivityLayout, type AppShellHandle } from '@nxgt/material/components';
686
727
 
687
- <ActivityLayout items={items} header="large">
728
+ <ActivityLayout items={items} header="large-flexible">
688
729
  <Outlet />
689
730
  </ActivityLayout>
690
731
 
691
732
  // a route opts in, whatever the layout says
692
- export const handle = { title: 'Projects', header: 'large' } satisfies AppShellHandle;
733
+ export const handle = { title: 'Projects', header: 'large-flexible' } satisfies AppShellHandle;
693
734
 
694
- // a bar that goes away on the way down
695
- <ActivityLayout items={items} header={{ variant: 'small', hideOnScroll: true }} />
735
+ // a centred small bar that goes away on the way down
736
+ <ActivityLayout items={items} header={{ size: 'small', align: 'center', hideOnScroll: true }} />
696
737
  ```
697
738
 
698
- Defaults: `variant` is `'small'`; `collapse` is `true` for `'medium'` and
699
- `'large'`; `hideOnScroll` is `false`. Nothing to import beyond `styles.css`: the
700
- bar's CSS ships with it.
701
-
702
- - `'small'`: one row, the title beside the actions.
703
- - `'center'`: the title centred in the room between the menu button and the
704
- actions.
705
- - `'medium'` and `'large'`: the page's title on a second row, in display type
706
- (`large` is taller and bigger, up to two lines), under the small row. As the
707
- page scrolls the headline fades and the bar shrinks into the small row, which
708
- then shows the title (a copy hidden from assistive technology). `collapse:
709
- false` keeps the headline however far the page scrolls.
710
- - `'none'`: no bar. The screen's own `ActivityContent` title is then the page's
711
- `h1`; the phone's menu button goes with the bar, so pair it with a navigation
712
- that has one on its own (`navigation="bar"`).
713
- - `hideOnScroll`: the bar slides away while the page scrolls down (past 64px) and
714
- comes back on the way up. While away it is `inert` and out of the tab order; if
715
- the focus was inside it, the focus moves to the content region.
716
-
717
- A route gives its own through `handle.header` (a variant or the options, like
718
- the prop). The deepest route that declares one wins, and its options win over
719
- the layout's one by one: a route that only names a variant keeps the layout's
720
- `hideOnScroll`. It is read from the matched routes, so the server and the
721
- browser draw the same bar.
722
-
723
- The headline **is** the page's one `h1` (the small row's copy is `aria-hidden`).
739
+ Defaults: `size` is `'small'`, `align` is `'start'`, `collapse` is `true` for the
740
+ two flexible sizes, `hideOnScroll` is `false`. Nothing to import beyond
741
+ `styles.css`: the bar's CSS ships with it.
742
+
743
+ | Size | Use it for | What it draws |
744
+ | --- | --- | --- |
745
+ | `'small'` | most screens, a dense tool | one row: the title beside the actions. `align: 'center'` centres the title between the menu button and the actions |
746
+ | `'medium-flexible'` | a section's landing screen | the page's title on a second row (28 px), under the small row |
747
+ | `'large-flexible'` | a screen that opens a flow or a long list | the title on a second row, bigger (40 px, 36 px on a phone) and up to two lines |
748
+ | `'search'` | a search-first screen | the `header.search` field is the bar's centre; the title stays in the page as a visually hidden `h1`, so the screen keeps its heading |
749
+ | `'none'` | a full-screen tool that draws its own chrome | no bar. The screen's own `ActivityContent` title is then the page's `h1`; the phone's menu button goes with the bar, so pair it with a navigation that has one on its own (`navigation="bar"`) |
750
+
751
+ - **Collapse.** A flexible bar folds its second row into the small row as the
752
+ page scrolls: the headline fades and the bar shrinks over the headline row's
753
+ measured height, the title shrinks to 90% from its start edge, and the small
754
+ row then shows the title (a copy hidden from assistive technology). A scrolled
755
+ bar takes a soft tint and a 1px hairline. `collapse: false` keeps the headline
756
+ however far the page scrolls.
757
+ - **`hideOnScroll`**: the bar slides away while the page scrolls down (past 64px)
758
+ and comes back on the way up. While away it is `inert` and out of the tab order;
759
+ if the focus was inside it, the focus moves to the content region.
760
+ - **Subtitle.** `headerContext` is the bar's subtitle, a muted line under the title
761
+ (see [A context line after the title](#a-context-line-after-the-title)).
762
+ - **Actions and main action.** `headerActions` takes nodes or a `TopAppBarAction[]`,
763
+ and `headerPrimaryAction` the screen's main action (see
764
+ [The app bar](#the-app-bar-a-title-the-actions-the-account)).
765
+
766
+ The bar's height is `--nxgt-header-height` (default `4rem`, see
767
+ [the header height](./theming.md#the-header-height)): the row is that height
768
+ less the bar's own vertical padding, so a custom value resizes the bar, the
769
+ rail's header and the first-paint total together. A `className` on the bar (a
770
+ background, a radius, a shadow) wins over its at-rest look; once the page
771
+ scrolls, the scrolled tint replaces a `className` background. The bar is drawn
772
+ as the page's `<header>`; `title` on a standalone `TopAppBar` is its `<h1>` (it
773
+ was the HTML `title` attribute), and its root is a column whose children sit in
774
+ a `[data-slot="top-app-bar-row"]`, so layout utilities on the root's `className`
775
+ (`justify-between`, `gap-*`, `items-*`) no longer reach them (see
776
+ [Moving to TopAppBar](./migrating-to-top-app-bar.md#what-looks-different)).
777
+
778
+ A route gives its own through `handle.header`: a size (`'large-flexible'`) or the
779
+ options (`{ size, align, collapse, hideOnScroll, tabs, search }`, like the prop).
780
+ The deepest route that declares one wins, and its options win over the layout's
781
+ one by one: a route that only names a size keeps the layout's `hideOnScroll` and
782
+ `align`. It is read from the matched routes, so the server and the browser draw
783
+ the same bar.
784
+
785
+ The title **is** the page's one `h1` (the small row's copy is `aria-hidden`).
724
786
  A medium or large header needs a title in the bar, a route's `handle.title`;
725
787
  without one the bar stays small, so nothing changes for a screen that titles
726
788
  itself with `ActivityContent`. The server always renders the bar expanded and
727
789
  shown; the browser then follows the scroll.
728
790
 
791
+ **The search size.** `header: { size: 'search', search: { placeholder: 'Search projects' } }`
792
+ puts the search field at the bar's centre. A route that sets a `handle.title`
793
+ still gets its `h1`, visually hidden but in the accessibility tree and
794
+ labelling `<main>`; without a title the screen's own `ActivityContent` title is
795
+ the heading, as for any small bar. See [The app bar's search](#the-app-bars-search)
796
+ for the field's props.
797
+
798
+ **Phone and desktop.**
799
+
800
+ - On a phone the menu button is a ghost icon button leading the bar, with no
801
+ brand or logo; the bar keeps one free icon action, the rest go to the account
802
+ menu as inline sections. `headerPrimaryAction` is a floating button above the
803
+ bottom navigation. `headerContext` is hidden (the sheet's `context` covers it).
804
+ - From 768 px two icon actions fit, `headerPrimaryAction` is a `Button` in the
805
+ bar, and `headerContext` shows under the title.
806
+ - Heights: the headline row of a medium bar is 3 rem and a large bar's 6.5 rem
807
+ before it is measured, then follows the title's real height.
808
+
809
+ **Reduced motion.** Under `prefers-reduced-motion: reduce` the collapse snaps
810
+ between the two states at the halfway point, with no morph, no slide for
811
+ `hideOnScroll` and no transition on the bar's tint.
812
+
729
813
  The scroll is the shell's ([Scroll direction](#scroll-direction)): the window,
730
814
  or the content region with `scroll="container"` (a hidden bar then gives its
731
815
  room back). Once per animation frame the bar sets one CSS variable on itself,
732
- `--nxgt-header-progress` (0 expanded, 1 collapsed), through `setProperty`, so
733
- no inline style reaches the markup; the sizes are CSS in `app-header.css`.
734
- Under reduced motion the bar snaps between the two states halfway, with no slide.
735
- A plain shell, with no collapsing or hiding bar, tracks no scroll at all.
736
-
737
- `AppHeader` takes the same as props (`variant`, `collapse`, `hideOnScroll`, the
738
- `headline` node, `tabs` (a node, default none) and `back` (a `ShellBack`,
739
- default none); see [Using the parts](#using-the-parts)), and so does `TopAppBar` (`variant`, `headline`) for a bar
740
- of your own. Known limits: a hidden bar in a `scroll="container"` shell that is
741
- barely taller than its container can flicker at the very bottom (the region
742
- grows, so the scroll clamps); the scroll-driven CSS animation enhancement is not
743
- used, the JavaScript path drives every browser.
816
+ `--nxgt-header-progress` (0 expanded, 1 collapsed), through `setProperty`, and
817
+ `data-scrolled` once the page has moved, so no inline style reaches the markup
818
+ and a frame of scrolling does not re-render. A plain shell, with no collapsing
819
+ or hiding bar, tracks no scroll at all.
820
+
821
+ **Deprecated spellings.** `'center'`, `'medium'`, `'large'` and `variant` in the
822
+ options still work and log a one-time development warning (see
823
+ [troubleshooting](../troubleshooting.md#activitylayout-header-center-medium-or-large-is-deprecated)):
824
+ `'center'` is `{ size: 'small', align: 'center' }`, `'medium'` is
825
+ `'medium-flexible'`, `'large'` is `'large-flexible'`. See
826
+ [Moving to TopAppBar](./migrating-to-top-app-bar.md).
827
+
828
+ `TopAppBar` takes the same as props (`size`, `align`, `collapse`, `hideOnScroll`,
829
+ `title`, `subtitle`, `tabs`, `search`) for a bar of your own; `AppHeader` is its
830
+ deprecated wrapper (see [Using the parts](#using-the-parts)). Known limits: a
831
+ hidden bar in a `scroll="container"` shell that is barely taller than its
832
+ container can flicker at the very bottom (the region grows, so the scroll
833
+ clamps); the scroll-driven CSS animation enhancement is not used, the JavaScript
834
+ path drives every browser.
744
835
 
745
836
  ### The app bar's search
746
837
 
@@ -755,13 +846,13 @@ import { ActivityLayout } from '@nxgt/material/components';
755
846
  <ActivityLayout
756
847
  items={items}
757
848
  header={{
758
- variant: 'small',
849
+ size: 'small',
759
850
  search: { placeholder: 'Search projects', shortcut: '/', onSubmit: (q) => navigate(`/search?q=${q}`) },
760
851
  }}
761
852
  />
762
853
 
763
854
  // a route turns it off, or changes it, like the other options
764
- export const handle = { title: 'Settings', header: { variant: 'small', search: false } };
855
+ export const handle = { title: 'Settings', header: { size: 'small', search: false } };
765
856
  ```
766
857
 
767
858
  The search never changes the shape of the page: the content stays mounted when
@@ -792,7 +883,7 @@ const tabs = (
792
883
  );
793
884
 
794
885
  export function Layout({ items }: { items: MenuItem[] }) {
795
- return <ActivityLayout items={items} header={{ variant: 'small', tabs }} />;
886
+ return <ActivityLayout items={items} header={{ size: 'small', tabs }} />;
796
887
  }
797
888
  ```
798
889
 
@@ -809,13 +900,13 @@ const projectTabs = (
809
900
 
810
901
  export const handle = {
811
902
  title: 'Atlas',
812
- header: { variant: 'small', tabs: projectTabs }, // tabs: null drops the layout's
903
+ header: { size: 'small', tabs: projectTabs }, // tabs: null drops the layout's
813
904
  } satisfies AppShellHandle;
814
905
  ```
815
906
 
816
907
  The option is `header.tabs` (there is no `handle.headerTabs`): a route's `tabs`
817
908
  replaces the layout's whole, `tabs: null` drops them for that route, and a route
818
- that only names a variant keeps the layout's. The row's height is one CSS
909
+ that only names a size keeps the layout's. The row's height is one CSS
819
910
  variable, `--nxgt-header-tabs` (3rem, in `app-header.css`). The bar's whole
820
911
  height (the row, a headline row and the tabs) is `--nxgt-header-total` on the
821
912
  `ActivityLayout` root, measured with a `ResizeObserver` (worked out in CSS from
@@ -1045,14 +1136,14 @@ A multi-tenant app keeps the current tenant visible on a desktop screen with
1045
1136
  ```
1046
1137
 
1047
1138
  - **It is outside the `h1`** (`[data-slot=app-header-context]`), so the
1048
- heading's name stays the page's title; it is truncated (at most 14rem), and
1049
- its full text is the `title` tooltip once it is cut off (#629).
1139
+ heading's name stays the page's title; it sits under the title (the bar's
1140
+ subtitle), is truncated to the title's width, and its full text is the `title` tooltip once it is cut off (#629).
1050
1141
  - **From `md` up only.** On a phone it is hidden: the menu sheet's `context`
1051
1142
  line covers it. The two props are independent, so setting `context` alone
1052
1143
  adds nothing to the bar, and setting both never shows the name twice on one
1053
1144
  screen.
1054
1145
  - **The title truncates first**: with a long title, the slot and the line
1055
- together, the title gives way (two lines on a phone, then an ellipsis); the slot stays whole and the line keeps its cap.
1146
+ together, the title gives way (two lines on a phone, then an ellipsis); the slot stays whole and the line stays under the title.
1056
1147
  - **It needs a page title**: while the bar shows none (no `handle.title`),
1057
1148
  `headerTitle` is the context line and `headerContext` is ignored.
1058
1149
 
@@ -1793,8 +1884,8 @@ export function InvoiceTrail() {
1793
1884
 
1794
1885
  ## Using the parts
1795
1886
 
1796
- `AppSidebar`, `AppHeader` and `AppBreadcrumb` are exported for a layout of your
1797
- own. They take the same `items`; `AppHeader` takes `actions`, `avatar`,
1887
+ `AppSidebar`, `AppHeader` (deprecated, use `TopAppBar`) and `AppBreadcrumb` are
1888
+ exported for a layout of your own. They take the same `items`; `AppHeader` takes `actions`, `avatar`,
1798
1889
  `showMenuIcon` (default `true`), and `menuOpen` / `onMenuOpenChange` to control
1799
1890
  its phone menu sheet, `tabs` (a node, default none: a `TabsNav` drawn as a row
1800
1891
  at the foot of the bar) and `back` (a `ShellBack`, default none: the back arrow,
@@ -1802,6 +1893,10 @@ before the menu button). The lower-level `Sidebar`, `TopAppBar` and
1802
1893
  `Breadcrumb` primitives are on the
1803
1894
  [navigation & layout](../components/navigation-and-layout.md) page.
1804
1895
 
1896
+ The shell draws its bar with `TopAppBar`, as the page's `<header>`. `AppHeader`
1897
+ is deprecated: it stays as a wrapper over `TopAppBar` with the same props, and
1898
+ warns once in development.
1899
+
1805
1900
  `AppHeader` needs a React Router context even on its own: its menu sheet reads
1806
1901
  the location (`useLocation`) to close when it changes, and outside a router
1807
1902
  that hook throws. Render it under your `RouterProvider` — or, in a test, inside
@@ -187,6 +187,8 @@ A fill, a stroke or a gradient stop is a **role**, a **tone** or a free colour.
187
187
  `outline-variant`, `success`, `info`, `warning`, `neutral`. `ROLES` lists them
188
188
  and `ROLE_VARS` gives each one's CSS variable (`on-primary-container` is
189
189
  `--primary-text`, `outline` is `--muted-foreground`).
190
+ The drawing role `surface-container` resolves to `--card`; it is not the
191
+ `--surface-container` theme token (`bg-surface-container`).
190
192
  They follow light and dark, scoped themes and a retheme with no re-render.
191
193
  - **Tones**: `tone('success')`, `tone('success', 'container')` (variants `base`,
192
194
  `text`, `on`, `container`) name a theme tone or an app tone of your own, the
@@ -75,7 +75,7 @@ layout of your own. Every preset, its entries (`bar`, `priority`, `group`,
75
75
  <ActivityLayout
76
76
  items={items}
77
77
  header={{
78
- variant: 'large',
78
+ size: 'large-flexible',
79
79
  hideOnScroll: true,
80
80
  search: { placeholder: 'Search projects', shortcut: '/' },
81
81
  tabs: (
@@ -95,13 +95,13 @@ A route changes its own bar with a handle, and draws a back arrow:
95
95
  ```tsx
96
96
  export const handle = {
97
97
  title: 'Atlas redesign',
98
- header: { variant: 'medium' },
98
+ header: { size: 'medium-flexible' },
99
99
  back: { to: '/projects', label: 'Projects' },
100
100
  } satisfies AppShellHandle;
101
101
  ```
102
102
 
103
103
  The search is a pill with a docked results panel from 768px and a full-screen
104
- view on a phone. See [The app bar variants](app-shell.md#the-app-bar-variants),
104
+ view on a phone. See [Top app bar sizes](app-shell.md#top-app-bar-sizes),
105
105
  [the search](app-shell.md#the-app-bars-search),
106
106
  [the tabs](app-shell.md#tabs-under-the-app-bar) and
107
107
  [the back arrow](app-shell.md#the-back-arrow).
@@ -0,0 +1,156 @@
1
+ # Moving to TopAppBar
2
+
3
+ `ActivityLayout` draws its app bar with `TopAppBar`, and `TopAppBar` has new
4
+ sizes, a title that is the page's `h1`, free icon actions and a main action. This
5
+ is a minor release: every old spelling keeps working, logs a one-time development
6
+ warning, and is removed no earlier than the next major. This page lists each one
7
+ with what replaces it, what looks different, and which slots the next major renames.
8
+
9
+ ## Contents
10
+
11
+ - [`AppHeader` becomes `TopAppBar`](#appheader-becomes-topappbar)
12
+ - [`header` values and `handle.header`](#header-values-and-handleheader)
13
+ - [`TopAppBar` `variant` and `headline`](#topappbar-variant-and-headline)
14
+ - [Actions, the account menu and a main action](#actions-the-account-menu-and-a-main-action)
15
+ - [What looks different](#what-looks-different)
16
+ - [The `data-slot` rename in the next major](#the-data-slot-rename-in-the-next-major)
17
+
18
+ ## `AppHeader` becomes `TopAppBar`
19
+
20
+ **Search**: `AppHeader`.
21
+
22
+ `AppHeader` is a thin wrapper over `TopAppBar` with the props it always had; it
23
+ logs `AppHeader: is deprecated, use TopAppBar` once in development. Inside
24
+ `ActivityLayout` there is nothing to do: the layout does not use the wrapper.
25
+ In a layout of your own, draw the bar with `TopAppBar`; the menu button and back
26
+ arrow go in `leading`, the avatar in `trailing`.
27
+
28
+ **Replace**:
29
+
30
+ ```tsx
31
+ // before
32
+ <AppHeader items={items} variant="large" headline={<h1>Orders</h1>} avatar={<Avatar />} />
33
+
34
+ // after
35
+ <TopAppBar
36
+ as="header"
37
+ size="large-flexible"
38
+ title="Orders"
39
+ leading={<MenuButton />}
40
+ trailing={<Avatar />}
41
+ />
42
+ ```
43
+
44
+ `TopAppBar` does not draw the phone's menu sheet or read the router: the shell
45
+ does. A bar of your own passes its own `leading` and `actions`. `AppHeader` also
46
+ renders a `div`, where `as="header"` makes the bar the page's banner.
47
+
48
+ ## `header` values and `handle.header`
49
+
50
+ **Search**: `header="center"`, `header="medium"`, `header="large"`,
51
+ `header: 'center'` (and `'medium'`, `'large'`), `variant:` inside a `header` object
52
+ or a route's `handle.header`.
53
+
54
+ | Before | After |
55
+ | --- | --- |
56
+ | `'small'` | `'small'` (unchanged) |
57
+ | `'center'` | `{ size: 'small', align: 'center' }` |
58
+ | `'medium'` | `'medium-flexible'` |
59
+ | `'large'` | `'large-flexible'` |
60
+ | `'none'` | `'none'` (unchanged) |
61
+ | `{ variant: 'large', hideOnScroll: true }` | `{ size: 'large-flexible', hideOnScroll: true }` |
62
+ | (none) | `'search'`: the search field is the bar's centre |
63
+
64
+ A string in the new spelling sets the size only, so the layout's `align` stays.
65
+ A route's header in one spelling overrides the layout's in the other. The warning
66
+ is `ActivityLayout: header `medium` is deprecated; use 'medium-flexible'.`
67
+ ([troubleshooting](../troubleshooting.md#activitylayout-header-center-medium-or-large-is-deprecated)).
68
+
69
+ `resolveHeader` and `ResolvedHeader` gain `size` and `align`; `variant` stays
70
+ filled with the old equivalent. `ShellHeaderVariant` is widened with the new
71
+ values: an exhaustive `switch` over it needs them.
72
+
73
+ ## `TopAppBar` `variant` and `headline`
74
+
75
+ **Search**: `<TopAppBar variant=`, `headline=`.
76
+
77
+ | Before | After |
78
+ | --- | --- |
79
+ | `variant="center"` | `size="small" align="center"` |
80
+ | `variant="medium"` | `size="medium-flexible"` |
81
+ | `variant="large"` | `size="large-flexible"` |
82
+ | `headline={<h1>Projects</h1>}` | `title="Projects"` (the bar renders the `<h1>`) |
83
+
84
+ `headline` rendered in a non-heading element with display type; it still does, now
85
+ with the bar's title styling. `data-variant` is still set (`"search"` is new), and
86
+ `data-size` and `data-align` are added.
87
+
88
+ ## Actions, the account menu and a main action
89
+
90
+ All additive. `headerActions` (and `TopAppBar`'s `actions`) takes a
91
+ `TopAppBarAction[]` as well as a node: free ghost icon buttons, with the rest in
92
+ the account menu on the avatar. `headerPrimaryAction` (`primaryAction`) is a
93
+ `Button` from 768 px and a floating button on a phone. `headerContext` is now the
94
+ bar's subtitle, under the title. An `actions` that is a node, an array of elements
95
+ included, and `trailing` draw as before. See
96
+ [Top app bar sizes](./app-shell.md#top-app-bar-sizes) and
97
+ [the `TopAppBar` props](../components/navigation-and-layout.md#topappbar).
98
+
99
+ An avatar used as `trailing` next to an `actions` array of data becomes the menu's
100
+ trigger (a `<button>`): it must hold no link or button.
101
+
102
+ ## What looks different
103
+
104
+ No code change, but snapshots of an app that renders the bar change:
105
+
106
+ - **Heights.** The headline row of a medium bar is 3 rem and a large bar's 6.5 rem
107
+ before it is measured (it was 5 rem and 8 rem), then follows the title's real
108
+ height. Medium titles are 28 px, large ones 40 px (36 px on a phone).
109
+ - **A flat bar.** It sits on the page ground with no shadow, border or surface
110
+ background (it was a translucent card). Once scrolled it takes the
111
+ `--surface-container` tint and a 1 px `--border` hairline (the `data-scrolled`
112
+ attribute). A `className` background, radius or shadow wins at rest (the
113
+ bar's own look is in `@layer components`); once scrolled the tint replaces a
114
+ `className` background, as the scrolled state's look.
115
+ - **A ghost menu button on a phone**, leading the bar, with no logo or brand.
116
+ Desktop is unchanged.
117
+ - **Vertical padding** sits on the bar itself, so `className="py-1"` still works.
118
+ - **`position`.** A `TopAppBar` is no longer `position: relative` except
119
+ `size="search"`, so a `sticky` class works.
120
+ - **The root is a column.** A standalone `TopAppBar` used to be one flex row on
121
+ the root; the root is now `flex-col` and its children sit in a
122
+ `[data-slot="top-app-bar-row"]` (a headline row and the tabs follow it). A
123
+ layout utility in the root's `className` (`justify-between`, `gap-*`,
124
+ `items-*`, a horizontal `px-*`) no longer reaches the children; aim it at the
125
+ row, for example `className="[&_[data-slot=top-app-bar-row]]:justify-between"`.
126
+ A vertical `py-*` on the root still works.
127
+ - **`title` is the page's heading.** On the old `TopAppBar`, `title` was the HTML
128
+ `title` attribute (a native tooltip). It is now the bar's own title and renders
129
+ an `<h1>`. To keep a tooltip, put `title` on an element you pass as a child.
130
+ - **The shell's bar** is the page's `<header>` (a banner landmark) instead of a
131
+ `<div>`. It keeps its `data-slot` names; `app-header-page-title-row` is now the
132
+ title block, always present around the title and the context line. A small
133
+ bar with `hideOnScroll` or tabs still carries `data-variant="small"`. The
134
+ internal `--nxgt-header-h` custom property (never documented) is gone;
135
+ `--nxgt-header-height` still sets the bar's height, and the first-paint total
136
+ `--nxgt-header-total` follows it.
137
+
138
+ ## The `data-slot` rename in the next major
139
+
140
+ The shell keeps its `app-header*` `data-slot` names in this minor, so selectors
141
+ and tests written against them keep working; `TopAppBar`'s CSS styles both
142
+ families. The next major renames the slots in the table below. Update selectors then, or now with the new
143
+ names on a `TopAppBar` of your own:
144
+
145
+ | Today (shell) | Next major |
146
+ | --- | --- |
147
+ | `app-header` | `top-app-bar` |
148
+ | `app-header-page-title` | `top-app-bar-title` |
149
+ | `app-header-headline` | `top-app-bar-headline` |
150
+ | `app-header-collapsed-title` | `top-app-bar-collapsed-title` |
151
+ | `app-header-context` | `top-app-bar-subtitle` |
152
+ | `app-header-tabs` | `top-app-bar-tabs` |
153
+
154
+ The other `app-header*` slots (`app-header-title`, `-title-after`, `-back`, `-menu`) are not in this table: their next-major names are not decided yet, so do not rely on a `top-app-bar-*` equivalent.
155
+
156
+ **Search**: `app-header` in tests, CSS and `data-slot=` selectors.
@@ -73,6 +73,7 @@ at the top are needed is in [getting started](../getting-started.md#styles).
73
73
  | `--error-text` | `text-error-text` | error drawn as text: field messages, `HelperText` in its error state, form and dialog errors, `variant="error"` menu items |
74
74
  | `--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` |
75
75
  | `--ground` | `bg-ground` | the page's ground under `ActivityLayout`: the tinted colour the cards stand on (see [Page ground](#page-ground)) |
76
+ | `--surface-container`, `--surface-container-high` | `bg-surface-container`, `bg-surface-container-high` | tinted chrome: `--surface-container` is the scrolled top app bar (draw it with a 1px `--border` hairline), the primary at 12 % over `--ground` in light (less for a very dark primary, solved per seed) and 26 % in dark; `--surface-container-high` is raised chrome such as a search field, the primary at 16 % over `--background` in light (+4 points over the solved share) and 34 % over `--ground` in dark. Foreground, muted foreground and primary text keep 4.5:1 or more on both, in every preset |
76
77
  | `--card`, `--popover`, `--tooltip` (+ `-foreground`) | `bg-card`, `bg-popover`, `bg-tooltip` | surfaces |
77
78
  | `--card-edge` | `border-card-edge` | the edge of a `Card`, a `Popover` and a `Dialog`: `var(--border)` in light, `color-mix(in oklab, white 8%, var(--card))` in dark, a hairline lighter than the surface so it lifts off `--ground` |
78
79
  | `--border`, `--input`, `--ring` | `border-border`, `border-input`, `ring-ring` | outlines |
@@ -83,6 +84,10 @@ at the top are needed is in [getting started](../getting-started.md#styles).
83
84
  | `--nxgt-cards-min`, `--nxgt-cards-gap` | set on the list or an ancestor | the narrowest a `DataTable`/`ServerTable` card is (default `15rem`) with `cardDensity="compact"`, and the gap between cards (`0.75rem`, `0.5rem` when compact): see [Cards and tiles](./cards-and-tiles.md#columns) |
84
85
  | `--nxgt-field-radius`, `--nxgt-field-inner-radius` | `rounded-field` (and its sides, `rounded-s-field`…), `rounded-field-inner` | every field's corners, and the corner a field meets its open list with: see [The field box](#the-field-box) |
85
86
 
87
+ ```tsx
88
+ <header className="bg-surface-container border-b border-border" />
89
+ ```
90
+
86
91
  Some colours are derived rather than tokens of their own:
87
92
  `bg-background-paper` (5 % primary), `bg-paper` (0.5 % primary),
88
93
  `bg-primary-container`, `bg-secondary-container`, `bg-tertiary-container`,
package/docs/roadmap.md CHANGED
@@ -6,12 +6,6 @@ version they shipped in, and planned entries may name the major they target.
6
6
 
7
7
  ## Now
8
8
 
9
- - **App shell header refresh** — `AppHeader` and the look `TopAppBar`
10
- shares sit flat on the page ground and tint when content scrolls under
11
- them; the actions are grouped in a pill, the search is a pill field (an
12
- icon on a phone), and the menu button is a ghost icon button. The new look
13
- is the default in a minor, with no API change; an A/B preview comes first
14
- ([#2503](https://github.com/softistx/nxgt-material/issues/2503)).
15
9
  - **Calculator** — `@nxgt/material/calculator` is an exact-decimal calculator
16
10
  (Standard, Scientific, point of sale, Financial, Conversion, Programmer;
17
11
  every variant but Standard loads on demand)
@@ -386,6 +380,23 @@ version they shipped in, and planned entries may name the major they target.
386
380
 
387
381
  ## Shipped
388
382
 
383
+ - **App shell header refresh: the TopAppBar family (next minor)** — `TopAppBar`
384
+ takes four sizes (`small`, `medium-flexible`, `large-flexible`, `search`), an
385
+ `align`, a `title` that is the page's `h1` with a `subtitle`, and a collapse
386
+ from large to small on scroll with a morph (snapped under reduced motion).
387
+ It sits flat on the page ground and tints when content scrolls under it,
388
+ with new surface-container tokens. Actions are data (`TopAppBarAction`):
389
+ free icon buttons plus an account menu on the avatar, and a `primaryAction`
390
+ that is a floating button on a phone. `ActivityLayout` draws its bar with it
391
+ (`header` / `handle.header` take the new sizes, `headerActions` takes data,
392
+ `headerPrimaryAction` is new, `headerContext` is the subtitle) and the
393
+ phone's menu button is a ghost icon leading the bar. `AppHeader` and the old
394
+ `variant`, `headline` and `'center'` / `'medium'` / `'large'` values are
395
+ deprecated, and some `app-header*` slots are renamed in the next major (see the guide)
396
+ ([#2503](https://github.com/softistx/nxgt-material/issues/2503),
397
+ [guide](guides/app-shell.md#top-app-bar-sizes),
398
+ [moving to `TopAppBar`](guides/migrating-to-top-app-bar.md)).
399
+
389
400
  - **Seven more layout recipes (next minor)** — whole screens to start from under Guides/Recipes, each a tested Storybook story with its code in the plugin's `screen-recipes` skill: a team feed, a gallery of pictures by day, sign-in, sign-up and forgotten-password screens, a messages thread, settings with sections, an overview of rearrangeable widgets and a document editor, on the layouts the shell ships ([#2413](https://github.com/softistx/nxgt-material/issues/2413)).
390
401
 
391
402
  - **Search dialog and results page recipe (next patch)** — a command dialog (Ctrl/Cmd+K and a header button), debounced grouped suggestions, type chips, a conditional filter and highlighted result cards, with its code in the plugin's `screen-recipes` skill ([#2333](https://github.com/softistx/nxgt-material/issues/2333)).