prime-ui-kit 0.9.1 → 0.9.3

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 (169) hide show
  1. package/README.md +30 -12
  2. package/dist/chunks/{chunk-NHOWNIAR.js → chunk-6JTZOA5I.js} +1472 -671
  3. package/dist/chunks/{chunk-DB2Y3XXA.js → chunk-OAE4IQX2.js} +2845 -1732
  4. package/dist/chunks/{chunk-KCQFOORX.js → chunk-U5SR2LZR.js} +17 -4
  5. package/dist/color-picker.js +2 -2
  6. package/dist/components/button/Button.d.ts +19 -2
  7. package/dist/components/card/Card.d.ts +1 -1
  8. package/dist/components/code-block/CodeBlock.d.ts +1 -1
  9. package/dist/components/color-picker/ColorPicker.d.ts +1 -1
  10. package/dist/components/data-table/DataTable.d.ts +6 -1
  11. package/dist/components/data-table/Row.d.ts +3 -1
  12. package/dist/components/data-table/rows.d.ts +4 -0
  13. package/dist/components/data-table/types.d.ts +5 -0
  14. package/dist/components/digit-input/DigitInput.d.ts +6 -1
  15. package/dist/components/dnd/Dnd.d.ts +1 -1
  16. package/dist/components/dnd/dragSession.d.ts +9 -0
  17. package/dist/components/dnd/useDropTarget.d.ts +5 -0
  18. package/dist/components/dnd/useFlipList.d.ts +3 -3
  19. package/dist/components/drawer/Drawer.d.ts +8 -5
  20. package/dist/components/dropdown/Dropdown.d.ts +17 -0
  21. package/dist/components/index.d.ts +9 -2
  22. package/dist/components/input/Input.d.ts +16 -1
  23. package/dist/components/input/passwordStrength.d.ts +9 -0
  24. package/dist/components/kanban/Kanban.d.ts +129 -0
  25. package/dist/components/modal/Modal.d.ts +5 -2
  26. package/dist/components/page-toolbar/PageToolbar.d.ts +53 -0
  27. package/dist/components/progress-bar/ProgressBar.d.ts +16 -1
  28. package/dist/components/segmented-control/SegmentedControl.d.ts +5 -2
  29. package/dist/components/sparkline/Sparkline.d.ts +26 -0
  30. package/dist/components/tabs/Tabs.d.ts +5 -2
  31. package/dist/hooks/useEdgeOverflow.d.ts +16 -0
  32. package/dist/hooks/useMediaQuery.d.ts +7 -0
  33. package/dist/hooks/useSwipeDismiss.d.ts +34 -0
  34. package/dist/icons/registry.d.ts +2 -0
  35. package/dist/index.css +2977 -1306
  36. package/dist/index.d.ts +1 -0
  37. package/dist/index.js +17 -3
  38. package/dist/internal/CheckMark.d.ts +12 -0
  39. package/dist/internal/MorphText.d.ts +12 -0
  40. package/dist/internal/RollingNumber.d.ts +14 -0
  41. package/dist/internal/overlay/FloatingPanel.d.ts +3 -1
  42. package/dist/internal/overlay/useFloatingLayer.d.ts +11 -1
  43. package/dist/internal/surfaceDepth.d.ts +25 -0
  44. package/dist/internal/swatch.d.ts +4 -1
  45. package/dist/internal/touchTarget.d.ts +4 -0
  46. package/dist/layout/app-header/AppHeader.d.ts +110 -0
  47. package/dist/layout/app-shell/AppShell.d.ts +16 -11
  48. package/dist/layout/bottom-nav/BottomNav.d.ts +80 -0
  49. package/dist/layout/index.d.ts +5 -1
  50. package/dist/motion/celebrate.d.ts +18 -0
  51. package/dist/tokens/layers.d.ts +37 -0
  52. package/dist/tokens/naming.d.ts +1 -1
  53. package/dist/tokens/primitives.d.ts +4 -0
  54. package/dist/tokens/semantic.d.ts +122 -56
  55. package/dist/tokens/themes/dark.d.ts +10 -14
  56. package/package.json +3 -2
  57. package/src/components/accordion/COMPONENT.md +5 -5
  58. package/src/components/avatar/COMPONENT.md +1 -1
  59. package/src/components/badge/COMPONENT.md +2 -2
  60. package/src/components/banner/COMPONENT.md +1 -1
  61. package/src/components/button/COMPONENT.md +26 -5
  62. package/src/components/button/examples/download.tsx +45 -0
  63. package/src/components/button/examples/hold-to-confirm.tsx +23 -0
  64. package/src/components/button/examples/label-morph.tsx +33 -0
  65. package/src/components/card/COMPONENT.md +4 -2
  66. package/src/components/card/examples/narrow.tsx +2 -2
  67. package/src/components/card/examples/panel-chart.tsx +1 -1
  68. package/src/components/card/examples/variants.tsx +1 -1
  69. package/src/components/checkbox/examples/examples.module.css +5 -3
  70. package/src/components/code-block/COMPONENT.md +6 -6
  71. package/src/components/code-block/examples/overview.tsx +1 -1
  72. package/src/components/code-block/examples/variants.tsx +1 -1
  73. package/src/components/color-picker/COMPONENT.md +1 -1
  74. package/src/components/color-swatches/COMPONENT.md +5 -5
  75. package/src/components/command-menu/COMPONENT.md +1 -1
  76. package/src/components/crossfade/COMPONENT.md +1 -1
  77. package/src/components/crossfade/examples/overview.tsx +3 -3
  78. package/src/components/data-table/COMPONENT.md +24 -10
  79. package/src/components/data-table/examples/columns-visibility.tsx +167 -0
  80. package/src/components/digit-input/COMPONENT.md +15 -13
  81. package/src/components/digit-input/examples/examples.module.css +8 -0
  82. package/src/components/digit-input/examples/in-form.tsx +2 -5
  83. package/src/components/digit-input/examples/on-complete.tsx +7 -6
  84. package/src/components/digit-input/examples/sizes.tsx +1 -1
  85. package/src/components/divider/COMPONENT.md +1 -1
  86. package/src/components/dnd/COMPONENT.md +3 -3
  87. package/src/components/dnd/examples/examples.module.css +4 -4
  88. package/src/components/drawer/COMPONENT.md +17 -9
  89. package/src/components/drawer/examples/date-sheet.tsx +41 -0
  90. package/src/components/drawer/examples/examples.module.css +5 -0
  91. package/src/components/drawer/examples/placement.tsx +4 -2
  92. package/src/components/dropdown/COMPONENT.md +20 -6
  93. package/src/components/dropdown/examples/checkbox-items.tsx +48 -0
  94. package/src/components/dropdown/examples/examples.module.css +1 -1
  95. package/src/components/empty-page/COMPONENT.md +1 -1
  96. package/src/components/empty-page/examples/examples.module.css +1 -1
  97. package/src/components/example-frame/COMPONENT.md +1 -1
  98. package/src/components/input/COMPONENT.md +10 -1
  99. package/src/components/input/examples/password-strength.tsx +12 -0
  100. package/src/components/kanban/COMPONENT.md +181 -0
  101. package/src/components/kanban/examples/controlled.tsx +78 -0
  102. package/src/components/kanban/examples/examples.module.css +31 -0
  103. package/src/components/kanban/examples/limits.tsx +58 -0
  104. package/src/components/kanban/examples/narrow.tsx +76 -0
  105. package/src/components/kanban/examples/overview.tsx +130 -0
  106. package/src/components/kanban/examples/states.tsx +75 -0
  107. package/src/components/kanban/examples/structure.tsx +76 -0
  108. package/src/components/login-form/COMPONENT.md +5 -5
  109. package/src/components/login-form/examples/register.tsx +2 -2
  110. package/src/components/login-form/examples/reset-password.tsx +2 -2
  111. package/src/components/modal/COMPONENT.md +11 -8
  112. package/src/components/modal/examples/examples.module.css +2 -2
  113. package/src/components/notification/COMPONENT.md +2 -2
  114. package/src/components/page-content/COMPONENT.md +1 -1
  115. package/src/components/page-content/examples/examples.module.css +1 -1
  116. package/src/components/page-toolbar/COMPONENT.md +142 -0
  117. package/src/components/page-toolbar/examples/examples.module.css +16 -0
  118. package/src/components/page-toolbar/examples/narrow.tsx +103 -0
  119. package/src/components/page-toolbar/examples/overview.tsx +113 -0
  120. package/src/components/page-toolbar/examples/save-changes.tsx +40 -0
  121. package/src/components/page-toolbar/examples/without-search.tsx +60 -0
  122. package/src/components/popover/COMPONENT.md +5 -4
  123. package/src/components/progress-bar/COMPONENT.md +6 -2
  124. package/src/components/progress-bar/examples/examples.module.css +7 -0
  125. package/src/components/progress-bar/examples/milestone.tsx +43 -0
  126. package/src/components/progress-bar/examples/steps.tsx +35 -0
  127. package/src/components/progress-circle/COMPONENT.md +1 -1
  128. package/src/components/scroll-container/COMPONENT.md +1 -1
  129. package/src/components/segmented-control/COMPONENT.md +8 -8
  130. package/src/components/segmented-control/examples/full-width.tsx +1 -1
  131. package/src/components/select/COMPONENT.md +2 -1
  132. package/src/components/skeleton/COMPONENT.md +1 -1
  133. package/src/components/skeleton/examples/with-crossfade.tsx +3 -3
  134. package/src/components/smart-filter/COMPONENT.md +2 -0
  135. package/src/components/sparkline/COMPONENT.md +98 -0
  136. package/src/components/sparkline/examples/examples.module.css +13 -0
  137. package/src/components/sparkline/examples/overview.tsx +27 -0
  138. package/src/components/sparkline/examples/period.tsx +46 -0
  139. package/src/components/spinner/COMPONENT.md +1 -1
  140. package/src/components/tabs/COMPONENT.md +24 -22
  141. package/src/components/tabs/examples/closable.tsx +1 -1
  142. package/src/components/tabs/examples/examples.module.css +14 -0
  143. package/src/components/tabs/examples/orientation.tsx +87 -33
  144. package/src/components/tooltip/COMPONENT.md +8 -6
  145. package/src/components/tooltip/examples/examples.module.css +2 -2
  146. package/src/layout/app-header/COMPONENT.md +178 -0
  147. package/src/layout/app-header/examples/breadcrumbs.tsx +36 -0
  148. package/src/layout/app-header/examples/examples.module.css +47 -0
  149. package/src/layout/app-header/examples/in-app-shell.tsx +70 -0
  150. package/src/layout/app-header/examples/narrow.tsx +29 -0
  151. package/src/layout/app-header/examples/overview.tsx +36 -0
  152. package/src/layout/app-shell/COMPONENT.md +35 -29
  153. package/src/layout/app-shell/examples/examples.module.css +17 -2
  154. package/src/layout/app-shell/examples/footer.tsx +59 -0
  155. package/src/layout/app-shell/examples/overview.tsx +19 -8
  156. package/src/layout/bottom-nav/COMPONENT.md +153 -0
  157. package/src/layout/bottom-nav/examples/examples.module.css +39 -0
  158. package/src/layout/bottom-nav/examples/floating.tsx +89 -0
  159. package/src/layout/bottom-nav/examples/icon-only.tsx +41 -0
  160. package/src/layout/bottom-nav/examples/in-app-shell.tsx +64 -0
  161. package/src/layout/bottom-nav/examples/overview.tsx +44 -0
  162. package/src/layout/bottom-nav/examples/router.tsx +31 -0
  163. package/src/layout/bottom-nav/examples/structure.tsx +47 -0
  164. package/src/layout/sidebar/COMPONENT.md +14 -13
  165. package/src/layout/sidebar/examples/examples.module.css +5 -5
  166. package/src/styles/globals.css +125 -2
  167. package/src/styles/theme-dark.css +39 -13
  168. package/src/styles/theme-light.css +84 -16
  169. package/src/styles/tokens.css +2 -0
