@slotted/styles 0.0.0-stage → 0.1.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +10 -2
  3. package/package.json +276 -3
  4. package/src/accordion/accordion.css +96 -0
  5. package/src/accordion/accordion.tokens.json +14 -0
  6. package/src/action-bar/action-bar.css +455 -0
  7. package/src/action-bar/action-bar.tokens.json +50 -0
  8. package/src/action-sheet/action-sheet.css +462 -0
  9. package/src/action-sheet/action-sheet.tokens.json +59 -0
  10. package/src/alert/alert.css +764 -0
  11. package/src/alert/alert.tokens.json +77 -0
  12. package/src/app-shell/app-shell.css +1899 -0
  13. package/src/app-shell/app-shell.tokens.json +88 -0
  14. package/src/aspect-ratio/aspect-ratio.css +131 -0
  15. package/src/aspect-ratio/aspect-ratio.tokens.json +1 -0
  16. package/src/attachment/attachment.css +906 -0
  17. package/src/attachment/attachment.tokens.json +85 -0
  18. package/src/avatar/avatar.css +598 -0
  19. package/src/avatar/avatar.tokens.json +64 -0
  20. package/src/avatar-group/avatar-group.css +268 -0
  21. package/src/avatar-group/avatar-group.tokens.json +16 -0
  22. package/src/badge/badge.css +462 -0
  23. package/src/badge/badge.tokens.json +58 -0
  24. package/src/breadcrumb/breadcrumb.css +441 -0
  25. package/src/breadcrumb/breadcrumb.tokens.json +50 -0
  26. package/src/button/button.css +754 -0
  27. package/src/button/button.tokens.json +72 -0
  28. package/src/button-group/button-group.css +455 -0
  29. package/src/button-group/button-group.tokens.json +11 -0
  30. package/src/calendar/calendar.css +846 -0
  31. package/src/calendar/calendar.tokens.json +67 -0
  32. package/src/card/card.css +602 -0
  33. package/src/card/card.tokens.json +68 -0
  34. package/src/carousel/carousel.css +860 -0
  35. package/src/carousel/carousel.tokens.json +62 -0
  36. package/src/cascader/cascader.css +1320 -0
  37. package/src/cascader/cascader.tokens.json +103 -0
  38. package/src/chart/chart.css +1473 -0
  39. package/src/chart/chart.tokens.json +107 -0
  40. package/src/checkbox/checkbox.css +1011 -0
  41. package/src/checkbox/checkbox.tokens.json +102 -0
  42. package/src/checkbox-group/checkbox-group.css +70 -0
  43. package/src/checkbox-group/checkbox-group.tokens.json +6 -0
  44. package/src/code-block/code-block.css +555 -0
  45. package/src/code-block/code-block.tokens.json +69 -0
  46. package/src/collapsible/collapsible.css +546 -0
  47. package/src/collapsible/collapsible.tokens.json +55 -0
  48. package/src/color-picker/color-picker.css +607 -0
  49. package/src/color-picker/color-picker.tokens.json +58 -0
  50. package/src/column-manager/column-manager.css +452 -0
  51. package/src/column-manager/column-manager.tokens.json +43 -0
  52. package/src/combobox/combobox.css +1048 -0
  53. package/src/combobox/combobox.tokens.json +112 -0
  54. package/src/command-palette/command-palette.css +1000 -0
  55. package/src/command-palette/command-palette.tokens.json +75 -0
  56. package/src/container/container.css +198 -0
  57. package/src/container/container.tokens.json +18 -0
  58. package/src/context-menu/context-menu.css +104 -0
  59. package/src/context-menu/context-menu.tokens.json +11 -0
  60. package/src/currency-field/currency-field.css +787 -0
  61. package/src/currency-field/currency-field.tokens.json +80 -0
  62. package/src/dashboard-grid/dashboard-grid.css +748 -0
  63. package/src/dashboard-grid/dashboard-grid.tokens.json +67 -0
  64. package/src/data-grid/data-grid.css +2484 -0
  65. package/src/data-grid/data-grid.tokens.json +100 -0
  66. package/src/data-list/data-list.css +455 -0
  67. package/src/data-list/data-list.tokens.json +35 -0
  68. package/src/date-field/date-field.css +560 -0
  69. package/src/date-field/date-field.tokens.json +80 -0
  70. package/src/date-picker/date-picker.css +420 -0
  71. package/src/date-picker/date-picker.tokens.json +49 -0
  72. package/src/date-range-picker/date-range-picker.css +983 -0
  73. package/src/date-range-picker/date-range-picker.tokens.json +109 -0
  74. package/src/datetime-field/datetime-field.css +850 -0
  75. package/src/datetime-field/datetime-field.tokens.json +91 -0
  76. package/src/datetime-picker/datetime-picker.css +843 -0
  77. package/src/datetime-picker/datetime-picker.tokens.json +78 -0
  78. package/src/datetime-range-picker/datetime-range-picker.css +1375 -0
  79. package/src/datetime-range-picker/datetime-range-picker.tokens.json +135 -0
  80. package/src/description-list/description-list.css +675 -0
  81. package/src/description-list/description-list.tokens.json +66 -0
  82. package/src/detail-panel/detail-panel.css +329 -0
  83. package/src/detail-panel/detail-panel.tokens.json +33 -0
  84. package/src/dialog/dialog.css +968 -0
  85. package/src/dialog/dialog.tokens.json +66 -0
  86. package/src/divider/divider.css +131 -0
  87. package/src/divider/divider.tokens.json +18 -0
  88. package/src/drawer/drawer.css +1303 -0
  89. package/src/drawer/drawer.tokens.json +75 -0
  90. package/src/entity-picker/entity-picker.css +1367 -0
  91. package/src/entity-picker/entity-picker.tokens.json +118 -0
  92. package/src/event-calendar/event-calendar.css +2148 -0
  93. package/src/event-calendar/event-calendar.tokens.json +87 -0
  94. package/src/field/field.css +707 -0
  95. package/src/field/field.tokens.json +58 -0
  96. package/src/field-group/field-group.css +226 -0
  97. package/src/field-group/field-group.tokens.json +33 -0
  98. package/src/filter-bar/filter-bar.css +1306 -0
  99. package/src/filter-bar/filter-bar.tokens.json +78 -0
  100. package/src/form/form.css +218 -0
  101. package/src/form/form.tokens.json +32 -0
  102. package/src/fullscreen/fullscreen.css +376 -0
  103. package/src/fullscreen/fullscreen.tokens.json +50 -0
  104. package/src/grid/grid.css +173 -0
  105. package/src/grid/grid.tokens.json +20 -0
  106. package/src/highlight/highlight.css +135 -0
  107. package/src/highlight/highlight.tokens.json +16 -0
  108. package/src/hover-card/hover-card.css +93 -0
  109. package/src/hover-card/hover-card.tokens.json +17 -0
  110. package/src/icon/icon.css +136 -0
  111. package/src/icon/icon.tokens.json +14 -0
  112. package/src/image/image.css +449 -0
  113. package/src/image/image.tokens.json +27 -0
  114. package/src/import-export-center/import-export-center.css +1175 -0
  115. package/src/import-export-center/import-export-center.tokens.json +84 -0
  116. package/src/inline/inline.css +149 -0
  117. package/src/inline/inline.tokens.json +14 -0
  118. package/src/inline-message/inline-message.css +396 -0
  119. package/src/inline-message/inline-message.tokens.json +19 -0
  120. package/src/kbd/kbd.css +490 -0
  121. package/src/kbd/kbd.tokens.json +40 -0
  122. package/src/link/link.css +237 -0
  123. package/src/link/link.tokens.json +25 -0
  124. package/src/list/list.css +240 -0
  125. package/src/list/list.tokens.json +42 -0
  126. package/src/list-item/list-item.css +937 -0
  127. package/src/list-item/list-item.tokens.json +114 -0
  128. package/src/listbox/listbox.css +674 -0
  129. package/src/listbox/listbox.tokens.json +72 -0
  130. package/src/loading-overlay/loading-overlay.css +730 -0
  131. package/src/loading-overlay/loading-overlay.tokens.json +48 -0
  132. package/src/markdown/markdown.css +168 -0
  133. package/src/markdown/markdown.tokens.json +18 -0
  134. package/src/menu/menu.css +817 -0
  135. package/src/menu/menu.tokens.json +78 -0
  136. package/src/menubar/menubar.css +170 -0
  137. package/src/menubar/menubar.tokens.json +32 -0
  138. package/src/message/message.css +1453 -0
  139. package/src/message/message.tokens.json +85 -0
  140. package/src/message-scroller/message-scroller.css +371 -0
  141. package/src/message-scroller/message-scroller.tokens.json +47 -0
  142. package/src/multi-select/multi-select.css +1427 -0
  143. package/src/multi-select/multi-select.tokens.json +114 -0
  144. package/src/navigation-menu/navigation-menu.css +1029 -0
  145. package/src/navigation-menu/navigation-menu.tokens.json +70 -0
  146. package/src/notification-center/notification-center.css +856 -0
  147. package/src/notification-center/notification-center.tokens.json +84 -0
  148. package/src/number-field/number-field.css +794 -0
  149. package/src/number-field/number-field.tokens.json +80 -0
  150. package/src/otp-input/otp-input.css +525 -0
  151. package/src/otp-input/otp-input.tokens.json +69 -0
  152. package/src/page/page.css +179 -0
  153. package/src/page/page.tokens.json +20 -0
  154. package/src/page-header/page-header.css +1181 -0
  155. package/src/page-header/page-header.tokens.json +71 -0
  156. package/src/pagination/pagination.css +500 -0
  157. package/src/pagination/pagination.tokens.json +56 -0
  158. package/src/popover/popover.css +526 -0
  159. package/src/popover/popover.tokens.json +60 -0
  160. package/src/progress-indicator/progress-indicator.css +1584 -0
  161. package/src/progress-indicator/progress-indicator.tokens.json +50 -0
  162. package/src/prompt-composer/prompt-composer.css +680 -0
  163. package/src/prompt-composer/prompt-composer.tokens.json +99 -0
  164. package/src/prose/prose.css +733 -0
  165. package/src/prose/prose.tokens.json +79 -0
  166. package/src/radio-group/radio-group.css +778 -0
  167. package/src/radio-group/radio-group.tokens.json +103 -0
  168. package/src/rating/rating.css +1071 -0
  169. package/src/rating/rating.tokens.json +88 -0
  170. package/src/result-state/result-state.css +980 -0
  171. package/src/result-state/result-state.tokens.json +63 -0
  172. package/src/runtime-properties.mjs +321 -0
  173. package/src/saved-views/saved-views.css +394 -0
  174. package/src/saved-views/saved-views.tokens.json +41 -0
  175. package/src/scroll-area/scroll-area.css +481 -0
  176. package/src/scroll-area/scroll-area.tokens.json +15 -0
  177. package/src/section/section.css +177 -0
  178. package/src/section/section.tokens.json +39 -0
  179. package/src/segmented-control/segmented-control.css +564 -0
  180. package/src/segmented-control/segmented-control.tokens.json +67 -0
  181. package/src/select/select.css +1593 -0
  182. package/src/select/select.tokens.json +110 -0
  183. package/src/side-navigation/side-navigation.css +1096 -0
  184. package/src/side-navigation/side-navigation.tokens.json +87 -0
  185. package/src/skeleton/skeleton.css +431 -0
  186. package/src/skeleton/skeleton.tokens.json +29 -0
  187. package/src/slider/slider.css +792 -0
  188. package/src/slider/slider.tokens.json +75 -0
  189. package/src/sparkline/sparkline.css +282 -0
  190. package/src/sparkline/sparkline.tokens.json +45 -0
  191. package/src/spinner/spinner.css +224 -0
  192. package/src/spinner/spinner.tokens.json +8 -0
  193. package/src/split-button/split-button.css +253 -0
  194. package/src/split-button/split-button.tokens.json +12 -0
  195. package/src/splitter/splitter.css +336 -0
  196. package/src/splitter/splitter.tokens.json +24 -0
  197. package/src/stack/stack.css +128 -0
  198. package/src/stack/stack.tokens.json +14 -0
  199. package/src/stat-card/stat-card.css +641 -0
  200. package/src/stat-card/stat-card.tokens.json +95 -0
  201. package/src/status-indicator/status-indicator.css +481 -0
  202. package/src/status-indicator/status-indicator.tokens.json +38 -0
  203. package/src/stepper/stepper.css +629 -0
  204. package/src/stepper/stepper.tokens.json +72 -0
  205. package/src/switch/switch.css +703 -0
  206. package/src/switch/switch.tokens.json +75 -0
  207. package/src/table/table.css +1482 -0
  208. package/src/table/table.tokens.json +69 -0
  209. package/src/table-of-contents/table-of-contents.css +533 -0
  210. package/src/table-of-contents/table-of-contents.tokens.json +53 -0
  211. package/src/tabs/tabs.css +998 -0
  212. package/src/tabs/tabs.tokens.json +82 -0
  213. package/src/tag/tag.css +768 -0
  214. package/src/tag/tag.tokens.json +85 -0
  215. package/src/tags-input/tags-input.css +757 -0
  216. package/src/tags-input/tags-input.tokens.json +84 -0
  217. package/src/text-field/text-field.css +900 -0
  218. package/src/text-field/text-field.tokens.json +87 -0
  219. package/src/textarea/textarea.css +365 -0
  220. package/src/textarea/textarea.tokens.json +59 -0
  221. package/src/time-field/time-field.css +719 -0
  222. package/src/time-field/time-field.tokens.json +86 -0
  223. package/src/time-picker/time-picker.css +665 -0
  224. package/src/time-picker/time-picker.tokens.json +66 -0
  225. package/src/time-range-picker/time-range-picker.css +1287 -0
  226. package/src/time-range-picker/time-range-picker.tokens.json +134 -0
  227. package/src/timeline/timeline.css +1596 -0
  228. package/src/timeline/timeline.tokens.json +96 -0
  229. package/src/toast/toast.css +943 -0
  230. package/src/toast/toast.tokens.json +90 -0
  231. package/src/toggle/toggle.css +290 -0
  232. package/src/toggle/toggle.tokens.json +54 -0
  233. package/src/toggle-group/toggle-group.css +463 -0
  234. package/src/toggle-group/toggle-group.tokens.json +54 -0
  235. package/src/toolbar/toolbar.css +434 -0
  236. package/src/toolbar/toolbar.tokens.json +49 -0
  237. package/src/tooltip/tooltip.css +368 -0
  238. package/src/tooltip/tooltip.tokens.json +39 -0
  239. package/src/top-navigation/top-navigation.css +1287 -0
  240. package/src/top-navigation/top-navigation.tokens.json +91 -0
  241. package/src/tour/tour.css +680 -0
  242. package/src/tour/tour.tokens.json +67 -0
  243. package/src/transfer/transfer.css +571 -0
  244. package/src/transfer/transfer.tokens.json +49 -0
  245. package/src/tree/tree.css +1000 -0
  246. package/src/tree/tree.tokens.json +80 -0
  247. package/src/typography/typography.css +211 -0
  248. package/src/typography/typography.tokens.json +31 -0
  249. package/src/upload/upload.css +1706 -0
  250. package/src/upload/upload.tokens.json +123 -0
  251. package/src/visually-hidden/visually-hidden.css +52 -0
  252. package/src/visually-hidden/visually-hidden.tokens.json +1 -0
  253. package/src/wizard/wizard.css +740 -0
  254. package/src/wizard/wizard.tokens.json +63 -0
