laif-ds 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CHANGELOG.md +25 -0
- package/dist/_virtual/index3.js +5 -5
- package/dist/_virtual/index4.js +5 -5
- package/dist/_virtual/index5.js +4 -4
- package/dist/agent-docs/adoption-report.json +26 -24
- package/dist/agent-docs/components/AppBottomNav.md +180 -49
- package/dist/agent-docs/components/AppSidebar.md +504 -11
- package/dist/agent-docs/components/Sidebar.md +72 -1
- package/dist/agent-docs/components-list.md +3 -2
- package/dist/agent-docs/manifest.json +52 -15
- package/dist/components/design-tokens.js +0 -64
- package/dist/components/nav-tokens.js +41 -0
- package/dist/components/ui/app-bottom-nav.js +125 -130
- package/dist/components/ui/app-sidebar-mobile.js +119 -104
- package/dist/components/ui/app-sidebar.js +516 -313
- package/dist/components/ui/nav-badge.js +66 -0
- package/dist/components/ui/sidebar.js +272 -207
- package/dist/index.d.ts +122 -29
- package/dist/node_modules/eventemitter3/index.js +1 -1
- package/dist/node_modules/hast-util-to-jsx-runtime/lib/index.js +1 -1
- package/dist/node_modules/recharts/es6/util/Events.js +1 -1
- package/dist/node_modules/unified/lib/index.js +1 -1
- package/dist/styles.v3.css +1 -1
- package/package.json +1 -1
|
@@ -16,7 +16,7 @@ Low-level primitives to build responsive sidebars with desktop/offcanvas modes,
|
|
|
16
16
|
- **SidebarHeader / SidebarFooter**: Top/bottom containers.
|
|
17
17
|
- **SidebarContent**: Scrollable body.
|
|
18
18
|
- **SidebarGroup / SidebarGroupLabel / SidebarGroupAction / SidebarGroupContent**: Labeled groups with optional action.
|
|
19
|
-
- **SidebarMenu / SidebarMenuItem / SidebarMenuButton / SidebarMenuAction / SidebarMenuBadge**: Menu building blocks.
|
|
19
|
+
- **SidebarMenu / SidebarMenuItem / SidebarMenuButton / SidebarMenuAction / SidebarMenuBadge**: Menu building blocks. `SidebarMenuBadge` is **deprecated** — see below.
|
|
20
20
|
- **SidebarMenuSub / SidebarMenuSubItem / SidebarMenuSubButton**: Nested menus.
|
|
21
21
|
|
|
22
22
|
---
|
|
@@ -30,8 +30,78 @@ Low-level primitives to build responsive sidebars with desktop/offcanvas modes,
|
|
|
30
30
|
toggle an `openMobile` no sheet is listening to, leaving the navigation
|
|
31
31
|
unreachable. So on mobile `collapsible` has no effect at all.
|
|
32
32
|
- **Collapsible**: `icon` mode shows only icons and tooltips; `offcanvas` moves panel off-screen.
|
|
33
|
+
- **Icon-mode geometry** (`SidebarMenuButton` in the 3rem rail): a 32px pill
|
|
34
|
+
(`!size-8`, `mx-auto`) with **6px** of padding (`!p-1.5`) around a **20px**
|
|
35
|
+
glyph (`[&>svg]:size-5`) — 16px in expanded rows, where the button's own
|
|
36
|
+
`[&>svg]:size-4` applies. The three numbers come from
|
|
37
|
+
`navTokens.rail.button` (`src/components/nav-tokens.ts`) and are one decision: `32 - 2*6 = 20` makes the
|
|
38
|
+
content box **exactly** the glyph, which is what keeps every glyph on the
|
|
39
|
+
panel's centre line. The label stays mounted as a `flex-1` sibling (it is the
|
|
40
|
+
row's accessible name), so any slack in that box is claimed by the label and
|
|
41
|
+
pushes the glyph off-centre by half of it. Give the glyph a different size and
|
|
42
|
+
the padding has to follow.
|
|
33
43
|
- **Keyboard**: Toggle with ⌘/Ctrl + B.
|
|
34
44
|
- **Tooltips**: Menu buttons show tooltips only when collapsed on desktop.
|
|
45
|
+
- **Peek (overlay expansion)**: see below.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## `SidebarMenuBadge` is deprecated
|
|
50
|
+
|
|
51
|
+
Use `AppSidebar` with **`NavItem.badge` / `NavItem.badgeLabel`** instead (see
|
|
52
|
+
`AppSidebar.md` → "Badges"). The primitive still works and is still exported,
|
|
53
|
+
unchanged, for a consumer hand-building a `Sidebar` out of these parts — but it
|
|
54
|
+
is the wrong tool for a navigation row:
|
|
55
|
+
|
|
56
|
+
- it is `group-data-[collapsible=icon]:hidden`, so whatever it carries simply
|
|
57
|
+
**disappears** in the 3rem rail, where `NavItem.badge` degrades to a dot and
|
|
58
|
+
moves the value into the tooltip;
|
|
59
|
+
- it has **no spoken form** — `NavItem.badgeLabel` rides an `sr-only` span
|
|
60
|
+
inside the row while the visual mark is `aria-hidden`;
|
|
61
|
+
- it is positioned absolutely over the row rather than laid out in it, so it
|
|
62
|
+
cannot share the row with a chevron or an action;
|
|
63
|
+
- it renders on one surface, where `NavItem.badge` renders on all four a
|
|
64
|
+
`NavItem` feeds, the mobile panel and `AppBottomNav` included.
|
|
65
|
+
|
|
66
|
+
Removing it would be a breaking change on a published package, so it stays until
|
|
67
|
+
a major.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Peek — `useSidebar().peek` / `setPeek`
|
|
72
|
+
|
|
73
|
+
A second, temporary way for the sidebar to be expanded, used by `AppSidebar`'s
|
|
74
|
+
`expandOnHover`. The primitive provides the mechanism only: the hover, focus and
|
|
75
|
+
pin logic lives in `AppSidebar`, which calls `setPeek` and nothing else.
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
const { state, open, setOpen, peek, setPeek } = useSidebar();
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
- `peek: boolean` — temporary expansion driven by hover or focus.
|
|
82
|
+
- `setPeek(peek: boolean)` — the only thing the owner of the logic writes.
|
|
83
|
+
|
|
84
|
+
Three things follow from it, and each exists for a reason:
|
|
85
|
+
|
|
86
|
+
- **`state` is `open || peek ? "expanded" : "collapsed"`.** Widening the panel
|
|
87
|
+
alone would not work: the children read `state` and the
|
|
88
|
+
`group-data-[collapsible=icon]:*` rules, not the panel's width, so labels,
|
|
89
|
+
inline submenus and the tooltip suppression on `SidebarMenuButton` all hang off
|
|
90
|
+
this. Note that `data-collapsible` empties as soon as `state` is `"expanded"`,
|
|
91
|
+
peek included.
|
|
92
|
+
- **`peek` never goes through `setOpen`,** so it never writes the
|
|
93
|
+
`sidebar_state` cookie. Otherwise every pass of the mouse would overwrite the
|
|
94
|
+
consumer's persisted preference with noise.
|
|
95
|
+
- **The wrapper gets `data-peek="true"`,** and that is what keeps the peek an
|
|
96
|
+
_overlay_ rather than a push: the gap element and `SidebarInset` cancel the
|
|
97
|
+
effect of `data-state` on `data-peek="true"` and stay at icon width (per
|
|
98
|
+
variant — `floating` / `inset` keep their
|
|
99
|
+
`calc(var(--sidebar-width-icon) + 1rem)`), while the `fixed` panel takes the
|
|
100
|
+
raised z-index and the shadow. Any consumer styling that keys off
|
|
101
|
+
`data-state="collapsed"` for layout has to be re-anchored the same way.
|
|
102
|
+
|
|
103
|
+
`peek` is forced to `false` below 768px, where the panel is a Sheet and there is
|
|
104
|
+
no hover to speak of.
|
|
35
105
|
|
|
36
106
|
---
|
|
37
107
|
|
|
@@ -66,6 +136,7 @@ export function Shell() {
|
|
|
66
136
|
<SidebarMenuButton isActive>
|
|
67
137
|
<span>Dashboard</span>
|
|
68
138
|
</SidebarMenuButton>
|
|
139
|
+
{/* Deprecated — prefer `AppSidebar` + `NavItem.badge`. */}
|
|
69
140
|
<SidebarMenuBadge>3</SidebarMenuBadge>
|
|
70
141
|
</SidebarMenuItem>
|
|
71
142
|
</SidebarMenu>
|
|
@@ -46,9 +46,9 @@ This document provides a complete mapping of all components available in the lai
|
|
|
46
46
|
### Layout & Structure Components
|
|
47
47
|
|
|
48
48
|
- **Accordion** - Collapsible content panels for organizing information hierarchically
|
|
49
|
-
- **AppBottomNav** - Mobile bottom navigation bar (`md:hidden`, in the normal flow as the last child of `SidebarInset`): six equal cells — up to 5 primary destinations plus a Menu cell that opens `AppSidebar`'s mobile panel, optionally already drilled into a level, or up to 6 destinations with `showMenu={false}` when the app opens that panel from its own header
|
|
49
|
+
- **AppBottomNav** - Mobile bottom navigation bar (`md:hidden`, in the normal flow as the last child of `SidebarInset`): six equal cells — up to 5 primary destinations plus a Menu cell that opens `AppSidebar`'s mobile panel, optionally already drilled into a level, or up to 6 destinations with `showMenu={false}` when the app opens that panel from its own header, with an optional badge per cell (`badge`: a count as a superscript on the glyph, `99+` past 99, short text degrading to a dot) and `menuBadge` for a dot on the Menu cell, OR-ed with the badged items dropped past the cell budget
|
|
50
50
|
- **AppCard** - Enhanced card component with variant styles, semantic states, size control, and loading skeleton support
|
|
51
|
-
- **AppSidebar** - Application sidebar navigation component, with groups rendered as a stacked list or as a tab bar (`navigationDisplay="tab"`, one group at a time, only the selected tab labelled and animated open to fit it, `navigationFooter` always visible), optional icon-only collapse (`collapsible="icon"`: tooltips on top-level items, dropdown for sub-items) and, below 768px, a Settings-style mobile menu (grouped cards, 44px rows, drill-down on sub-items)
|
|
51
|
+
- **AppSidebar** - Application sidebar navigation component, with groups rendered as a stacked list or as a tab bar (`navigationDisplay="tab"`, one group at a time, only the selected tab labelled and animated open to fit it, `navigationFooter` always visible), optional icon-only collapse (`collapsible="icon"`: tooltips on top-level items, dropdown for sub-items), optional hover expansion over that (`expandOnHover`: the pointer or the focus expands the panel as an overlay that does not move the page, a Pin in the header makes it permanent, `pinned`/`defaultPinned`/`onPinnedChange` alias the primitive's `open`) and, below 768px, a Settings-style mobile menu (grouped cards, 44px rows, drill-down on sub-items), plus an optional badge on every row and group (`badge`: `number | string | true` with `badgeLabel` for the spoken form, degrading per surface — pill when expanded, dot in the icon rail with the value in the tooltip, superscript in `AppBottomNav` — and never aggregating counts)
|
|
52
52
|
- **AppStepper** - Step-by-step progress indicator for multi-step processes
|
|
53
53
|
- **AspectRatio** - Container that maintains a specific aspect ratio for responsive layouts
|
|
54
54
|
- **Breadcrumb** - Breadcrumb navigation component for hierarchical navigation
|
|
@@ -152,6 +152,7 @@ The following components are marked as deprecated and should not be used in new
|
|
|
152
152
|
- **MultipleSelector** - ~~Use `AppSelect` instead~~
|
|
153
153
|
- **AppMultipleSelectDropdown** - ~~Use `AppSelect` instead~~
|
|
154
154
|
- **ResizePrompt** - ~~Use alternative resize handling methods~~
|
|
155
|
+
- **SidebarMenuBadge** - ~~Use `NavItem.badge` instead~~ (on `AppSidebar`, with `badgeLabel` for the spoken form; the primitive stays exported and functional, but it is hidden in icon mode and has no spoken form — see `Sidebar.md`)
|
|
155
156
|
|
|
156
157
|
---
|
|
157
158
|
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "1.1.0",
|
|
3
|
-
"generatedAt": "2026-09-
|
|
3
|
+
"generatedAt": "2026-09-18T09:11:12.439Z",
|
|
4
4
|
"package": {
|
|
5
5
|
"name": "laif-ds",
|
|
6
|
-
"version": "1.0.
|
|
6
|
+
"version": "1.0.2"
|
|
7
7
|
},
|
|
8
8
|
"stats": {
|
|
9
9
|
"documentedComponentCount": 94,
|
|
10
|
-
"catalogedComponentCount":
|
|
11
|
-
"missingFromDocsCount":
|
|
10
|
+
"catalogedComponentCount": 94,
|
|
11
|
+
"missingFromDocsCount": 2,
|
|
12
12
|
"deprecatedComponentCount": 2,
|
|
13
13
|
"averageAiReadinessScore": 56.17,
|
|
14
14
|
"highAiReadinessCount": 38,
|
|
15
15
|
"lowAiReadinessCount": 41
|
|
16
16
|
},
|
|
17
17
|
"missingFromDocs": [
|
|
18
|
+
"SidebarMenuBadge",
|
|
18
19
|
"TruncatedCell"
|
|
19
20
|
],
|
|
20
21
|
"components": [
|
|
@@ -286,8 +287,9 @@
|
|
|
286
287
|
"exampleTitles": [
|
|
287
288
|
"`BottomNavItem`",
|
|
288
289
|
"Layout",
|
|
289
|
-
"Icon size
|
|
290
|
+
"Icon size",
|
|
290
291
|
"Active state",
|
|
292
|
+
"Badges",
|
|
291
293
|
"Opening the menu",
|
|
292
294
|
"Accessibility",
|
|
293
295
|
"Motion",
|
|
@@ -295,6 +297,7 @@
|
|
|
295
297
|
"Edge cases",
|
|
296
298
|
"App shell",
|
|
297
299
|
"Localised copy",
|
|
300
|
+
"A Menu cell of the app's own",
|
|
298
301
|
"Without the Menu cell"
|
|
299
302
|
],
|
|
300
303
|
"controlledPattern": "likely-controlled",
|
|
@@ -307,10 +310,10 @@
|
|
|
307
310
|
},
|
|
308
311
|
"metadata": {
|
|
309
312
|
"props": {
|
|
310
|
-
"totalProps":
|
|
313
|
+
"totalProps": 19,
|
|
311
314
|
"requiredPropsCount": 4,
|
|
312
|
-
"typedPropsCount":
|
|
313
|
-
"describedPropsCount":
|
|
315
|
+
"typedPropsCount": 19,
|
|
316
|
+
"describedPropsCount": 18
|
|
314
317
|
},
|
|
315
318
|
"accessibility": {
|
|
316
319
|
"hasCoverage": true
|
|
@@ -1016,6 +1019,10 @@
|
|
|
1016
1019
|
"title": "Behavior",
|
|
1017
1020
|
"slug": "behavior"
|
|
1018
1021
|
},
|
|
1022
|
+
{
|
|
1023
|
+
"title": "Badges (`badge` / `badgeLabel`)",
|
|
1024
|
+
"slug": "badges-badge-badgelabel"
|
|
1025
|
+
},
|
|
1019
1026
|
{
|
|
1020
1027
|
"title": "Navigation display (`navigationDisplay`)",
|
|
1021
1028
|
"slug": "navigation-display-navigationdisplay"
|
|
@@ -1024,6 +1031,10 @@
|
|
|
1024
1031
|
"title": "Icon mode (`collapsible=\"icon\"`)",
|
|
1025
1032
|
"slug": "icon-mode-collapsible-icon"
|
|
1026
1033
|
},
|
|
1034
|
+
{
|
|
1035
|
+
"title": "Hover expansion (`expandOnHover`)",
|
|
1036
|
+
"slug": "hover-expansion-expandonhover"
|
|
1037
|
+
},
|
|
1027
1038
|
{
|
|
1028
1039
|
"title": "Mobile menu (`<768px`)",
|
|
1029
1040
|
"slug": "mobile-menu-768px"
|
|
@@ -1041,8 +1052,24 @@
|
|
|
1041
1052
|
"hasExamples": true,
|
|
1042
1053
|
"exampleTitles": [
|
|
1043
1054
|
"Icons: `iconName` or `icon`",
|
|
1055
|
+
"The three forms",
|
|
1056
|
+
"Degradation per surface",
|
|
1057
|
+
"Counts never aggregate",
|
|
1058
|
+
"Colour and geometry",
|
|
1059
|
+
"Accessibility",
|
|
1060
|
+
"Live counts vs static ones, and who announces them",
|
|
1061
|
+
"What this deliberately does not do",
|
|
1044
1062
|
"The group selector (`navigationDisplay=\"tab\"` + `collapsible=\"icon\"`, collapsed)",
|
|
1063
|
+
"The peek is an overlay, the pin is a push",
|
|
1064
|
+
"Preconditions",
|
|
1065
|
+
"Timing",
|
|
1066
|
+
"The Pin",
|
|
1067
|
+
"`pinned` is `open`",
|
|
1068
|
+
"Keyboard and focus",
|
|
1069
|
+
"Menus that live in a portal",
|
|
1070
|
+
"Other things worth knowing",
|
|
1045
1071
|
"Basic",
|
|
1072
|
+
"With badges",
|
|
1046
1073
|
"With Header/Footer Content"
|
|
1047
1074
|
],
|
|
1048
1075
|
"controlledPattern": "controlled-uncontrolled-documented",
|
|
@@ -1052,10 +1079,10 @@
|
|
|
1052
1079
|
},
|
|
1053
1080
|
"metadata": {
|
|
1054
1081
|
"props": {
|
|
1055
|
-
"totalProps":
|
|
1082
|
+
"totalProps": 21,
|
|
1056
1083
|
"requiredPropsCount": 1,
|
|
1057
|
-
"typedPropsCount":
|
|
1058
|
-
"describedPropsCount":
|
|
1084
|
+
"typedPropsCount": 21,
|
|
1085
|
+
"describedPropsCount": 19
|
|
1059
1086
|
},
|
|
1060
1087
|
"accessibility": {
|
|
1061
1088
|
"hasCoverage": true
|
|
@@ -1064,10 +1091,11 @@
|
|
|
1064
1091
|
"coveredStates": [
|
|
1065
1092
|
"hover",
|
|
1066
1093
|
"focus",
|
|
1094
|
+
"disabled",
|
|
1067
1095
|
"empty",
|
|
1068
1096
|
"selected"
|
|
1069
1097
|
],
|
|
1070
|
-
"coveredStateCount":
|
|
1098
|
+
"coveredStateCount": 5
|
|
1071
1099
|
}
|
|
1072
1100
|
},
|
|
1073
1101
|
"aiReadiness": {
|
|
@@ -5080,6 +5108,14 @@
|
|
|
5080
5108
|
"title": "Behavior",
|
|
5081
5109
|
"slug": "behavior"
|
|
5082
5110
|
},
|
|
5111
|
+
{
|
|
5112
|
+
"title": "`SidebarMenuBadge` is deprecated",
|
|
5113
|
+
"slug": "sidebarmenubadge-is-deprecated"
|
|
5114
|
+
},
|
|
5115
|
+
{
|
|
5116
|
+
"title": "Peek — `useSidebar().peek` / `setPeek`",
|
|
5117
|
+
"slug": "peek-usesidebar-peek-setpeek"
|
|
5118
|
+
},
|
|
5083
5119
|
{
|
|
5084
5120
|
"title": "Example",
|
|
5085
5121
|
"slug": "example"
|
|
@@ -5092,7 +5128,7 @@
|
|
|
5092
5128
|
"hasPropsTable": false,
|
|
5093
5129
|
"hasExamples": false,
|
|
5094
5130
|
"exampleTitles": [],
|
|
5095
|
-
"controlledPattern": "
|
|
5131
|
+
"controlledPattern": "likely-controlled",
|
|
5096
5132
|
"requiredProps": []
|
|
5097
5133
|
},
|
|
5098
5134
|
"metadata": {
|
|
@@ -5107,9 +5143,10 @@
|
|
|
5107
5143
|
},
|
|
5108
5144
|
"states": {
|
|
5109
5145
|
"coveredStates": [
|
|
5146
|
+
"hover",
|
|
5110
5147
|
"focus"
|
|
5111
5148
|
],
|
|
5112
|
-
"coveredStateCount":
|
|
5149
|
+
"coveredStateCount": 2
|
|
5113
5150
|
}
|
|
5114
5151
|
},
|
|
5115
5152
|
"aiReadiness": {
|
|
@@ -6275,5 +6312,5 @@
|
|
|
6275
6312
|
]
|
|
6276
6313
|
}
|
|
6277
6314
|
],
|
|
6278
|
-
"checksum": "
|
|
6315
|
+
"checksum": "1b95270b522d7fdbd54fa834020be6d9bf1b0632fda8a9458556d024398a8574"
|
|
6279
6316
|
}
|
|
@@ -70,70 +70,6 @@ const e = {
|
|
|
70
70
|
badge: {
|
|
71
71
|
base: "inline-flex items-center justify-center border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 gap-1"
|
|
72
72
|
},
|
|
73
|
-
// Navigation rows (sidebar / menu lists) specific.
|
|
74
|
-
//
|
|
75
|
-
// Not `dropdownItem.selected`: that one paints the accent colour *on a 10%
|
|
76
|
-
// dilution of itself* — measured 1.85:1 in light, 3.28 tangerine, 4.23
|
|
77
|
-
// claymorphism, 6.62 dark. Fine as a chip inside a dropdown, unreadable as a
|
|
78
|
-
// row label. Here the tint stays on the surface and the label keeps
|
|
79
|
-
// full-contrast `text-d-foreground` (12.19 / 10.35 / 9.35 / 10.22).
|
|
80
|
-
//
|
|
81
|
-
// The bar is `d-foreground`, not `d-primary`, for the same reason: in the
|
|
82
|
-
// light theme `--d-primary` is a pale cyan that measures 1.85:1 against the
|
|
83
|
-
// tinted row, so a primary bar would fail WCAG 1.4.11 (>= 3:1 for a non-text
|
|
84
|
-
// indicator) in the default theme. Shape and position carry the state; the
|
|
85
|
-
// tint adds the brand colour on top where the theme can afford it.
|
|
86
|
-
//
|
|
87
|
-
// `base` goes on every row so the selected row's 2px does not shift content.
|
|
88
|
-
navItem: {
|
|
89
|
-
base: "border-l-2 border-l-transparent",
|
|
90
|
-
selected: "bg-d-primary/10 border-l-d-foreground text-d-foreground font-semibold",
|
|
91
|
-
// A bottom bar's cells are columns, not rows, so the left rule has no edge
|
|
92
|
-
// to run along and the state has to be carried by the cell's own surface: a
|
|
93
|
-
// pill, no border on any side.
|
|
94
|
-
//
|
|
95
|
-
// The pill is the sidebar row's own state, ported: `bg-d-sidebar-accent` +
|
|
96
|
-
// `text-d-sidebar-accent-foreground` + a bold label, which is exactly what
|
|
97
|
-
// `sidebarMenuButtonVariants` paints under `data-[active=true]`. One
|
|
98
|
-
// vocabulary for "you are here" across the two navigations, which is worth
|
|
99
|
-
// more than either surface being optimal on its own.
|
|
100
|
-
//
|
|
101
|
-
// What comes with it is the sidebar's contrast, and it is very low.
|
|
102
|
-
// `--d-sidebar-accent` is `--colors--surface--tertiary` at 40% alpha, so on
|
|
103
|
-
// the bar (`d-card`, #fafafa light / #1f1f1f dark) it composites to #efefef
|
|
104
|
-
// and #2f2f2f — **1.10:1 in light and 1.23:1 in dark**, far under the 3:1 of
|
|
105
|
-
// WCAG 1.4.11 for a non-text state indicator. That is not a regression
|
|
106
|
-
// introduced here: the same fill on the sidebar's own surface (#f5f5f5)
|
|
107
|
-
// measures 1.08:1, so the desktop sidebar has always marked the current row
|
|
108
|
-
// by *weight*, with the tint as a hint. The label is never at risk either
|
|
109
|
-
// way — `--d-sidebar-accent-foreground` is `--colors--text--body-primary`,
|
|
110
|
-
// the colour the inactive cells already use, 11.9:1 on the composited fill.
|
|
111
|
-
//
|
|
112
|
-
// `font-bold` and not `semibold`: with a fill this faint the weight is the
|
|
113
|
-
// state, and it is the sidebar's own value. It costs label width on a cell
|
|
114
|
-
// that already truncates at 375px — if that has to be bought back,
|
|
115
|
-
// `font-semibold` is the place to spend it, not the fill.
|
|
116
|
-
//
|
|
117
|
-
// Measured alternatives, for whenever the 3:1 floor has to be met.
|
|
118
|
-
// `bg-d-primary` + `text-d-primary-foreground`: 1.98:1 light / 7.99:1 dark —
|
|
119
|
-
// the brand fill this replaced, under the floor in light but separating by
|
|
120
|
-
// hue, which 1.4.11 does not measure. `bg-d-foreground` + `text-d-background`:
|
|
121
|
-
// 13.01:1 / 12.49:1 — conforming, but it reads as a selected chip rather
|
|
122
|
-
// than the current tab and leaves the bar with no brand. Not a tint of
|
|
123
|
-
// primary: /10 is 1.07:1 light, /40 still 1.30:1, solid `bg-d-accent` 1.26:1.
|
|
124
|
-
//
|
|
125
|
-
// `rounded-md` (6px, = `radius.default`) and not `radius.sm`: `sm` is 4px in
|
|
126
|
-
// the v4 build and 2px in the v3 one, and this would be the single place
|
|
127
|
-
// where the two CSS bundles disagree. Not `rounded-d-md` either — that maps
|
|
128
|
-
// onto `--d-radius` and becomes 18px under claymorphism, while the dock it
|
|
129
|
-
// sits inside is a fixed `rounded-t-lg`.
|
|
130
|
-
//
|
|
131
|
-
// `basePill` reserves nothing (there is no border to reserve), so activating
|
|
132
|
-
// a cell cannot shift its content; it is there so every cell shares the
|
|
133
|
-
// radius and only the fill changes.
|
|
134
|
-
basePill: "rounded-md",
|
|
135
|
-
selectedPill: "bg-d-sidebar-accent text-d-sidebar-accent-foreground font-bold"
|
|
136
|
-
},
|
|
137
73
|
// Dropdown item specific
|
|
138
74
|
dropdownItem: {
|
|
139
75
|
selected: "bg-d-primary/10 text-d-primary font-medium data-[selected=true]:text-d-primary data-[selected=true]:bg-d-primary/30 hover:text-d-primary hover:bg-d-primary/30",
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
const e = {
|
|
3
|
+
/**
|
|
4
|
+
* The state of a navigation item, in the two shapes the surfaces need: a left
|
|
5
|
+
* rule for a vertical list, a pill for a horizontal bar.
|
|
6
|
+
*/
|
|
7
|
+
item: {
|
|
8
|
+
// The bar is `d-foreground`, not `d-primary`: in the light theme
|
|
9
|
+
// `--d-primary` measures 1.85:1 against the tinted row and a primary bar
|
|
10
|
+
// would fail WCAG 1.4.11 (>= 3:1 for a non-text indicator).
|
|
11
|
+
// `base` goes on every row so the selected row's 2px does not shift content.
|
|
12
|
+
base: "border-l-2 border-l-transparent",
|
|
13
|
+
selected: "bg-d-primary/10 border-l-d-foreground text-d-foreground font-semibold",
|
|
14
|
+
// Duplicates what `sidebarMenuButtonVariants` paints under
|
|
15
|
+
// `data-[active=true]` in `sidebar.tsx` — two copies of "you are here", by
|
|
16
|
+
// decision. The fill is ~1.1:1, under WCAG 1.4.11's 3:1, so `font-bold`
|
|
17
|
+
// carries the state and the tint is only a hint. `rounded-md` and not
|
|
18
|
+
// `radius.sm` (4px in v4, 2px in v3) nor `rounded-d-md` (18px in clay).
|
|
19
|
+
basePill: "rounded-md",
|
|
20
|
+
selectedPill: "bg-d-sidebar-accent text-d-sidebar-accent-foreground font-bold"
|
|
21
|
+
},
|
|
22
|
+
/**
|
|
23
|
+
* The icon rail at 3rem, where the glyph carries the whole row. One home for
|
|
24
|
+
* the geometry because `sidebarMenuButtonVariants`, the two initial-letter
|
|
25
|
+
* fallbacks in `app-sidebar.tsx` and the group selector must agree on it.
|
|
26
|
+
*/
|
|
27
|
+
rail: {
|
|
28
|
+
// The content box is exactly the glyph (`32 - 2*6 = 20`), and that equality
|
|
29
|
+
// is the centring mechanism: the label stays mounted as a `flex-1` sibling,
|
|
30
|
+
// so any slack in the box is claimed by the label and pushes every glyph
|
|
31
|
+
// off-centre. Change one of the three numbers and the other two follow.
|
|
32
|
+
button: "group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-1.5 group-data-[collapsible=icon]:mx-auto group-data-[collapsible=icon]:[&>svg]:size-5",
|
|
33
|
+
// Sized to the rail's content box in both states, for the same reason: a
|
|
34
|
+
// 16px box in the rail's 20px content box leaves 4px for the label to claim
|
|
35
|
+
// and that row's letter sits left of every glyph above it.
|
|
36
|
+
fallbackGlyph: "size-4 group-data-[collapsible=icon]:size-5 group-data-[collapsible=icon]:text-sm"
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
export {
|
|
40
|
+
e as navTokens
|
|
41
|
+
};
|