@@ -0,0 +1,70 @@
1
+ /** The header in the app frame: one row with the Sidebar brand across the two planes, sticky over main — `AppShell`, `Sidebar.Brand`. */
2
+ import { AppHeader, AppShell, Icon, PageContent, Sidebar, Typography } from "prime-ui-kit";
3
+
4
+ import styles from "./examples.module.css";
5
+
6
+ const ORDERS = [
7
+ "№ 4821 · ООО «Север» · 84 000 ₽",
8
+ "№ 4820 · ИП Котов · 12 500 ₽",
9
+ "№ 4819 · ООО «Вектор» · 230 000 ₽",
10
+ "№ 4818 · АО «Меридиан» · 47 900 ₽",
11
+ "№ 4817 · ООО «Полюс» · 9 800 ₽",
12
+ "№ 4816 · ИП Белова · 156 000 ₽",
13
+ ];
14
+
15
+ export default function AppHeaderInAppShellExample() {
16
+ return (
17
+ <div className={styles.stage}>
18
+ <AppShell.Root fillViewport className={styles.shell}>
19
+ <AppShell.Nav>
20
+ <Sidebar.Root offCanvas="never">
21
+ <Sidebar.Header>
22
+ <Sidebar.Brand href="#home" description="Отдел продаж">
23
+ <Sidebar.BrandLogo>
24
+ <span className={styles.logo}>
25
+ <Icon name="nav.layoutGrid" />
26
+ </span>
27
+ </Sidebar.BrandLogo>
28
+ Acme CRM
29
+ </Sidebar.Brand>
30
+ </Sidebar.Header>
31
+ <Sidebar.Content>
32
+ <Sidebar.Item>
33
+ <Sidebar.ItemIcon>
34
+ <Icon name="nav.home" />
35
+ </Sidebar.ItemIcon>
36
+ Обзор
37
+ </Sidebar.Item>
38
+ <Sidebar.Item current>
39
+ <Sidebar.ItemIcon>
40
+ <Icon name="object.package" />
41
+ </Sidebar.ItemIcon>
42
+ Заказы
43
+ </Sidebar.Item>
44
+ </Sidebar.Content>
45
+ </Sidebar.Root>
46
+ </AppShell.Nav>
47
+ <AppHeader.Root>
48
+ <AppHeader.Start>
49
+ <AppHeader.Title>
50
+ Заказы
51
+ <AppHeader.Description>128 в работе</AppHeader.Description>
52
+ </AppHeader.Title>
53
+ </AppHeader.Start>
54
+ <AppHeader.Search>Поиск</AppHeader.Search>
55
+ </AppHeader.Root>
56
+ <AppShell.Main>
57
+ <PageContent.Section>
58
+ <PageContent.Body>
59
+ {ORDERS.map((order) => (
60
+ <Typography key={order} variant="body-m">
61
+ {order}
62
+ </Typography>
63
+ ))}
64
+ </PageContent.Body>
65
+ </PageContent.Section>
66
+ </AppShell.Main>
67
+ </AppShell.Root>
68
+ </div>
69
+ );
70
+ }
@@ -0,0 +1,29 @@
1
+ /** On a phone: the menu button opens the navigation, the description goes and the search folds into an icon — `AppHeader.MenuButton`, `show`. */
2
+ import { AppHeader, Button, Icon } from "prime-ui-kit";
3
+
4
+ import styles from "./examples.module.css";
5
+
6
+ export default function AppHeaderNarrowExample() {
7
+ return (
8
+ <div className={`${styles.window} ${styles.phone}`}>
9
+ <AppHeader.Root>
10
+ <AppHeader.Start>
11
+ {/* `always`: this frame is phone-wide on any screen; in an app keep the default `narrow`. */}
12
+ <AppHeader.MenuButton show="always" aria-expanded={false} />
13
+ <AppHeader.Title>
14
+ Заказы
15
+ <AppHeader.Description>128 в работе</AppHeader.Description>
16
+ </AppHeader.Title>
17
+ </AppHeader.Start>
18
+ <AppHeader.Search>Поиск заказов</AppHeader.Search>
19
+ <AppHeader.Actions>
20
+ <Button.Root variant="soft" tone="neutral" aria-label="Новый заказ">
21
+ <Button.Icon>
22
+ <Icon name="action.add" />
23
+ </Button.Icon>
24
+ </Button.Root>
25
+ </AppHeader.Actions>
26
+ </AppHeader.Root>
27
+ </div>
28
+ );
29
+ }
@@ -0,0 +1,36 @@
1
+ /** The top bar of a screen: where you are, global search, notifications and the primary action — `AppHeader.Title`, `AppHeader.Search`, `AppHeader.Actions`. */
2
+ import { AppHeader, Button, Icon } from "prime-ui-kit";
3
+
4
+ import styles from "./examples.module.css";
5
+
6
+ export default function AppHeaderOverviewExample() {
7
+ return (
8
+ <div className={styles.window}>
9
+ <AppHeader.Root>
10
+ <AppHeader.Start>
11
+ <AppHeader.Title>
12
+ <AppHeader.Icon>
13
+ <Icon name="nav.dashboard" />
14
+ </AppHeader.Icon>
15
+ Обзор
16
+ <AppHeader.Description>Отдел продаж · октябрь</AppHeader.Description>
17
+ </AppHeader.Title>
18
+ </AppHeader.Start>
19
+ <AppHeader.Search>Поиск заказов и клиентов</AppHeader.Search>
20
+ <AppHeader.Actions>
21
+ <Button.Root variant="soft" tone="neutral" aria-label="Уведомления">
22
+ <Button.Icon>
23
+ <Icon name="object.bell" />
24
+ </Button.Icon>
25
+ </Button.Root>
26
+ <Button.Root>
27
+ <Button.Icon>
28
+ <Icon name="action.add" />
29
+ </Button.Icon>
30
+ Новый заказ
31
+ </Button.Root>
32
+ </AppHeader.Actions>
33
+ </AppHeader.Root>
34
+ </div>
35
+ );
36
+ }
@@ -1,16 +1,17 @@
1
1
  # AppShell