@@ -0,0 +1,1096 @@
1
+ @layer slotted.components {
2
+ /**
3
+ * A column of destinations, grouped and nested, that reads the room it is
4
+ * given: a labelled list above ninety-six pixels and a rail of marks below.
5
+ *
6
+ * root ▸ (section ▸ section-label · list) | list
7
+ * list ▸ item ▸ link | trigger | link + trigger · actions · group ▸ item…
8
+ * ▸ divider · skeleton
9
+ * link | trigger ▸ icon · label · description · end · indicator
10
+ *
11
+ * The item is a grid of two rows: the row the reader presses on the first
12
+ * and the group on the second. The group's track transitions between `0fr`
13
+ * and `1fr`, so it opens at its own height without being measured — the
14
+ * technique `collapsible` uses for its panel.
15
+ *
16
+ * ── The rail is a width ─────────────────────────────────────────────────
17
+ *
18
+ * `app-shell` made its `nav` region a query container and said the family
19
+ * inside it answers the rail without being told: the root is a container of
20
+ * its own, and below `compactAt` (96, declared in the contract and compared
21
+ * by `side-navigation.styles.test.mjs`) every part but the mark leaves the
22
+ * eye. Nothing leaves the DOM: the label is clipped rather than removed, so
23
+ * the link keeps its name and the tooltip the script adds repeats it.
24
+ *
25
+ * ── Geometry is the shell's ─────────────────────────────────────────────
26
+ *
27
+ * The six row properties `app-shell` publishes are read first and the
28
+ * control scale second, so a row here stands on the same left edge as the
29
+ * shell's toggle and its account control. Outside the shell the fallbacks
30
+ * are the same numbers the shell composes its own from.
31
+ */
32
+ [data-ui='side-navigation'] {
33
+ --_row-height: var(--slotted-app-shell-row-height, var(--slotted-control-height-md, 34px));
34
+ /** The mark's box is the row's height: a square the mark is centred in. */
35
+ --_mark: var(--slotted-app-shell-row-mark, var(--_row-height));
36
+ --_inset: var(--slotted-app-shell-row-inset, var(--slotted-space-4, 0.5rem));
37
+ /**
38
+ * How far a row's own box stands off the column's inline edges. A row is a
39
+ * chip with a radius, and a radius drawn against the edge of the region it
40
+ * sits in reads as a corner cut away: under the shell's flush plane the
41
+ * region meets the window, and the chip met it too — measured at zero
42
+ * pixels from the frame, against twelve on the inset plane, where the
43
+ * region's own margin had been hiding the same fact.
44
+ *
45
+ * It is taken out of the row's inset rather than added to it, so the mark
46
+ * stays on the left edge the shell's own controls stand on. `min` is for a
47
+ * product that sets the inset to nothing: the standoff never exceeds what
48
+ * there is to give.
49
+ */
50
+ --_standoff: min(var(--slotted-space-2, 0.25rem), var(--_inset));
51
+ --_gap: var(--slotted-app-shell-row-gap, var(--slotted-control-gap-md, 8px));
52
+ --_font-size: var(--slotted-app-shell-row-font-size, var(--slotted-font-size-sm, 0.8125rem));
53
+ --_line-height: var(--slotted-app-shell-row-line-height, var(--slotted-line-height-sm, 1.45));
54
+ --_icon-size: var(--slotted-control-icon-size-md, 16px);
55
+ --_description-font-size: var(--slotted-font-size-xs, 0.75rem);
56
+ --_section-font-size: var(--slotted-font-size-xs, 0.75rem);
57
+ /**
58
+ * A child's label stands under its parent's label: the indent is the
59
+ * mark's box and the gap after it, which is where the parent's words begin.
60
+ */
61
+ --_indent: var(--slotted-side-navigation-indent, calc(var(--_mark) + var(--_gap)));
62
+ /**
63
+ * A row without a mark has no box for its children to stand under, so the
64
+ * levels below it step by a smaller, constant distance: Ant's inline menu
65
+ * indents 24 pixels a level, shadcn's submenu 24. Five levels of the
66
+ * first indent would have left a fifth-level label fifty pixels wide.
67
+ */
68
+ --_indent-step: var(--slotted-side-navigation-indent-step, var(--slotted-space-7, 1.25rem));
69
+ --_radius: var(--slotted-side-navigation-radius, var(--slotted-radius-md, 6px));
70
+ --_row-gap: var(--slotted-side-navigation-row-gap, var(--slotted-space-1, 0.125rem));
71
+ --_section-gap: var(--slotted-side-navigation-section-gap, var(--slotted-space-5, 0.75rem));
72
+ --_guide-width: var(--slotted-border-width-thin, 1px);
73
+ --_dot-size: var(--slotted-side-navigation-dot-size, var(--slotted-space-3, 0.375rem));
74
+
75
+ --_color: var(--slotted-side-navigation-color, var(--slotted-text-muted, GrayText));
76
+ --_hover-color: var(--slotted-side-navigation-hover-color, var(--slotted-text, CanvasText));
77
+ --_hover-background: var(
78
+ --slotted-side-navigation-hover-background,
79
+ var(--slotted-tone-secondary-subtle-hover)
80
+ );
81
+ --_active-background: var(
82
+ --slotted-side-navigation-active-background,
83
+ var(--slotted-tone-secondary-subtle-active)
84
+ );
85
+ --_current-color: var(--slotted-side-navigation-current-color, var(--slotted-tone-accent-text));
86
+ --_current-background: var(
87
+ --slotted-side-navigation-current-background,
88
+ var(--slotted-tone-accent-surface)
89
+ );
90
+ /**
91
+ * The state is a pair, ground and edge: in `high-contrast` every tone
92
+ * ground is the page's own white and the edge is what is left to say where
93
+ * the reader is. The edge is the foundation's `--slotted-current-edge`:
94
+ * transparent in `light` and `dark`, where the accent ground and the
95
+ * accent ink say the state and a grey line around a tinted row read as a
96
+ * chosen option rather than a place; black in `high-contrast`, where it
97
+ * carries the state alone. Drawn as an inset shadow, so it changes no
98
+ * measurement and moves no text.
99
+ */
100
+ --_current-edge: var(
101
+ --slotted-side-navigation-current-edge,
102
+ var(--slotted-current-edge, transparent)
103
+ );
104
+ --_dot-color: var(--slotted-side-navigation-dot-color, var(--slotted-tone-accent-solid));
105
+ --_section-color: var(
106
+ --slotted-side-navigation-section-color,
107
+ var(--slotted-text-muted, GrayText)
108
+ );
109
+ --_description-color: var(
110
+ --slotted-side-navigation-description-color,
111
+ var(--slotted-text-muted, GrayText)
112
+ );
113
+ /**
114
+ * The second line of the current row is the page's own ink, not the
115
+ * tone's: the muted ink measures 4.47:1 over the accent surface in
116
+ * `light`, under the floor, and the tone's text would make the two lines
117
+ * one colour. The default ink keeps the line secondary by size and weight.
118
+ */
119
+ --_current-description-color: var(
120
+ --slotted-side-navigation-current-description-color,
121
+ var(--slotted-text, CanvasText)
122
+ );
123
+ /** The air between a title and its second line. */
124
+ --_description-gap: var(
125
+ --slotted-side-navigation-description-gap,
126
+ var(--slotted-space-1, 0.125rem)
127
+ );
128
+ --_guide-color: var(
129
+ --slotted-side-navigation-guide-color,
130
+ var(--slotted-border-subtle, ButtonBorder)
131
+ );
132
+ --_disabled-color: var(--slotted-disabled-foreground, GrayText);
133
+
134
+ --_duration: var(--slotted-control-transition-duration, var(--slotted-duration-short));
135
+ --_easing: var(--slotted-control-transition-easing, var(--slotted-easing-standard));
136
+
137
+ box-sizing: border-box;
138
+ color: var(--_color);
139
+ font-family: var(--slotted-control-font-family, inherit);
140
+ font-size: var(--_font-size);
141
+ line-height: var(--_line-height);
142
+ min-inline-size: 0;
143
+ }
144
+
145
+ [data-ui='side-navigation'][data-size='sm'] {
146
+ --_row-height: var(--slotted-control-height-sm, 30px);
147
+ --_mark: var(--_row-height);
148
+ --_gap: var(--slotted-control-gap-sm, 6px);
149
+ --_font-size: var(--slotted-font-size-xs, 0.75rem);
150
+ --_line-height: var(--slotted-line-height-xs, 1.4);
151
+ --_icon-size: var(--slotted-control-icon-size-sm, 14px);
152
+ }
153
+
154
+ [data-ui='side-navigation'][data-size='lg'] {
155
+ --_row-height: var(--slotted-control-height-lg, 38px);
156
+ --_mark: var(--_row-height);
157
+ --_gap: var(--slotted-control-gap-lg, 8px);
158
+ --_font-size: var(--slotted-font-size-md, 0.875rem);
159
+ --_line-height: var(--slotted-line-height-md, 1.5);
160
+ --_icon-size: var(--slotted-control-icon-size-lg, 18px);
161
+ --_description-font-size: var(--slotted-font-size-sm, 0.8125rem);
162
+ }
163
+
164
+ /* ── The root ─────────────────────────────────────────────────────────── */
165
+
166
+ [data-ui='side-navigation'][data-part='root'] {
167
+ container-name: slotted-side-navigation;
168
+ container-type: inline-size;
169
+ display: flex;
170
+ flex-direction: column;
171
+ gap: var(--_section-gap);
172
+ /**
173
+ * `container-type: inline-size` makes the box's intrinsic width nought,
174
+ * so the width is stated: the shell's rail centres the children of its
175
+ * body, and a root left to `auto` there measured nothing and drew nothing.
176
+ */
177
+ inline-size: 100%;
178
+ }
179
+
180
+ [data-ui='side-navigation'] [data-part='section'] {
181
+ display: flex;
182
+ flex-direction: column;
183
+ min-inline-size: 0;
184
+ }
185
+
186
+ /**
187
+ * The section's title starts where the mark's ink starts — the row's inset
188
+ * plus the air the box keeps around the glyph — rather than on the label
189
+ * column: a navigation whose rows carry no marks keeps its titles near its
190
+ * words, and one whose rows do keeps them on the glyphs' own edge.
191
+ */
192
+ [data-ui='side-navigation'] [data-part='section-label'] {
193
+ color: var(--_section-color);
194
+ font-size: var(--_section-font-size);
195
+ font-weight: var(--slotted-font-weight-semibold, 600);
196
+ line-height: var(--slotted-line-height-xs, 1.4);
197
+ /* Air between the title and the first row's box: with the row current, the
198
+ two grounds stood a pixel apart and read as one shape. */
199
+ margin-block-end: var(
200
+ --slotted-side-navigation-section-label-gap,
201
+ var(--slotted-space-2, 0.25rem)
202
+ );
203
+ min-inline-size: 0;
204
+ padding-block: var(--slotted-space-2, 0.25rem);
205
+ overflow-wrap: anywhere;
206
+ padding-inline: calc(var(--_inset) + (var(--_mark) - var(--_icon-size)) / 2);
207
+ }
208
+
209
+ [data-ui='side-navigation'] [data-part='list'],
210
+ [data-ui='side-navigation'] [data-part='group'] {
211
+ display: flex;
212
+ flex-direction: column;
213
+ gap: var(--_row-gap);
214
+ list-style: none;
215
+ margin: 0;
216
+ min-inline-size: 0;
217
+ padding: 0;
218
+ }
219
+
220
+ /* ── The item: a row, and a group under it ────────────────────────────── */
221
+
222
+ /**
223
+ * Three columns for the row — the link, the trigger beside it, the actions —
224
+ * and a second row for the group. A trigger that is the whole row spans the
225
+ * three columns; the group always does.
226
+ */
227
+ [data-ui='side-navigation'] [data-part='item'] {
228
+ /**
229
+ * `start`, not the default stretch. While the second track is between
230
+ * `0fr` and `1fr` the grid holds free space, and a stretched `auto` first
231
+ * track takes it: measured, the row grew from 34 to 60 pixels at the
232
+ * middle of every open and close and came back — a bounce on the row the
233
+ * reader had just pressed. Packed at the start, the row stays 34.
234
+ */
235
+ align-content: start;
236
+ display: grid;
237
+ grid-template-columns: minmax(0, 1fr) auto auto;
238
+ grid-template-rows: auto 0fr;
239
+ min-inline-size: 0;
240
+ position: relative;
241
+ /**
242
+ * ── Motion, planned ─────────────────────────────────────────────────
243
+ *
244
+ * Four things move in this family, each on one token:
245
+ *
246
+ * 1. A group opening and closing: the track, `--slotted-duration-medium`
247
+ * on the standard curve, and the rows inside it fading on
248
+ * `--slotted-duration-short` — in on the way open, so they arrive as
249
+ * the room for them does, and out on the way closed, so the track
250
+ * never folds over words that are still fully painted.
251
+ * 2. The chevron turning a quarter turn, on the track's own
252
+ * `--slotted-duration-medium`, so the turn and the opening end together.
253
+ * 3. A row's ground and ink under hover and current,
254
+ * `--slotted-control-transition-duration`; a press answers at once,
255
+ * on `--slotted-duration-instant`, because feedback that fades in is
256
+ * feedback that arrives late.
257
+ * 4. The flyout, on the overlay rule every layer follows (R7).
258
+ *
259
+ * What does not move: the label leaving for the rail. The region's width
260
+ * is what animates there, in `app-shell`, and the query flips the row at
261
+ * ninety-six pixels; a fade on top of a width already in motion is two
262
+ * animations for one change.
263
+ */
264
+ transition: grid-template-rows var(--slotted-duration-medium) var(--_easing);
265
+ }
266
+
267
+ [data-ui='side-navigation'] [data-part='item'][data-expanded] {
268
+ grid-template-rows: auto 1fr;
269
+ }
270
+
271
+ [data-ui='side-navigation'] [data-part='item'] > [data-part='group'] {
272
+ grid-column: 1 / -1;
273
+ grid-row: 2;
274
+ min-block-size: 0;
275
+ overflow: hidden;
276
+ /**
277
+ * A closed group is out of the tab sequence and the accessibility tree
278
+ * from the moment the track has finished closing, and back in it from the
279
+ * moment it starts to open: `step-end` on the way out, `step-start` on
280
+ * the way in.
281
+ */
282
+ opacity: 0;
283
+ /* The air above the first child belongs to the open state: closed, the
284
+ track is nought and a closed item is the height of its row alone. */
285
+ padding-block-start: 0;
286
+ transition:
287
+ opacity var(--slotted-duration-short) var(--slotted-easing-exit),
288
+ padding-block-start var(--slotted-duration-medium) var(--_easing),
289
+ visibility var(--slotted-duration-medium) step-end;
290
+ visibility: hidden;
291
+ }
292
+
293
+ [data-ui='side-navigation'] [data-part='item'][data-expanded] > [data-part='group'] {
294
+ opacity: 1;
295
+ padding-block-start: var(--_row-gap);
296
+ transition:
297
+ opacity var(--slotted-duration-short) var(--slotted-easing-enter)
298
+ var(--slotted-duration-short),
299
+ padding-block-start var(--slotted-duration-medium) var(--_easing),
300
+ visibility var(--slotted-duration-medium) step-start;
301
+ visibility: visible;
302
+ }
303
+
304
+ /**
305
+ * The nested list is inset so its labels stand under the parent's, and a
306
+ * guide runs down the mark's centre: shadcn and Primer both draw it, and it
307
+ * is what tells a third level from a second.
308
+ */
309
+ [data-ui='side-navigation'] [data-part='item'] > [data-part='group'] {
310
+ padding-inline-start: var(--_indent-step);
311
+ position: relative;
312
+ }
313
+
314
+ /* Under a row with a mark, the children stand under the parent's words. */
315
+ [data-ui='side-navigation']
316
+ [data-part='item']:has(> :is([data-part='link'], [data-part='trigger']) > [data-part='icon'])
317
+ > [data-part='group'] {
318
+ padding-inline-start: var(--_indent);
319
+ }
320
+
321
+ [data-ui='side-navigation'][data-guides] [data-part='item'] > [data-part='group']::before {
322
+ background-color: var(--_guide-color);
323
+ content: '';
324
+ inline-size: var(--_guide-width);
325
+ inset-block: var(--_row-gap) 0;
326
+ /* On the parent's own text edge, for a parent with no mark … */
327
+ inset-inline-start: var(--_inset);
328
+ position: absolute;
329
+ }
330
+
331
+ /* … and down the centre of the mark for one that has it. */
332
+ [data-ui='side-navigation'][data-guides]
333
+ [data-part='item']:has(> :is([data-part='link'], [data-part='trigger']) > [data-part='icon'])
334
+ > [data-part='group']::before {
335
+ inset-inline-start: calc(var(--_inset) + var(--_mark) / 2 - var(--_guide-width) / 2);
336
+ }
337
+
338
+ /* ── The row: a link, or a trigger ────────────────────────────────────── */
339
+
340
+ [data-ui='side-navigation'] :is([data-part='link'], [data-part='trigger']) {
341
+ align-content: center;
342
+ align-items: center;
343
+ appearance: none;
344
+ background-color: transparent;
345
+ border: 0;
346
+ border-radius: var(--_radius);
347
+ box-sizing: border-box;
348
+ color: inherit;
349
+ cursor: pointer;
350
+ display: grid;
351
+ font: inherit;
352
+ grid-column: 1;
353
+ grid-row: 1;
354
+ grid-template-columns: auto minmax(0, 1fr) auto auto;
355
+ grid-template-rows: auto auto;
356
+ margin-block: 0;
357
+ margin-inline: var(--_standoff);
358
+ min-block-size: var(--_row-height);
359
+ min-inline-size: 0;
360
+ padding-block: 0;
361
+ padding-inline: calc(var(--_inset) - var(--_standoff));
362
+ position: relative;
363
+ text-align: start;
364
+ text-decoration: none;
365
+ transition:
366
+ background-color var(--_duration) var(--_easing),
367
+ color var(--_duration) var(--_easing);
368
+ }
369
+
370
+ [data-ui='side-navigation']
371
+ [data-part='item']:not([data-disabled])
372
+ > :is([data-part='link'], [data-part='trigger']):active {
373
+ transition-duration: var(--slotted-duration-instant);
374
+ }
375
+
376
+ [data-ui='side-navigation'] [data-part='item'] > [data-part='trigger'] {
377
+ grid-column: 1 / -1;
378
+ }
379
+
380
+ /**
381
+ * Beside a link, the trigger is a mark-only button at the end of the row —
382
+ * the APG hybrid example's second control — the height of the row and the
383
+ * width of a mark, so the pair reads as one row with a chevron on it.
384
+ */
385
+ [data-ui='side-navigation'] [data-part='item']:has(> [data-part='link']) > [data-part='trigger'] {
386
+ grid-column: 2;
387
+ grid-template-columns: auto;
388
+ inline-size: var(--_mark);
389
+ padding-inline: 0;
390
+ place-items: center;
391
+ }
392
+
393
+ [data-ui='side-navigation'] [data-part='actions'] {
394
+ align-items: center;
395
+ align-self: center;
396
+ column-gap: var(--slotted-space-2, 0.25rem);
397
+ display: flex;
398
+ grid-column: 3;
399
+ grid-row: 1;
400
+ /* Air on both sides: from the row it stands beside, and from the column's edge. */
401
+ margin-inline-start: var(--slotted-space-2, 0.25rem);
402
+ padding-inline-end: var(--slotted-space-2, 0.25rem);
403
+ }
404
+
405
+ [data-ui='side-navigation'] :is([data-part='link'], [data-part='trigger']):focus-visible {
406
+ outline: var(--slotted-focus-ring-width, 2px) solid var(--slotted-focus-ring-color, Highlight);
407
+ outline-offset: calc(-1 * var(--slotted-focus-ring-offset, 2px));
408
+ }
409
+
410
+ /* ── The parts of a row ───────────────────────────────────────────────── */
411
+
412
+ /**
413
+ * The mark's box sizes what it holds: a bare svg by the child rule below,
414
+ * an icon box through `--slotted-icon-size`, and a glyph host such as
415
+ * `ng-icon` — which sizes itself at `1em` — through the font-size. Nothing
416
+ * placed here is drawn at the row's type size.
417
+ */
418
+ [data-ui='side-navigation'] [data-part='icon'] {
419
+ --slotted-icon-size: var(--_icon-size);
420
+
421
+ block-size: var(--_mark);
422
+ color: inherit;
423
+ display: grid;
424
+ font-size: var(--_icon-size);
425
+ grid-column: 1;
426
+ grid-row: 1;
427
+ inline-size: var(--_mark);
428
+ /**
429
+ * The mark's box starts where the row's inset ends, in a labelled column
430
+ * and in the rail alike: the rail is the box plus an inset a side, so a
431
+ * row with no padding and its mark centred puts the mark where a padded
432
+ * row put it. Nothing moves when the region changes width.
433
+ */
434
+ margin-inline-end: var(--_gap);
435
+ place-items: center;
436
+ position: relative;
437
+ }
438
+
439
+ [data-ui='side-navigation'] [data-part='icon'] > svg {
440
+ block-size: var(--_icon-size);
441
+ inline-size: var(--_icon-size);
442
+ }
443
+
444
+ /**
445
+ * ── Nothing is lost ─────────────────────────────────────────────────
446
+ *
447
+ * Truncation is five decisions, and each is written in the contract under
448
+ * `truncate`. Where the cut falls: at the end, by this sheet, or in the
449
+ * middle, by the binding, which keeps the start and the tail so two names
450
+ * that differ only at the end stay two names. How the whole is reached: the
451
+ * binding marks a row it measured as clipped with `data-clipped` and the
452
+ * row's tooltip carries the whole text — on hover, on visible focus, on
453
+ * press-and-hold — and a row that fits opens nothing. What gives way: the
454
+ * label alone shrinks; the mark, the count, the suffix, the chevron and the
455
+ * actions keep their width. What never clips: the count and the section
456
+ * title, which wraps instead. And the opt-out, `truncate="none"`, wraps the
457
+ * label, with `overflow-wrap: anywhere` breaking an unbroken token inside
458
+ * itself rather than letting it set the width of the column.
459
+ *
460
+ * An ellipsis rather than a fade, because a fade hides that anything is
461
+ * missing. `isolate` keeps a title in another script from reordering the
462
+ * row around it. The text is in the DOM in every mode, which is what
463
+ * `docs/build-briefing.md` requires.
464
+ */
465
+ [data-ui='side-navigation'] [data-part='label'] {
466
+ grid-column: 2;
467
+ grid-row: 1;
468
+ min-inline-size: 0;
469
+ overflow-wrap: anywhere;
470
+ unicode-bidi: isolate;
471
+ }
472
+
473
+ [data-ui='side-navigation']:not([data-truncate='none']) [data-part='label'] {
474
+ overflow: hidden;
475
+ overflow-wrap: normal;
476
+ text-overflow: ellipsis;
477
+ white-space: nowrap;
478
+ }
479
+
480
+ /* The binding has already placed the ellipsis; a second one at the edge would be a lie. */
481
+ [data-ui='side-navigation'][data-truncate='middle'] [data-part='label'] {
482
+ text-overflow: clip;
483
+ }
484
+
485
+ [data-ui='side-navigation'] [data-part='description'] {
486
+ color: var(--_description-color);
487
+ font-size: var(--_description-font-size);
488
+ font-weight: var(--slotted-font-weight-regular, 400);
489
+ grid-column: 2;
490
+ grid-row: 2;
491
+ line-height: var(--slotted-line-height-xs, 1.4);
492
+ margin-block-start: var(--_description-gap);
493
+ min-inline-size: 0;
494
+ overflow-wrap: anywhere;
495
+ unicode-bidi: isolate;
496
+ }
497
+
498
+ [data-ui='side-navigation']:not([data-truncate='none']) [data-part='description'] {
499
+ overflow: hidden;
500
+ overflow-wrap: normal;
501
+ text-overflow: ellipsis;
502
+ white-space: nowrap;
503
+ }
504
+
505
+ /**
506
+ * Every part but the description stands in the first track, and the mark
507
+ * decides that track's height. A part spanning both tracks would hand half
508
+ * its height to the empty second one, which put every one-line label in the
509
+ * top half of its row. A two-line row is the exception: there the second
510
+ * track has words in it, so the mark, the suffix and the chevron span the
511
+ * pair and centre on it, and the two lines sit on their own line boxes with
512
+ * nothing between — the first version put the title on the mark's own
513
+ * thirty-four pixels and the description under that, which read as a gap.
514
+ */
515
+ [data-ui='side-navigation']
516
+ :is([data-part='link'], [data-part='trigger']):has(> [data-part='description']) {
517
+ padding-block: var(--slotted-space-2, 0.25rem);
518
+ }
519
+
520
+ [data-ui='side-navigation']
521
+ :is([data-part='link'], [data-part='trigger']):has(> [data-part='description'])
522
+ > :is([data-part='icon'], [data-part='end'], [data-part='count'], [data-part='indicator']) {
523
+ grid-row: 1 / -1;
524
+ }
525
+
526
+ [data-ui='side-navigation']
527
+ :is([data-part='link'], [data-part='trigger']):has(> [data-part='description'])
528
+ > [data-part='label'] {
529
+ align-self: end;
530
+ line-height: var(--slotted-line-height-xs, 1.4);
531
+ }
532
+
533
+ [data-ui='side-navigation']
534
+ :is([data-part='link'], [data-part='trigger']):has(> [data-part='description'])
535
+ > [data-part='description'] {
536
+ align-self: start;
537
+ }
538
+
539
+ [data-ui='side-navigation'] [data-part='end'] {
540
+ align-items: center;
541
+ display: flex;
542
+ grid-column: 3;
543
+ grid-row: 1;
544
+ margin-inline-start: var(--_gap);
545
+ }
546
+
547
+ /**
548
+ * A quantity beside a destination — unread, pending, overdue — is a number
549
+ * and not a status, and it is drawn as one: tabular figures on the
550
+ * description's type step, in the row's muted ink, taking the current
551
+ * row's ink with it. The digits keep their column across rows so twelve
552
+ * under three lines up. A count that carries a tone — three overdue — is a
553
+ * `badge`, composed into `end`, and the two are told apart on purpose:
554
+ * every row with a number would otherwise be a row with a pill.
555
+ */
556
+ [data-ui='side-navigation'] [data-part='count'] {
557
+ color: var(--_description-color);
558
+ font-size: var(--_description-font-size);
559
+ font-variant-numeric: tabular-nums;
560
+ font-weight: var(--slotted-font-weight-medium, 500);
561
+ grid-column: 3;
562
+ grid-row: 1;
563
+ line-height: 1;
564
+ margin-inline-start: var(--_gap);
565
+ min-inline-size: 2ch;
566
+ text-align: end;
567
+ }
568
+
569
+ [data-ui='side-navigation'] [data-part='link'][data-current] > [data-part='description'] {
570
+ color: var(--_current-description-color);
571
+ }
572
+
573
+ [data-ui='side-navigation'] [data-part='link'][data-current] > [data-part='count'],
574
+ [data-ui='side-navigation']
575
+ [data-part='item'][data-current]
576
+ > [data-part='trigger']
577
+ > [data-part='count'] {
578
+ color: inherit;
579
+ }
580
+
581
+ [data-ui='side-navigation'] [data-part='indicator'] {
582
+ --slotted-icon-size: var(--_icon-size);
583
+
584
+ display: flex;
585
+ grid-column: 4;
586
+ grid-row: 1;
587
+ margin-inline-start: var(--_gap);
588
+ }
589
+
590
+ /**
591
+ * The mark is the `chevron-right` Glyph element in a `directional` icon box.
592
+ * Closed, it points along the reading direction — the way the children are
593
+ * folded away — and turns a quarter turn to point down when they are shown.
594
+ * The box mirrors itself in a right-to-left document, where the reading
595
+ * direction is the other way; the open mark points down in both, turning the
596
+ * other way where it is mirrored.
597
+ */
598
+ [data-ui='side-navigation'] [data-part='indicator'] > [data-ui='icon'] {
599
+ /* The same duration as the track it announces, so the turn and the opening end together. */
600
+ transition: rotate var(--slotted-duration-medium) var(--_easing);
601
+ }
602
+
603
+ [data-ui='side-navigation']
604
+ [data-part='item']:has(> [data-part='link'])
605
+ > [data-part='trigger']
606
+ > [data-part='indicator'] {
607
+ grid-column: 1;
608
+ margin-inline-start: 0;
609
+ }
610
+
611
+ [data-ui='side-navigation']
612
+ [data-part='item'][data-expanded]
613
+ > [data-part='trigger']
614
+ > [data-part='indicator']
615
+ > [data-ui='icon'] {
616
+ rotate: 90deg;
617
+ }
618
+
619
+ /* The mirror is applied before the turn, so the mirrored mark turns the other way to land on down. */
620
+ [data-ui='side-navigation']
621
+ [data-part='item'][data-expanded]
622
+ > [data-part='trigger']
623
+ > [data-part='indicator']
624
+ > [data-ui='icon']:dir(rtl) {
625
+ rotate: -90deg;
626
+ }
627
+
628
+ /* ── The ink, centred rather than the line box ────────────────────────── */
629
+
630
+ /**
631
+ * A grid track centres the *line box*, and a line box is not symmetric
632
+ * around the letters in it: the ascender reaches further above the cap than
633
+ * the descender reaches below the baseline, so a label centred beside a
634
+ * mark sits high — the reviewer read the words as "more up than down".
635
+ * `text-box` trims each line to the cap and the baseline, and the padding
636
+ * gives the half-leading back on both sides equally, so the box the track
637
+ * centres is symmetric around the ink and the row keeps the height it had.
638
+ * It is the treatment `button`, `badge`, `kbd` and `tag` already take.
639
+ *
640
+ * The count is trimmed too, so a figure's cap centre and the label's cap
641
+ * centre are the same line; the section title and the description follow
642
+ * for the same reason. `0.75em` stands for the cap height, as it does in
643
+ * `button`: a proportion of the type, which is what the trim removes.
644
+ */
645
+ @supports (text-box: trim-both cap alphabetic) {
646
+ [data-ui='side-navigation'] [data-part='label'] {
647
+ padding-block: calc((var(--_line-height) * 1em - 0.75em) / 2);
648
+ text-box: trim-both cap alphabetic;
649
+ }
650
+
651
+ [data-ui='side-navigation'] [data-part='description'],
652
+ [data-ui='side-navigation'] [data-part='section-label'] {
653
+ padding-block: calc((var(--slotted-line-height-xs, 1.4) * 1em - 0.75em) / 2);
654
+ text-box: trim-both cap alphabetic;
655
+ }
656
+
657
+ [data-ui='side-navigation'] [data-part='count'] {
658
+ line-height: var(--slotted-line-height-xs, 1.4);
659
+ padding-block: calc((var(--slotted-line-height-xs, 1.4) * 1em - 0.75em) / 2);
660
+ text-box: trim-both cap alphabetic;
661
+ }
662
+ }
663
+
664
+ /* ── States ───────────────────────────────────────────────────────────── */
665
+
666
+ [data-ui='side-navigation']
667
+ [data-part='item']:not([data-disabled])
668
+ > [data-part='link']:not([data-current]):hover,
669
+ [data-ui='side-navigation']
670
+ [data-part='item']:not([data-disabled]):not([data-current])
671
+ > [data-part='trigger']:enabled:hover {
672
+ background-color: var(--_hover-background);
673
+ color: var(--_hover-color);
674
+ }
675
+
676
+ /* A trigger whose group is in the flyout holds the pressed ground while the layer is open. */
677
+ [data-ui='side-navigation']
678
+ [data-part='item'][data-open]:not([data-current])
679
+ > [data-part='trigger'],
680
+ [data-ui='side-navigation']
681
+ [data-part='item']:not([data-disabled])
682
+ > [data-part='link']:not([data-current]):active,
683
+ [data-ui='side-navigation']
684
+ [data-part='item']:not([data-disabled]):not([data-current])
685
+ > [data-part='trigger']:enabled:active {
686
+ background-color: var(--_active-background);
687
+ }
688
+
689
+ /**
690
+ * The current destination, and the parent that hides it: the tone's ground
691
+ * under its text, the pair the foundation calibrates against each other.
692
+ * Carbon and PatternFly draw a bar on the row's start edge instead, and
693
+ * `state-rail.test.mjs` refuses one (R1): the state is said by the ground.
694
+ */
695
+ [data-ui='side-navigation'] [data-part='link'][data-current],
696
+ [data-ui='side-navigation'] [data-part='item'][data-current] > [data-part='trigger'] {
697
+ background-color: var(--_current-background);
698
+ box-shadow: inset 0 0 0 var(--_guide-width) var(--_current-edge);
699
+ color: var(--_current-color);
700
+ font-weight: var(--slotted-font-weight-medium, 500);
701
+ }
702
+
703
+ [data-ui='side-navigation']
704
+ [data-part='item'][data-disabled]
705
+ > :is([data-part='link'], [data-part='trigger']),
706
+ [data-ui='side-navigation'] [data-part='trigger']:disabled {
707
+ color: var(--_disabled-color);
708
+ cursor: not-allowed;
709
+ }
710
+
711
+ [data-ui='side-navigation']
712
+ [data-part='item'][data-disabled]
713
+ > [data-part='link']
714
+ > [data-part='description'] {
715
+ color: var(--_disabled-color);
716
+ }
717
+
718
+ /* ── The divider and the skeleton row ─────────────────────────────────── */
719
+
720
+ [data-ui='side-navigation'] [data-part='divider'] {
721
+ border: 0;
722
+ border-block-start: var(--_guide-width) solid var(--_guide-color);
723
+ margin-block: var(--slotted-space-2, 0.25rem);
724
+ margin-inline: var(--_inset);
725
+ }
726
+
727
+ [data-ui='side-navigation'] [data-part='skeleton'] {
728
+ align-items: center;
729
+ display: grid;
730
+ gap: var(--_gap);
731
+ grid-template-columns: var(--_mark) minmax(0, 1fr);
732
+ min-block-size: var(--_row-height);
733
+ padding-inline: var(--_inset);
734
+ }
735
+
736
+ [data-ui='side-navigation'] [data-part='skeleton'] > [data-part='icon'] {
737
+ margin-inline: 0;
738
+ }
739
+
740
+ /* The boxes are `skeleton`'s; this family only says how big a mark is. */
741
+ [data-ui='side-navigation'] [data-part='skeleton'] > [data-part='icon'] > [data-ui='skeleton'] {
742
+ --slotted-skeleton-block-size: var(--_icon-size);
743
+ --slotted-skeleton-inline-size: var(--_icon-size);
744
+ }
745
+
746
+ [data-ui='side-navigation'] [data-part='skeleton'] > [data-part='label'] {
747
+ display: block;
748
+ min-inline-size: 0;
749
+ overflow: visible;
750
+ }
751
+
752
+ /* ── Compact: the rail ────────────────────────────────────────────────── */
753
+
754
+ /**
755
+ * What leaves the eye and what stays. The label is clipped rather than
756
+ * removed, so the link keeps its name; the trigger beside a link is removed,
757
+ * because a rail has no room for two controls in a row and the mark is the
758
+ * link's. The rules are written twice — once under the container query for
759
+ * `auto`, once under `mode="compact"` — because a container's own query
760
+ * cannot select it and there is no third selector that says "either".
761
+ */
762
+ @container slotted-side-navigation (inline-size < 96px) {
763
+ [data-ui='side-navigation'][data-mode='auto'] [data-part='label'],
764
+ [data-ui='side-navigation'][data-mode='auto'] [data-part='description'],
765
+ [data-ui='side-navigation'][data-mode='auto'] [data-part='end'],
766
+ [data-ui='side-navigation'][data-mode='auto'] [data-part='count'] {
767
+ block-size: 1px;
768
+ clip-path: inset(50%);
769
+ inline-size: 1px;
770
+ margin: 0;
771
+ overflow: hidden;
772
+ position: absolute;
773
+ white-space: nowrap;
774
+ }
775
+
776
+ [data-ui='side-navigation'][data-mode='auto'] [data-part='indicator'],
777
+ [data-ui='side-navigation'][data-mode='auto'] [data-part='actions'],
778
+ [data-ui='side-navigation'][data-mode='auto']
779
+ [data-part='item']:has(> [data-part='link'])
780
+ > [data-part='trigger'] {
781
+ display: none;
782
+ }
783
+
784
+ [data-ui='side-navigation'][data-mode='auto'] :is([data-part='link'], [data-part='trigger']) {
785
+ grid-template-columns: auto;
786
+ inline-size: var(--_mark);
787
+ justify-content: center;
788
+ justify-self: center;
789
+ padding: 0;
790
+ }
791
+
792
+ [data-ui='side-navigation'][data-mode='auto'] [data-part='icon'] {
793
+ margin: 0;
794
+ }
795
+
796
+ [data-ui='side-navigation'][data-mode='auto'] [data-part='item'] {
797
+ grid-template-rows: auto 0fr;
798
+ }
799
+
800
+ [data-ui='side-navigation'][data-mode='auto'] [data-part='item'] > [data-part='group'] {
801
+ visibility: hidden;
802
+ }
803
+
804
+ [data-ui='side-navigation'][data-mode='auto'] [data-part='section-label'] {
805
+ block-size: 0;
806
+ border-block-start: var(--_guide-width) solid var(--_guide-color);
807
+ color: transparent;
808
+ margin-block: var(--slotted-space-2, 0.25rem);
809
+ margin-inline: var(--_inset);
810
+ overflow: hidden;
811
+ padding: 0;
812
+ }
813
+
814
+ [data-ui='side-navigation'][data-mode='auto'] [data-part='skeleton'] {
815
+ grid-template-columns: var(--_mark);
816
+ justify-content: center;
817
+ padding-inline: 0;
818
+ }
819
+
820
+ [data-ui='side-navigation'][data-mode='auto']
821
+ [data-part='skeleton']
822
+ > :not([data-part='icon']) {
823
+ display: none;
824
+ }
825
+
826
+ [data-ui='side-navigation'][data-mode='auto']
827
+ :is([data-part='link'], [data-part='trigger']):has(
828
+ > :is([data-part='end'], [data-part='count'])
829
+ )
830
+ > [data-part='icon']::after {
831
+ background-color: var(--_dot-color);
832
+ block-size: var(--_dot-size);
833
+ border-radius: var(--slotted-radius-full, 9999px);
834
+ content: '';
835
+ inline-size: var(--_dot-size);
836
+ inset-block-start: var(--slotted-space-2, 0.25rem);
837
+ inset-inline-end: var(--slotted-space-2, 0.25rem);
838
+ position: absolute;
839
+ }
840
+ }
841
+
842
+ [data-ui='side-navigation'][data-mode='compact'] [data-part='label'],
843
+ [data-ui='side-navigation'][data-mode='compact'] [data-part='description'],
844
+ [data-ui='side-navigation'][data-mode='compact'] [data-part='end'],
845
+ [data-ui='side-navigation'][data-mode='compact'] [data-part='count'] {
846
+ block-size: 1px;
847
+ clip-path: inset(50%);
848
+ inline-size: 1px;
849
+ margin: 0;
850
+ overflow: hidden;
851
+ position: absolute;
852
+ white-space: nowrap;
853
+ }
854
+
855
+ [data-ui='side-navigation'][data-mode='compact'] [data-part='indicator'],
856
+ [data-ui='side-navigation'][data-mode='compact'] [data-part='actions'],
857
+ [data-ui='side-navigation'][data-mode='compact']
858
+ [data-part='item']:has(> [data-part='link'])
859
+ > [data-part='trigger'] {
860
+ display: none;
861
+ }
862
+
863
+ [data-ui='side-navigation'][data-mode='compact'] :is([data-part='link'], [data-part='trigger']) {
864
+ grid-template-columns: auto;
865
+ inline-size: var(--_mark);
866
+ justify-content: center;
867
+ justify-self: center;
868
+ padding: 0;
869
+ }
870
+
871
+ [data-ui='side-navigation'][data-mode='compact'] [data-part='icon'] {
872
+ margin: 0;
873
+ }
874
+
875
+ [data-ui='side-navigation'][data-mode='compact'] [data-part='item'] {
876
+ grid-template-rows: auto 0fr;
877
+ }
878
+
879
+ [data-ui='side-navigation'][data-mode='compact'] [data-part='item'] > [data-part='group'] {
880
+ visibility: hidden;
881
+ }
882
+
883
+ [data-ui='side-navigation'][data-mode='compact'] [data-part='section-label'] {
884
+ block-size: 0;
885
+ border-block-start: var(--_guide-width) solid var(--_guide-color);
886
+ color: transparent;
887
+ margin-block: var(--slotted-space-2, 0.25rem);
888
+ margin-inline: var(--_inset);
889
+ overflow: hidden;
890
+ padding: 0;
891
+ }
892
+
893
+ [data-ui='side-navigation'][data-mode='compact'] [data-part='skeleton'] {
894
+ grid-template-columns: var(--_mark);
895
+ justify-content: center;
896
+ padding-inline: 0;
897
+ }
898
+
899
+ [data-ui='side-navigation'][data-mode='compact']
900
+ [data-part='skeleton']
901
+ > :not([data-part='icon']) {
902
+ display: none;
903
+ }
904
+
905
+ [data-ui='side-navigation'][data-mode='compact']
906
+ :is([data-part='link'], [data-part='trigger']):has(
907
+ > :is([data-part='end'], [data-part='count'])
908
+ )
909
+ > [data-part='icon']::after {
910
+ background-color: var(--_dot-color);
911
+ block-size: var(--_dot-size);
912
+ border-radius: var(--slotted-radius-full, 9999px);
913
+ content: '';
914
+ inline-size: var(--_dot-size);
915
+ inset-block-start: var(--slotted-space-2, 0.25rem);
916
+ inset-inline-end: var(--slotted-space-2, 0.25rem);
917
+ position: absolute;
918
+ }
919
+
920
+ /* ── The flyout: one group beside the rail ────────────────────────────── */
921
+
922
+ /**
923
+ * The layer `packages/core/src/positioner` writes `left` and `top` onto, in
924
+ * the portal container and outside the root — which is why it carries
925
+ * `data-ui` of its own and why the root's geometry is stated on `[data-ui]`
926
+ * rather than on the root part. The group inside it is a plain list again:
927
+ * no indent, no guide, the raised surface under it.
928
+ */
929
+ [data-ui='side-navigation'][data-part='flyout'] {
930
+ --_surface: var(
931
+ --slotted-side-navigation-flyout-surface,
932
+ var(--slotted-surface-raised, Canvas)
933
+ );
934
+ --_border-color: var(
935
+ --slotted-side-navigation-flyout-border-color,
936
+ var(--slotted-border-subtle, ButtonBorder)
937
+ );
938
+ --_shadow: var(--slotted-side-navigation-flyout-shadow, var(--slotted-shadow-md));
939
+ --_flyout-radius: var(--slotted-side-navigation-flyout-radius, var(--slotted-radius-lg, 8px));
940
+ --_measure: var(--slotted-side-navigation-flyout-measure, 16rem);
941
+
942
+ box-sizing: border-box;
943
+ inline-size: max-content;
944
+ inset-block-start: 0;
945
+ isolation: isolate;
946
+ max-inline-size: min(var(--_measure), var(--slotted-positioner-available-width, 100vw));
947
+ min-inline-size: var(--slotted-side-navigation-flyout-min-measure, 12rem);
948
+ /* The layer is only a frame around the surface; the surface takes the
949
+ pointer, and the frame must not swallow a press meant for the page. */
950
+ padding-block: 0;
951
+ padding-inline: var(--slotted-space-2, 0.25rem);
952
+ pointer-events: none;
953
+ position: absolute;
954
+ z-index: calc(var(--slotted-z-index-popover, 1100) + var(--slotted-layer-depth, 0));
955
+
956
+ opacity: 0;
957
+ --_enter-distance: var(--slotted-space-2, 0.25rem);
958
+ translate: 0 calc(-1 * var(--_enter-distance));
959
+ transition:
960
+ opacity var(--slotted-duration-short) var(--slotted-easing-exit),
961
+ translate var(--slotted-duration-short) var(--slotted-easing-exit);
962
+ }
963
+
964
+ [data-ui='side-navigation'][data-part='flyout'][data-side='top'] {
965
+ translate: 0 var(--_enter-distance);
966
+ }
967
+
968
+ /* The layer arrives along the inline axis it opens on: from one step inside
969
+ the rail's edge, the way a submenu does. */
970
+ [data-ui='side-navigation'][data-part='flyout'][data-side='right'] {
971
+ translate: calc(-1 * var(--_enter-distance)) 0;
972
+ }
973
+
974
+ [data-ui='side-navigation'][data-part='flyout'][data-side='left'] {
975
+ translate: var(--_enter-distance) 0;
976
+ }
977
+
978
+ [data-ui='side-navigation'][data-part='flyout'][data-open] {
979
+ opacity: 1;
980
+ translate: 0 0;
981
+ transition: none;
982
+ }
983
+
984
+ [data-ui='side-navigation'][data-part='flyout'][data-open][data-positioned] {
985
+ animation: slotted-side-navigation-enter-down var(--slotted-duration-medium)
986
+ var(--slotted-easing-enter);
987
+ }
988
+
989
+ [data-ui='side-navigation'][data-part='flyout'][data-open][data-positioned][data-side='top'] {
990
+ animation-name: slotted-side-navigation-enter-up;
991
+ }
992
+
993
+ [data-ui='side-navigation'][data-part='flyout'][data-open][data-positioned][data-side='right'] {
994
+ animation-name: slotted-side-navigation-enter-from-start;
995
+ }
996
+
997
+ [data-ui='side-navigation'][data-part='flyout'][data-open][data-positioned][data-side='left'] {
998
+ animation-name: slotted-side-navigation-enter-from-end;
999
+ }
1000
+
1001
+ @keyframes slotted-side-navigation-enter-down {
1002
+ from {
1003
+ opacity: 0;
1004
+ translate: 0 calc(-1 * var(--_enter-distance));
1005
+ }
1006
+ }
1007
+
1008
+ @keyframes slotted-side-navigation-enter-up {
1009
+ from {
1010
+ opacity: 0;
1011
+ translate: 0 var(--_enter-distance);
1012
+ }
1013
+ }
1014
+
1015
+ @keyframes slotted-side-navigation-enter-from-start {
1016
+ from {
1017
+ opacity: 0;
1018
+ translate: calc(-1 * var(--_enter-distance)) 0;
1019
+ }
1020
+ }
1021
+
1022
+ @keyframes slotted-side-navigation-enter-from-end {
1023
+ from {
1024
+ opacity: 0;
1025
+ translate: var(--_enter-distance) 0;
1026
+ }
1027
+ }
1028
+
1029
+ @keyframes slotted-side-navigation-fade {
1030
+ from {
1031
+ opacity: 0;
1032
+ }
1033
+ }
1034
+
1035
+ /* Stillness keeps the fade and drops the movement (R7). */
1036
+ @media (prefers-reduced-motion: reduce) {
1037
+ [data-ui='side-navigation'][data-part='flyout'],
1038
+ [data-ui='side-navigation'][data-part='flyout'][data-side='top'],
1039
+ [data-ui='side-navigation'][data-part='flyout'][data-side='left'],
1040
+ [data-ui='side-navigation'][data-part='flyout'][data-side='right'] {
1041
+ translate: 0 0;
1042
+ transition: opacity var(--slotted-duration-short) var(--slotted-easing-exit);
1043
+ }
1044
+
1045
+ [data-ui='side-navigation'][data-part='flyout'][data-open][data-positioned],
1046
+ [data-ui='side-navigation'][data-part='flyout'][data-open][data-positioned][data-side='top'],
1047
+ [data-ui='side-navigation'][data-part='flyout'][data-open][data-positioned][data-side='left'],
1048
+ [data-ui='side-navigation'][data-part='flyout'][data-open][data-positioned][data-side='right'] {
1049
+ animation-name: slotted-side-navigation-fade;
1050
+ }
1051
+ }
1052
+
1053
+ /* The anchor scrolled out of its own clipper: the layer waits, hidden. */
1054
+ [data-ui='side-navigation'][data-part='flyout'][data-detached] {
1055
+ visibility: hidden;
1056
+ }
1057
+
1058
+ [data-ui='side-navigation'][data-part='flyout'] > [data-part='group'] {
1059
+ background-color: var(--_surface);
1060
+ border: var(--_guide-width) solid var(--_border-color);
1061
+ border-radius: var(--_flyout-radius);
1062
+ box-shadow: var(--_shadow);
1063
+ color: var(--_color);
1064
+ padding: var(--slotted-space-2, 0.25rem);
1065
+ pointer-events: auto;
1066
+ }
1067
+
1068
+ [data-ui='side-navigation'][data-part='flyout']
1069
+ > [data-part='group']
1070
+ > [data-part='item']
1071
+ > [data-part='group'] {
1072
+ padding-inline-start: var(--_indent);
1073
+ }
1074
+
1075
+ /* ── Forced colours ───────────────────────────────────────────────────── */
1076
+
1077
+ @media (forced-colors: active) {
1078
+ [data-ui='side-navigation'] [data-part='link'][data-current],
1079
+ [data-ui='side-navigation'] [data-part='item'][data-current] > [data-part='trigger'] {
1080
+ background-color: Highlight;
1081
+ color: HighlightText;
1082
+ forced-color-adjust: none;
1083
+ }
1084
+
1085
+ [data-ui='side-navigation']
1086
+ [data-part='item'][data-disabled]
1087
+ > :is([data-part='link'], [data-part='trigger']),
1088
+ [data-ui='side-navigation'] [data-part='trigger']:disabled {
1089
+ color: GrayText;
1090
+ }
1091
+
1092
+ [data-ui='side-navigation'][data-part='flyout'] > [data-part='group'] {
1093
+ border-color: CanvasText;
1094
+ }
1095
+ }
1096
+ }