2
2
 
3
- **Category:** layout
3
+ **Category:** page
4
4
  **Kind:** layout
5
5
 
6
- > The app frame: a navigation rail on the canvas and a content panel on the surface.
6
+ > The app frame: a content panel that is the page itself and a navigation rail one layer above it.
7
7
 
8
8
  ## When to use
9
- - The root layout of an application: navigation column, optional sticky header, scrolling main column.
10
- - Any page that needs the kit's responsive gutters and the surface context for cards and fields.
9
+ - The root layout of an application: navigation column, an optional AppHeader, scrolling main column, a bottom bar for phone navigation.
10
+ - Any page that needs the kit's responsive gutters and the surface ladder for cards and fields.
11
11
 
12
12
  ## When not to use
13
- - The navigation itself → use [Sidebar](../sidebar/COMPONENT.md) inside `AppShell.Nav`.
13
+ - The navigation itself → use [Sidebar](../sidebar/COMPONENT.md) inside `AppShell.Nav`, [BottomNav](../bottom-nav/COMPONENT.md) inside `AppShell.Footer`.
14
+ - The top bar (title, search, actions, the menu button) → use [AppHeader](../app-header/COMPONENT.md) as a child of Root.
14
15
  - Page heading, actions and sections inside main → use [PageContent](../../components/page-content/COMPONENT.md).
15
16
  - A temporary side panel over the page → use [Drawer](../../components/drawer/COMPONENT.md).
16
17
  - A scroll region inside a page → use [ScrollContainer](../../components/scroll-container/COMPONENT.md).
@@ -22,11 +23,12 @@ import { AppShell } from "prime-ui-kit";
22
23
 
23
24
  ## Anatomy
24
25
  ```
25
- AppShell.Root grid: nav column | content panel (bg-surface)
26
- ├─ AppShell.Nav navigation column on the canvas (usually Sidebar.Root)
27
- ├─ AppShell.Header sticky top bar of the panel
28
- └─ AppShell.Main <main>, scrolls, carries the gutters
29
- AppShell.Template Root + Nav + Header + Main in one component
26
+ AppShell.Root grid: nav column | content panel (the page, layer 0)
27
+ ├─ AppShell.Nav navigation column (usually Sidebar.Root, a surface one layer above the page)
28
+ ├─ AppHeader.Root sticky top bar of the panel (a separate component)
29
+ ├─ AppShell.Main <main>, scrolls, carries the gutters
30
+ └─ AppShell.Footer sticky bottom bar of the panel (usually BottomNav.Root)
31
+ AppShell.Template Root + Nav + the header + Main + Footer in one component
30
32
  ```
31
33
  Every child of Root that is not `AppShell.Nav` is placed into the content panel.
32
34
 
@@ -35,12 +37,12 @@ Every child of Root that is not `AppShell.Nav` is placed into the content panel.
35
37
  <!-- Generated from api.ts by `bun run docs:build`. Edit api.ts, not this section. -->
36
38
 
37
39
  ### AppShell.Root
38
- `ref` → `HTMLDivElement`. Grid of the nav column (canvas) and the content panel (surface); every child that is not `AppShell.Nav` goes into the panel.
40
+ `ref` → `HTMLDivElement`. Grid of the nav column (Sidebar, a surface one layer above the page) and the content panel (the page itself); every child that is not `AppShell.Nav` goes into the panel.
39
41
 
40
42
  | Prop | Type | Default | Description |
41
43
  |---|---|---|---|
42
44
  | `fillViewport` | `boolean` | `false` | The shell is exactly the viewport high and only `AppShell.Main` scrolls; otherwise the document scrolls and the nav is sticky. |
43
- | `…rest` | `HTMLAttributes<HTMLDivElement>` | — | `children` (Nav, Header, Main), `className` and the other div attributes. |
45
+ | `…rest` | `HTMLAttributes<HTMLDivElement>` | — | `children` (Nav, `AppHeader.Root`, Main, Footer), `className` and the other div attributes. |
44
46
 
45
47
  ### AppShell.Nav
46
48
  `ref` → `HTMLDivElement`. The navigation column slot (not a landmark: Sidebar renders the `<nav>`).
@@ -49,28 +51,29 @@ Every child of Root that is not `AppShell.Nav` is placed into the content panel.
49
51
  |---|---|---|---|
50
52
  | `…rest` | `HTMLAttributes<HTMLDivElement>` | — | `children` (usually `Sidebar.Root`), `className` and the other div attributes. |
51
53
 
52
- ### AppShell.Header
53
- `ref` → `HTMLElement`. Sticky `<header>` row of the panel for breadcrumbs, search and actions.
54
+ ### AppShell.Main
55
+ `ref` → `HTMLElement`. The `<main>` with the canonical gutters: a vertical `ScrollContainer`.
54
56
 
55
57
  | Prop | Type | Default | Description |
56
58
  |---|---|---|---|
59
+ | `contentWidth` | `"contained" \| "full"` | `"full"` | `full` — the whole panel with gutters; `contained` — a centred column up to `--prime-layout-content-max-width`. |
57
60
  | `…rest` | `HTMLAttributes<HTMLElement>` | — | `children`, `className` and the other attributes. |
58
61
 
59
- ### AppShell.Main
60
- `ref` → `HTMLElement`. The `<main>` with the canonical gutters: a vertical `ScrollContainer`.
62
+ ### AppShell.Footer
63
+ `ref` → `HTMLDivElement`. Sticky bottom bar of the panel for `BottomNav`; no padding of its own. A container (`prime-shell-footer`): BottomNav inside shows only while the panel is narrower than 640px.
61
64
 
62
65
  | Prop | Type | Default | Description |
63
66
  |---|---|---|---|
64
- | `contentWidth` | `"contained" \| "full"` | `"full"` | `full` — the whole panel with gutters; `contained` — a centred column up to `--prime-layout-content-max-width`. |
65
- | `…rest` | `HTMLAttributes<HTMLElement>` | — | `children`, `className` and the other attributes. |
67
+ | `…rest` | `HTMLAttributes<HTMLDivElement>` | — | `children` (usually `BottomNav.Root`), `className` and the other div attributes. |
66
68
 
67
69
  ### AppShell.Template
68
- `ref` → the `<main>`. Root + Nav + Header + Main in one; main scrolls to the top when `scrollResetKey` changes.
70
+ `ref` → the `<main>`. Root + Nav + the header + Main + Footer in one; main scrolls to the top when `scrollResetKey` changes.
69
71
 
70
72
  | Prop | Type | Default | Description |
71
73
  |---|---|---|---|
72
74
  | `nav` | `ReactNode` | — | Navigation column content; without it the panel takes the full width. |
73
- | `header` | `ReactNode` | — | Header content; no header row when omitted. |
75
+ | `header` | `ReactNode` | — | The top bar of the panel, an `AppHeader.Root`; no header when omitted. |
76
+ | `footer` | `ReactNode` | — | Footer content (`BottomNav`); no footer when omitted. |
74
77
  | `mainProps` | `Omit<AppShellMainProps, "children">` | — | Props for Main (e.g. `contentWidth`). |
75
78
  | `scrollResetKey` | `unknown` | — | Main scrolls back to the top whenever this value changes; pass the router pathname. |
76
79
  | `children` | `ReactNode` | — | Page content inside Main. |
@@ -93,9 +96,10 @@ Every child of Root that is not `AppShell.Nav` is placed into the content panel.
93
96
  ### Structure
94
97
  | Value | Looks like | Use when | Default |
95
98
  |---|---|---|---|
96
- | with `AppShell.Nav` | Two columns: canvas rail + surface panel | Apps with navigation | |
99
+ | with `AppShell.Nav` | Two columns: a rail one layer above the page + the panel that is the page | Apps with navigation | |
97
100
  | without Nav | Panel takes the full width | Single-page tools, auth screens | |
98
- | with `AppShell.Header` | Sticky bar at the top of the panel | Breadcrumbs, page actions, mobile menu button | |
101
+ | with `AppHeader.Root` | Sticky bar at the top of the panel | Title or breadcrumbs, global search, actions, the mobile menu button | |
102
+ | with `AppShell.Footer` | Sticky bar at the bottom of the panel, shown while it holds a visible bar | BottomNav on a phone | |
99
103
 
100
104
  `AppShell.Template` with `nav={<Sidebar.Root …/>}` and `PageContent` inside is the standard app; `contentWidth="contained"` + `PageContent.Root maxWidth="readable"` for docs and articles.
101
105
 
@@ -104,12 +108,13 @@ Every child of Root that is not `AppShell.Nav` is placed into the content panel.
104
108
  |---|---|---|
105
109
  | fixed frame | `fillViewport` | `data-fill-viewport="true"` on Root |
106
110
  | content width | `contentWidth` | `data-content-width` on Main |
111
+ | footer bar | panel width (container `prime-shell-footer` on Footer) | BottomNav inside leaves from 640px of panel width; the empty footer takes no room |
107
112
 
108
113
  ## Layout & spacing
109
- - Two full-height planes edge to edge: no inset, radius, shadow or border — the boundary is the fill change (canvas → surface).
110
- - Gutters in Main and Header: `--prime-layout-gutter-s` (16) → `-m` (24) from 640px → `-l` (32) from 1024px. Main top padding `--prime-space-6` (24) → `--prime-space-10` (40) from 1024px; bottom `--prime-space-16`.
111
- - Header: min height = control m + 2 × `--prime-space-3`, padding-block `--prime-space-3`, `z-index: --prime-z-sticky`.
112
- - The panel is a surface context: fields use the surface field fill, Cards inside become sunken tiles without shadow.
114
+ - Two full-height planes edge to edge: no inset, radius, shadow or border — the boundary is the fill change (the Sidebar rail is layer 1, the panel the page: white rail beside a gray page in light, a rail one step lighter than the near-black page in dark).
115
+ - Gutters in Main: `--prime-layout-gutter-s` (16) → `-m` (24) from 640px → `-l` (32) from 1024px. Main top padding `--prime-space-6` (24) → `--prime-space-10` (40) from 1024px; bottom `--prime-space-16`.
116
+ - Footer: no padding of its own (BottomNav keeps the bottom safe-area inset), sticky at the bottom, `z-index: --prime-z-sticky`.
117
+ - The panel is the page (layer 0 of the surface ladder, gray in light): fields on it take the page fill, a Card inside is layer 1 — white in light with the raised whisper — and cards in cards go deeper.
113
118
 
114
119
  ## Accessibility
115
120
 
@@ -117,7 +122,7 @@ Every child of Root that is not `AppShell.Nav` is placed into the content panel.
117
122
  No keyboard interaction.
118
123
 
119
124
  ### ARIA
120
- - Main is the `<main>` landmark and Header is `<header>`; the navigation landmark comes from Sidebar (`<nav>`).
125
+ - Main is the `<main>` landmark; the banner comes from AppHeader (`<header>`), the navigation landmarks from Sidebar and BottomNav (`<nav>`).
121
126
  - Give the page one `<h1>` (PageContent.Title).
122
127
 
123
128
  ### Labels
@@ -128,8 +133,9 @@ No `labels`.
128
133
  ## Examples
129
134
  | Example | Shows |
130
135
  |---|---|
131
- | [overview.tsx](examples/overview.tsx) | The app frame: Sidebar in the nav column, breadcrumbs in the sticky header, the page in main; only main scrolls — `fillViewport`. |
136
+ | [overview.tsx](examples/overview.tsx) | The app frame: Sidebar in the nav column, breadcrumbs in the AppHeader, the page in main; only main scrolls — `fillViewport`. |
132
137
  | [contained.tsx](examples/contained.tsx) | A shell without navigation whose main column is centred and capped for long reads — `contentWidth`. |
138
+ | [footer.tsx](examples/footer.tsx) | A phone-wide app: the bar in the footer stays at the bottom while main scrolls under it, and leaves once the panel is 640px wide — `AppShell.Footer`. |
133
139
  | [template.tsx](examples/template.tsx) | Root, nav, header and main in one component; main scrolls back to the top whenever the page changes — `AppShell.Template` with `scrollResetKey`. |
134
140
 
135
141
  ## Mistakes
@@ -141,4 +147,4 @@ No `labels`.
141
147
 
142
148
  ## Related
143
149
  - **Built from:** [ScrollContainer](../../components/scroll-container/COMPONENT.md)
144
- - **See also:** [Sidebar](../sidebar/COMPONENT.md), [PageContent](../../components/page-content/COMPONENT.md), [Breadcrumb](../../components/breadcrumb/COMPONENT.md)
150
+ - **See also:** [AppHeader](../app-header/COMPONENT.md), [Sidebar](../sidebar/COMPONENT.md), [BottomNav](../bottom-nav/COMPONENT.md), [PageContent](../../components/page-content/COMPONENT.md), [Breadcrumb](../../components/breadcrumb/COMPONENT.md)
@@ -7,8 +7,8 @@
7
7
  height: calc(var(--prime-space-24) * 5);
8
8
  overflow: hidden;
9
9
  border-radius: var(--prime-card-radius);
10
- background: var(--prime-color-bg-canvas);
11
- /* The rail shares the preview's canvas fill: a hairline keeps the window edge visible. */
10
+ background: var(--prime-color-layer-current);
11
+ /* The window is a page on the preview's page: a hairline keeps its edge visible. */
12
12
  box-shadow:
13
13
  0 0 0 var(--prime-border-width) var(--prime-color-border-subtle),
14
14
  var(--prime-shadow-raised);
@@ -27,3 +27,18 @@
27
27
  .shell {
28
28
  height: 100%;
29
29
  }
30
+
31
+ /* footer.tsx: the same window, phone-wide, so the bar in the footer stays (it leaves from 640px). */
32
+ .phone {
33
+ box-sizing: border-box;
34
+ display: flex;
35
+ width: min(100%, var(--prime-drawer-width-s));
36
+ height: calc(var(--prime-space-24) * 5);
37
+ margin-inline: auto;
38
+ overflow: hidden;
39
+ border-radius: var(--prime-card-radius);
40
+ background: var(--prime-color-layer-current);
41
+ box-shadow:
42
+ 0 0 0 var(--prime-border-width) var(--prime-color-border-subtle),
43
+ var(--prime-shadow-raised);
44
+ }
@@ -0,0 +1,59 @@
1
+ /** A phone-wide app: the bar in the footer stays at the bottom while main scrolls under it, and leaves once the panel is 640px wide — `AppShell.Footer`. */
2
+ import { AppHeader, AppShell, BottomNav, Icon, PageContent, Typography } from "prime-ui-kit";
3
+
4
+ import styles from "./examples.module.css";
5
+
6
+ const SECTIONS = [
7
+ { label: "Главная", icon: "nav.home" },
8
+ { label: "Счета", icon: "object.receipt" },
9
+ { label: "Клиенты", icon: "object.users" },
10
+ { label: "Ещё", icon: "action.more" },
11
+ ] as const;
12
+
13
+ const INVOICES = [
14
+ "Счёт № 1207 · 84 000 ₽",
15
+ "Счёт № 1206 · 12 500 ₽",
16
+ "Счёт № 1205 · 230 000 ₽",
17
+ "Счёт № 1204 · 47 900 ₽",
18
+ "Счёт № 1203 · 9 800 ₽",
19
+ "Счёт № 1202 · 156 000 ₽",
20
+ "Счёт № 1201 · 31 200 ₽",
21
+ "Счёт № 1200 · 64 700 ₽",
22
+ ];
23
+
24
+ export default function AppShellFooterExample() {
25
+ return (
26
+ <div className={styles.phone}>
27
+ <AppShell.Root fillViewport className={styles.shell}>
28
+ <AppHeader.Root>
29
+ <AppHeader.Start>
30
+ <AppHeader.Title>Счета</AppHeader.Title>
31
+ </AppHeader.Start>
32
+ </AppHeader.Root>
33
+ <AppShell.Main>
34
+ <PageContent.Section>
35
+ <PageContent.Body>
36
+ {INVOICES.map((invoice) => (
37
+ <Typography key={invoice} variant="body-m">
38
+ {invoice}
39
+ </Typography>
40
+ ))}
41
+ </PageContent.Body>
42
+ </PageContent.Section>
43
+ </AppShell.Main>
44
+ <AppShell.Footer>
45
+ <BottomNav.Root>
46
+ {SECTIONS.map((item, index) => (
47
+ <BottomNav.Item key={item.label} current={index === 1}>
48
+ <BottomNav.ItemIcon>
49
+ <Icon name={item.icon} />
50
+ </BottomNav.ItemIcon>
51
+ {item.label}
52
+ </BottomNav.Item>
53
+ ))}
54
+ </BottomNav.Root>
55
+ </AppShell.Footer>
56
+ </AppShell.Root>
57
+ </div>
58
+ );
59
+ }
@@ -1,5 +1,14 @@
1
- /** The app frame: Sidebar in the nav column, breadcrumbs in the sticky header, the page in main; only main scrolls — `fillViewport`. */
2
- import { AppShell, Breadcrumb, Button, Icon, PageContent, Sidebar, Typography } from "prime-ui-kit";
1
+ /** The app frame: Sidebar in the nav column, breadcrumbs in the AppHeader, the page in main; only main scrolls — `fillViewport`. */
2
+ import {
3
+ AppHeader,
4
+ AppShell,
5
+ Breadcrumb,
6
+ Button,
7
+ Icon,
8
+ PageContent,
9
+ Sidebar,
10
+ Typography,
11
+ } from "prime-ui-kit";
3
12
 
4
13
  import styles from "./examples.module.css";
5
14
 
@@ -37,12 +46,14 @@ export default function AppShellOverviewExample() {
37
46
  </Sidebar.Content>
38
47
  </Sidebar.Root>
39
48
  </AppShell.Nav>
40
- <AppShell.Header>
41
- <Breadcrumb.Root>
42
- <Breadcrumb.Item href="#crm">CRM</Breadcrumb.Item>
43
- <Breadcrumb.Item current>Обзор</Breadcrumb.Item>
44
- </Breadcrumb.Root>
45
- </AppShell.Header>
49
+ <AppHeader.Root>
50
+ <AppHeader.Start>
51
+ <Breadcrumb.Root>
52
+ <Breadcrumb.Item href="#crm">CRM</Breadcrumb.Item>
53
+ <Breadcrumb.Item current>Обзор</Breadcrumb.Item>
54
+ </Breadcrumb.Root>
55
+ </AppHeader.Start>
56
+ </AppHeader.Root>
46
57
  <AppShell.Main>
47
58
  <PageContent.Section>
48
59
  <PageContent.Header>
@@ -0,0 +1,153 @@
1
+ # BottomNav
2
+
3
+ **Category:** navigation
4
+ **Kind:** layout
5
+
6
+ > Phone navigation: a bar of 3–5 main sections at the bottom of the screen.
7
+
8
+ ## When to use
9
+ - The main sections of an app on a phone, one tap away (Главная, Заказы, Клиенты, Ещё).
10
+ - Next to Sidebar in one app: Sidebar navigates on a wide screen, BottomNav on a phone (inside `AppShell.Footer` it leaves by itself once the panel is 640px wide).
11
+
12
+ ## When not to use
13
+ - More than five destinations or nested sections → use [Sidebar](../sidebar/COMPONENT.md) (off-canvas on a phone), put the rest under «Ещё».
14
+ - Switching views inside one page → use [Tabs](../../components/tabs/COMPONENT.md) or [SegmentedControl](../../components/segmented-control/COMPONENT.md).
15
+ - Actions (create, filter) → use [Button](../../components/button/COMPONENT.md); the bar only navigates.
16
+
17
+ ## Import
18
+ ```tsx
19
+ import { BottomNav } from "prime-ui-kit";
20
+ ```
21
+
22
+ ## Anatomy
23
+ ```
24
+ BottomNav.Root <nav> bar, equal columns, safe-area inset below
25
+ └─ BottomNav.Item <button>, <a href> or the asChild element
26
+ ├─ BottomNav.ItemIcon the glyph above the label (aria-hidden)
27
+ ├─ BottomNav.ItemCount Badge on the icon corner
28
+ └─ label one short word
29
+ ```
30
+
31
+ ## API
32
+
33
+ <!-- Generated from api.ts by `bun run docs:build`. Edit api.ts, not this section. -->
34
+
35
+ ### BottomNav.Root
36
+ `ref` → `HTMLElement`. The `<nav>` bar: equal columns, surface fill with a faint top divider, the bottom safe-area inset below the items. Inside `AppShell.Footer` it shows only while the panel is narrower than 640px.
37
+
38
+ | Prop | Type | Default | Description |
39
+ |---|---|---|---|
40
+ | `iconOnly` | `boolean` | `false` | Icons without visible labels; the labels stay in the DOM as visually hidden text and name the items. |
41
+ | `floating` | `boolean` | `false` | A Liquid Glass capsule over the content (the iOS 26 tab bar): a clear `--prime-color-bg-glass` tint over a light backdrop blur, a specular top edge, a glass selection capsule under the current item, inset `--prime-bottom-nav-floating-inset` from the edges and above the home indicator. Positions itself at the bottom of its positioned container (`AppShell.Footer`, which then takes no height), so the page scrolls under it; with `iconOnly` the capsule hugs its square items, centred. Opaque under `prefers-reduced-transparency`. |
42
+ | `labels` | `Partial<BottomNavLabels>` | — | Built-in strings, see Labels. |
43
+ | `…rest` | `HTMLAttributes<HTMLElement>` | — | `children` (3–5 `BottomNav.Item`), `aria-label` (replaces `labels.nav`), `className` and the other attributes. |
44
+
45
+ ### BottomNav.Item
46
+ `ref` → the rendered element. `<button type="button">`, `<a>` with `href`, or the single child with `asChild`: the icon (20) above a short label (10).
47
+
48
+ | Prop | Type | Default | Description |
49
+ |---|---|---|---|
50
+ | `current` | `boolean` | `false` | Current section: `aria-current="page"`; the icon and the label turn primary (the others are muted). |
51
+ | `disabled` | `boolean` | `false` | Not interactive: `disabled` / `aria-disabled`, `data-disabled`; a link loses its `href`. |
52
+ | `href` | `string` | — | Renders an `<a>` (with `target`, `rel`). |
53
+ | `asChild` | `boolean` | `false` | Renders the single child (e.g. a router `NavLink`) as the item; its children are the label and parts. |
54
+ | `children` | `ReactNode` | — | `BottomNav.ItemIcon`, an optional `BottomNav.ItemCount` and a short label (one word). |
55
+ | `…rest` | `ButtonHTMLAttributes<HTMLButtonElement>` | — | `onClick`, `aria-*`, `className` and the other attributes. |
56
+
57
+ ### BottomNav.ItemIcon
58
+ `ref` → `HTMLSpanElement`. The item glyph (`aria-hidden`, 20) above the label.
59
+
60
+ | Prop | Type | Default | Description |
61
+ |---|---|---|---|
62
+ | `children` | `ReactNode` | — (required) | The icon, e.g. `<Icon name="nav.home" />`. |
63
+ | `…rest` | `Omit<HTMLAttributes<HTMLSpanElement>, "children">` | — | `className` and the other span attributes. |
64
+
65
+ ### BottomNav.ItemCount
66
+ `ref` → `HTMLSpanElement` (the Badge). An `xs` Badge on the icon's top-end corner; read after the label («Заказы 12»).
67
+
68
+ | Prop | Type | Default | Description |
69
+ |---|---|---|---|
70
+ | `children` | `ReactNode` | — (required) | The number (or a short status such as «99+»). |
71
+ | `color` | `PaletteColor` | `"red"` | Badge hue: a count on a section asks for attention. |
72
+ | `variant` | `"solid" \| "soft" \| "outline"` | `"solid"` | Badge treatment. |
73
+ | `…rest` | `Omit<HTMLAttributes<HTMLSpanElement>, "children" \| "color">` | — | `className` and the other span attributes. |
74
+
75
+ ## Variants
76
+
77
+ Two item states only — current (primary color) and not (muted); no hover, no fill.
78
+
79
+ ### Flags (Root)
80
+ | Flag | Looks like | Use when | Default |
81
+ |---|---|---|---|
82
+ | `floating` | a glass capsule over the content: translucent fill, backdrop blur, bright rim, inset from the edges | the app's look wants depth over the page, the iOS tab-bar feel | |
83
+ | `iconOnly` | icons alone; labels visually hidden, still the items' names | 3–4 sections with unmistakable icons | |
84
+
85
+ Without flags the bar is flat at the screen edge on the surface, with a faint top divider.
86
+
87
+ ### Flags (Item)
88
+ | Flag | Part | Effect |
89
+ |---|---|---|
90
+ | `current` | Item | `aria-current="page"`, icon and label in the primary color |
91
+ | `disabled` | Item | not interactive, disabled text color |
92
+ | `asChild` | Item | the child element (router link) is the item |
93
+
94
+ ## States
95
+ | State | Driven by | DOM |
96
+ |---|---|---|
97
+ | current | `current` or a router link setting `aria-current` | `aria-current="page"` on the item |
98
+ | focus | keyboard | focus ring inside the item edge |
99
+ | disabled | `disabled` | `disabled` / `aria-disabled`, `data-disabled` |
100
+ | hidden | inside `AppShell.Footer` 640px wide or more | `display: none` (container `prime-shell-footer`) |
101
+ | floating / icon only | `floating` / `iconOnly` | `data-floating="true"` / `data-icon-only="true"` on the `<nav>` |
102
+ | less transparency | `prefers-reduced-transparency: reduce` | a floating capsule turns opaque (`--prime-color-layer-floating-bg`), no blur |
103
+
104
+ Motion: the color of the icon and the label changes over `fast`; in the floating capsule the selection capsule grows in from 92% and fades in (`fast`, enter easing), instant under reduced motion. No hover or press feedback: an item is either current or not.
105
+
106
+ ## Layout & spacing
107
+ - Bar `--prime-bottom-nav-height` (52) plus `env(safe-area-inset-bottom)`; side padding `--prime-bottom-nav-padding-x`. With `iconOnly` the bar is `--prime-bottom-nav-icon-only-height` (48).
108
+ - Items share the width equally; the icon (`--prime-bottom-nav-icon-size`, 20 — a light glyph, so the bar stays quiet next to the page) sits in a `--prime-bottom-nav-indicator-width` × `--prime-bottom-nav-indicator-height` box (28 × 20) that anchors the count; the label is `--prime-bottom-nav-label-size` (10, medium, line 12 — the iOS tab-bar size) 4 below it and truncates.
109
+ - Floating (the iOS 26 tab bar): items `--prime-bottom-nav-floating-item-size` (44) high inset `--prime-bottom-nav-floating-padding` (4) inside a full-radius capsule (52 in all); with `iconOnly` the items are 44 squares and the capsule hugs them. `--prime-bottom-nav-floating-inset` (12) from the sides and above `env(safe-area-inset-bottom)`. Liquid Glass: a clear `--prime-color-bg-glass` tint over a light `blur(--prime-bottom-nav-blur)` (8) with `saturate(180%)`, so the page reads through; a specular top edge in `--prime-color-bg-glass-edge`, a hairline of ink around it and the overlay shadow. The current item sits on a glass selection capsule (ink at 8% with its own top highlight). Inside `AppShell.Footer` main gets extra bottom padding so the last line clears the capsule.
110
+ - Surface fill with a faint top divider; inside `AppShell.Footer` the bar sticks to the bottom of the panel.
111
+
112
+ ## Accessibility
113
+
114
+ ### Keyboard
115
+ | Key | Action |
116
+ |---|---|
117
+ | `Tab` · `Shift+Tab` | Moves focus through the sections; a disabled one is skipped. |
118
+ | `Enter` | Opens the section (a link or a button). |
119
+
120
+ ### ARIA
121
+ - A `<nav>` landmark named by `labels.nav` («Основные разделы») or its own `aria-label`.
122
+ - The current section has `aria-current="page"`; visually it is the primary-color item among muted ones (a contrast step, not a hue).
123
+ - The icon is hidden from assistive tech; the item is named by its label, the count is read after it («Заказы 12»).
124
+ - A disabled item is a `disabled` button or an `aria-disabled` link without `href`.
125
+
126
+ ### Labels
127
+ <!-- Generated from api.ts by `bun run docs:build`. Edit api.ts, not this section. -->
128
+
129
+ | Key | Default | Used for |
130
+ |---|---|---|
131
+ | `nav` | `"Основные разделы"` | `aria-label` of the `<nav>` landmark. |
132
+
133
+ ## Examples
134
+ | Example | Shows |
135
+ |---|---|
136
+ | [overview.tsx](examples/overview.tsx) | Four sections of the app, the current one in the primary color and a tap moves it — `current`, `BottomNav.ItemIcon`. |
137
+ | [structure.tsx](examples/structure.tsx) | Counts on the icons and a section that is not available yet — `BottomNav.ItemCount`, `disabled`. |
138
+ | [icon-only.tsx](examples/icon-only.tsx) | Icons alone: the labels are hidden but still name the sections for screen readers — `iconOnly`. |
139
+ | [floating.tsx](examples/floating.tsx) | A glass capsule over the page: the client list scrolls under its blur, with labels or icons alone — `floating`, `iconOnly`. |
140
+ | [router.tsx](examples/router.tsx) | A router link as the item: the router sets `aria-current` and the item shows as current; render it inside a router — `asChild`. |
141
+ | [in-app-shell.tsx](examples/in-app-shell.tsx) | The app frame on a phone: the AppHeader, the page and the bar in the footer, which leaves once the panel is 640px wide — `AppShell.Footer`. |
142
+
143
+ ## Mistakes
144
+ - Six or more items → keep 3–5; the rest goes to a «Ещё» section or Sidebar.
145
+ - Long labels («Управление заказами») → one word; the label truncates.
146
+ - `iconOnly` with icons that need a caption to be understood → keep the labels.
147
+ - A floating bar outside a positioned container → it sits at the bottom of the nearest positioned ancestor; put it in `AppShell.Footer`.
148
+ - An action item («Создать») in the bar → the bar navigates; put the action in the page header.
149
+ - Hiding the bar with your own media query inside `AppShell.Footer` → the footer already hides it from 640px of panel width.
150
+
151
+ ## Related
152
+ - **Built from:** Badge, VisuallyHidden (internal)
153
+ - **See also:** [AppShell](../app-shell/COMPONENT.md), [Sidebar](../sidebar/COMPONENT.md), [Tabs](../../components/tabs/COMPONENT.md)
@@ -0,0 +1,39 @@
1
+ /* Layout of the BottomNav scenarios: a phone-wide screen frame. Semantic tokens only. */
2
+
3
+ /* A phone screen: the content above, the bar pinned to the bottom edge. */
4
+ .phone {
5
+ box-sizing: border-box;
6
+ display: flex;
7
+ flex-direction: column;
8
+ width: min(100%, var(--prime-drawer-width-s));
9
+ height: calc(var(--prime-space-24) * 5);
10
+ margin-inline: auto;
11
+ overflow: hidden;
12
+ border-radius: var(--prime-card-radius);
13
+ background: var(--prime-color-layer-current);
14
+ box-shadow:
15
+ 0 0 0 var(--prime-border-width) var(--prime-color-border-subtle),
16
+ var(--prime-shadow-raised);
17
+ }
18
+
19
+ /* The page above the bar. */
20
+ .screen {
21
+ display: flex;
22
+ flex: 1 1 auto;
23
+ flex-direction: column;
24
+ gap: var(--prime-space-2);
25
+ min-height: 0;
26
+ padding: var(--prime-space-6) var(--prime-layout-gutter-s);
27
+ }
28
+
29
+ /* in-app-shell.tsx: the shell fills the phone frame instead of the viewport. */
30
+ .shell {
31
+ height: 100%;
32
+ }
33
+
34
+ /* floating.tsx: a client row — avatar and two lines of text. */
35
+ .client {
36
+ display: flex;
37
+ align-items: center;
38
+ gap: var(--prime-space-3);
39
+ }