@ropav/styles 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/README.md +89 -5
  2. package/dist/components/accordion.css +6 -14
  3. package/dist/components/alert-dialog.css +1 -13
  4. package/dist/components/alert.css +1 -1
  5. package/dist/components/autocomplete/autocomplete.styles.d.ts +33 -0
  6. package/dist/components/autocomplete/autocomplete.styles.d.ts.map +1 -1
  7. package/dist/components/autocomplete/autocomplete.styles.js +12 -0
  8. package/dist/components/autocomplete.css +54 -36
  9. package/dist/components/breadcrumbs.css +9 -2
  10. package/dist/components/button-group.css +6 -10
  11. package/dist/components/button.css +10 -18
  12. package/dist/components/calendar-year-picker.css +20 -8
  13. package/dist/components/calendar.css +46 -21
  14. package/dist/components/card.css +1 -1
  15. package/dist/components/checkbox.css +54 -56
  16. package/dist/components/chip.css +3 -1
  17. package/dist/components/close-button.css +7 -6
  18. package/dist/components/color-area.css +6 -6
  19. package/dist/components/color-input-group/color-input-group.styles.d.ts +27 -0
  20. package/dist/components/color-input-group/color-input-group.styles.d.ts.map +1 -1
  21. package/dist/components/color-input-group/color-input-group.styles.js +6 -0
  22. package/dist/components/color-input-group.css +31 -28
  23. package/dist/components/color-picker.css +1 -5
  24. package/dist/components/color-slider.css +14 -12
  25. package/dist/components/color-swatch-picker.css +2 -85
  26. package/dist/components/color-swatch.css +1 -1
  27. package/dist/components/combo-box/combo-box.styles.d.ts +27 -0
  28. package/dist/components/combo-box/combo-box.styles.d.ts.map +1 -1
  29. package/dist/components/combo-box/combo-box.styles.js +17 -7
  30. package/dist/components/combo-box.css +34 -4
  31. package/dist/components/date-input-group/date-input-group.styles.d.ts +27 -0
  32. package/dist/components/date-input-group/date-input-group.styles.d.ts.map +1 -1
  33. package/dist/components/date-input-group/date-input-group.styles.js +6 -0
  34. package/dist/components/date-input-group.css +55 -33
  35. package/dist/components/date-picker.css +1 -5
  36. package/dist/components/date-range-picker.css +1 -5
  37. package/dist/components/disclosure.css +0 -4
  38. package/dist/components/drawer.css +4 -8
  39. package/dist/components/drop-zone/drop-zone.styles.d.ts +34 -0
  40. package/dist/components/drop-zone/drop-zone.styles.d.ts.map +1 -0
  41. package/dist/components/drop-zone/drop-zone.styles.js +8 -0
  42. package/dist/components/drop-zone/index.d.ts +2 -0
  43. package/dist/components/drop-zone/index.d.ts.map +1 -0
  44. package/dist/components/drop-zone/index.js +2 -0
  45. package/dist/components/drop-zone.css +78 -0
  46. package/dist/components/dropdown.css +1 -5
  47. package/dist/components/error-message.css +0 -4
  48. package/dist/components/field-error.css +0 -4
  49. package/dist/components/index.css +5 -0
  50. package/dist/components/index.d.ts +5 -0
  51. package/dist/components/index.d.ts.map +1 -1
  52. package/dist/components/input/input.styles.d.ts +15 -0
  53. package/dist/components/input/input.styles.d.ts.map +1 -1
  54. package/dist/components/input/input.styles.js +6 -0
  55. package/dist/components/input-group/input-group.styles.d.ts +27 -0
  56. package/dist/components/input-group/input-group.styles.d.ts.map +1 -1
  57. package/dist/components/input-group/input-group.styles.js +6 -0
  58. package/dist/components/input-group.css +35 -38
  59. package/dist/components/input-otp.css +14 -26
  60. package/dist/components/input.css +23 -32
  61. package/dist/components/kbd.css +3 -1
  62. package/dist/components/label.css +7 -2
  63. package/dist/components/link.css +13 -11
  64. package/dist/components/list-box/list-box.styles.d.ts +15 -0
  65. package/dist/components/list-box/list-box.styles.d.ts.map +1 -1
  66. package/dist/components/list-box/list-box.styles.js +15 -5
  67. package/dist/components/list-box-item.css +8 -12
  68. package/dist/components/list-box.css +19 -0
  69. package/dist/components/menu/menu.styles.d.ts +19 -1
  70. package/dist/components/menu/menu.styles.d.ts.map +1 -1
  71. package/dist/components/menu/menu.styles.js +9 -1
  72. package/dist/components/menu-item.css +4 -10
  73. package/dist/components/menu.css +19 -0
  74. package/dist/components/meter.css +8 -16
  75. package/dist/components/modal.css +1 -13
  76. package/dist/components/number-field/number-field.styles.d.ts +27 -0
  77. package/dist/components/number-field/number-field.styles.d.ts.map +1 -1
  78. package/dist/components/number-field/number-field.styles.js +6 -0
  79. package/dist/components/number-field.css +83 -36
  80. package/dist/components/pagination.css +6 -10
  81. package/dist/components/popover.css +15 -5
  82. package/dist/components/progress-bar/progress-bar.styles.d.ts +36 -0
  83. package/dist/components/progress-bar/progress-bar.styles.d.ts.map +1 -1
  84. package/dist/components/progress-bar/progress-bar.styles.js +10 -0
  85. package/dist/components/progress-bar.css +82 -16
  86. package/dist/components/progress-circle.css +0 -4
  87. package/dist/components/radio.css +7 -9
  88. package/dist/components/range-calendar.css +73 -31
  89. package/dist/components/search-field/search-field.styles.d.ts +27 -0
  90. package/dist/components/search-field/search-field.styles.d.ts.map +1 -1
  91. package/dist/components/search-field/search-field.styles.js +6 -0
  92. package/dist/components/search-field.css +36 -29
  93. package/dist/components/segmented-control/index.d.ts +2 -0
  94. package/dist/components/segmented-control/index.d.ts.map +1 -0
  95. package/dist/components/segmented-control/index.js +2 -0
  96. package/dist/components/segmented-control/segmented-control.styles.d.ts +73 -0
  97. package/dist/components/segmented-control/segmented-control.styles.d.ts.map +1 -0
  98. package/dist/components/segmented-control/segmented-control.styles.js +27 -0
  99. package/dist/components/segmented-control.css +188 -0
  100. package/dist/components/select/select.styles.d.ts +33 -0
  101. package/dist/components/select/select.styles.d.ts.map +1 -1
  102. package/dist/components/select/select.styles.js +12 -0
  103. package/dist/components/select.css +47 -33
  104. package/dist/components/sidebar/index.d.ts +2 -0
  105. package/dist/components/sidebar/index.d.ts.map +1 -0
  106. package/dist/components/sidebar/index.js +2 -0
  107. package/dist/components/sidebar/sidebar.styles.d.ts +226 -0
  108. package/dist/components/sidebar/sidebar.styles.d.ts.map +1 -0
  109. package/dist/components/sidebar/sidebar.styles.js +65 -0
  110. package/dist/components/sidebar.css +604 -0
  111. package/dist/components/slider.css +10 -11
  112. package/dist/components/splitter/index.d.ts +2 -0
  113. package/dist/components/splitter/index.d.ts.map +1 -0
  114. package/dist/components/splitter/index.js +2 -0
  115. package/dist/components/splitter/splitter.styles.d.ts +61 -0
  116. package/dist/components/splitter/splitter.styles.d.ts.map +1 -0
  117. package/dist/components/splitter/splitter.styles.js +26 -0
  118. package/dist/components/splitter.css +249 -0
  119. package/dist/components/switch.css +16 -15
  120. package/dist/components/table.css +72 -30
  121. package/dist/components/tabs.css +9 -20
  122. package/dist/components/tag.css +32 -24
  123. package/dist/components/textarea/textarea.styles.d.ts +15 -0
  124. package/dist/components/textarea/textarea.styles.d.ts.map +1 -1
  125. package/dist/components/textarea/textarea.styles.js +6 -0
  126. package/dist/components/textarea.css +23 -32
  127. package/dist/components/toast.css +4 -2
  128. package/dist/components/toggle-button-group.css +7 -11
  129. package/dist/components/toggle-button.css +9 -17
  130. package/dist/components/toolbar.css +1 -1
  131. package/dist/components/tooltip.css +15 -6
  132. package/dist/components/typography.css +12 -4
  133. package/dist/components/virtualizer/index.d.ts +2 -0
  134. package/dist/components/virtualizer/index.d.ts.map +1 -0
  135. package/dist/components/virtualizer/index.js +2 -0
  136. package/dist/components/virtualizer/virtualizer.styles.d.ts +44 -0
  137. package/dist/components/virtualizer/virtualizer.styles.d.ts.map +1 -0
  138. package/dist/components/virtualizer/virtualizer.styles.js +15 -0
  139. package/dist/components/virtualizer.css +72 -0
  140. package/dist/index.js +6 -1
  141. package/dist/ropav.min.css +2 -2
  142. package/dist/themes/airbnb.css +11 -4
  143. package/dist/themes/all.css +1 -0
  144. package/dist/themes/coinbase.css +11 -4
  145. package/dist/themes/default.css +40 -8
  146. package/dist/themes/discord.css +11 -4
  147. package/dist/themes/hero.css +282 -0
  148. package/dist/themes/lavender.css +11 -4
  149. package/dist/themes/mint.css +11 -4
  150. package/dist/themes/netflix.css +11 -4
  151. package/dist/themes/rabbit.css +11 -4
  152. package/dist/themes/shared/theme.css +20 -2
  153. package/dist/themes/sky.css +11 -4
  154. package/dist/themes/spotify.css +11 -4
  155. package/dist/themes/uber.css +11 -4
  156. package/dist/utilities/index.css +27 -0
  157. package/package.json +1 -1
  158. package/src/components/autocomplete/autocomplete.styles.ts +12 -0
  159. package/src/components/color-input-group/color-input-group.styles.ts +10 -0
  160. package/src/components/combo-box/combo-box.styles.ts +12 -0
  161. package/src/components/date-input-group/date-input-group.styles.ts +10 -0
  162. package/src/components/drop-zone/drop-zone.styles.ts +12 -0
  163. package/src/components/drop-zone/index.ts +1 -0
  164. package/src/components/index.ts +5 -0
  165. package/src/components/input/input.styles.ts +6 -0
  166. package/src/components/input-group/input-group.styles.ts +10 -0
  167. package/src/components/list-box/list-box.styles.ts +6 -0
  168. package/src/components/menu/menu.styles.ts +10 -0
  169. package/src/components/number-field/number-field.styles.ts +10 -0
  170. package/src/components/progress-bar/progress-bar.styles.ts +14 -0
  171. package/src/components/search-field/search-field.styles.ts +10 -0
  172. package/src/components/segmented-control/index.ts +1 -0
  173. package/src/components/segmented-control/segmented-control.styles.ts +37 -0
  174. package/src/components/select/select.styles.ts +12 -0
  175. package/src/components/sidebar/index.ts +1 -0
  176. package/src/components/sidebar/sidebar.styles.ts +117 -0
  177. package/src/components/splitter/index.ts +1 -0
  178. package/src/components/splitter/splitter.styles.ts +44 -0
  179. package/src/components/textarea/textarea.styles.ts +6 -0
  180. package/src/components/virtualizer/index.ts +1 -0
  181. package/src/components/virtualizer/virtualizer.styles.ts +15 -0
@@ -0,0 +1,65 @@
1
+ import { tv } from "tailwind-variants";
2
+ //#region src/components/sidebar/sidebar.styles.ts
3
+ const sidebarVariants = tv({
4
+ defaultVariants: {
5
+ collapsible: "icon",
6
+ side: "left",
7
+ variant: "sidebar"
8
+ },
9
+ slots: {
10
+ base: "sidebar",
11
+ content: "sidebar__content",
12
+ footer: "sidebar__footer",
13
+ group: "sidebar__group",
14
+ groupLabel: "sidebar__group-label",
15
+ header: "sidebar__header",
16
+ inset: "sidebar__inset",
17
+ item: "sidebar__item",
18
+ itemIcon: "sidebar__item-icon",
19
+ itemIndicator: "sidebar__item-indicator",
20
+ itemLabel: "sidebar__item-label",
21
+ itemTooltip: "sidebar__item-tooltip",
22
+ itemTrailing: "sidebar__item-trailing",
23
+ panel: "sidebar__panel",
24
+ rail: "sidebar__rail",
25
+ railTarget: "sidebar__rail-target",
26
+ separator: "sidebar__separator",
27
+ subMenu: "sidebar__sub-menu",
28
+ trigger: "sidebar__trigger"
29
+ },
30
+ variants: {
31
+ collapsible: {
32
+ icon: { panel: "sidebar__panel--icon" },
33
+ none: { panel: "sidebar__panel--none" },
34
+ offcanvas: { panel: "sidebar__panel--offcanvas" }
35
+ },
36
+ inDrawer: { true: { panel: "sidebar__panel--drawer" } },
37
+ isCollapsible: { true: { subMenu: "sidebar__sub-menu--collapsible" } },
38
+ isResizable: { true: { rail: "sidebar__rail--resizable" } },
39
+ isSub: { true: { item: "sidebar__item--sub" } },
40
+ side: {
41
+ left: {
42
+ base: "sidebar--left",
43
+ panel: "sidebar__panel--left"
44
+ },
45
+ right: {
46
+ base: "sidebar--right",
47
+ panel: "sidebar__panel--right"
48
+ }
49
+ },
50
+ variant: {
51
+ floating: {
52
+ panel: "sidebar__panel--floating",
53
+ rail: "sidebar__rail--quiet"
54
+ },
55
+ inset: {
56
+ inset: "sidebar__inset--card",
57
+ panel: "sidebar__panel--bare",
58
+ rail: "sidebar__rail--quiet"
59
+ },
60
+ sidebar: {}
61
+ }
62
+ }
63
+ });
64
+ //#endregion
65
+ export { sidebarVariants };
@@ -0,0 +1,604 @@
1
+ /* Sidebar styles */
2
+
3
+ /*
4
+ * The side and the collapse mode are modifier classes rather than `[data-side]` /
5
+ * `[data-collapsible]` descendant selectors on the shell, for the reason `splitter.css` sets out
6
+ * at length: a selector reaching down from the root reaches into anything nested under it too.
7
+ * A sidebar rarely nests, but a sidebar's `Inset` holds a whole page — a settings pane with its
8
+ * own sidebar is an ordinary thing to build — and the inner panel would then resolve the outer
9
+ * one's side and lay its border down the wrong edge.
10
+ *
11
+ * The modifier also buys the Storybook forced-colors sweep, which collects `[class*='--']`. The
12
+ * transient half of the state stays on `data-*`: `data-collapsed` is what changes while you watch,
13
+ * so it belongs where a state key belongs and not in the class list.
14
+ */
15
+
16
+ .sidebar {
17
+ /* The two widths the panel resolves between. Held on the shell rather than the panel so a caller
18
+ * retunes both from one place, and so a drag can write the expanded one to the same property the
19
+ * stylesheet reads. */
20
+ --sidebar-width: calc(var(--spacing) * 64);
21
+ --sidebar-width-icon: calc(var(--spacing) * 14);
22
+ /* The gap a card stands off by and the corner it wears, for the variants where something is
23
+ * a card: the panel under `--floating`, the inset under `--card`. One pair rather than two,
24
+ * because a variant makes exactly one of them a card and a second set would be the same two
25
+ * numbers under a name that is wrong half the time.
26
+ *
27
+ * The corner is the one every container in the library wears, so a sidebar's card and a Card
28
+ * dropped beside it round the same. */
29
+ --sidebar-card-gap: calc(var(--spacing) * 2);
30
+ --sidebar-card-radius: min(32px, var(--component-radius));
31
+ /* The rail's line and the grab area around it, matching `splitter.css` — same hairline, same
32
+ * reasoning about a target no one can hit, same wait before it flares. */
33
+ --sidebar-rail-gutter: 1px;
34
+ --sidebar-rail-target: 15px;
35
+ --sidebar-highlight-delay: 500ms;
36
+ /* What the line is painted with, at rest and lit. Separate properties so a caller can retune one
37
+ * state without flattening the rest onto a single colour. */
38
+ --sidebar-rail-line: var(--separator);
39
+ --sidebar-rail-line-hover: var(--accent);
40
+ --sidebar-rail-line-dragging: var(--sidebar-rail-line-hover);
41
+
42
+ @apply isolate flex h-full w-full;
43
+ }
44
+
45
+ /* Reversing the row rather than reordering the parts: the shell's children are written panel,
46
+ * rail, inset in that order whichever side the sidebar is on, so the rail stays between the two
47
+ * either way and a caller's markup does not change with the prop. */
48
+ .sidebar--left {
49
+ @apply flex-row;
50
+ }
51
+
52
+ .sidebar--right {
53
+ @apply flex-row-reverse;
54
+ }
55
+
56
+ /* ==========================================================================
57
+ Panel — the nav landmark
58
+ ========================================================================== */
59
+
60
+ /* `min-h-0` for the same reason `.splitter__panel` has it: a flex item's automatic minimum is its
61
+ * content, so a long nav list would push the panel past the viewport instead of scrolling inside
62
+ * `.sidebar__content`. */
63
+ .sidebar__panel {
64
+ @apply relative flex min-h-0 shrink-0 grow-0 flex-col overflow-hidden bg-surface text-surface-foreground;
65
+
66
+ inline-size: var(--sidebar-width);
67
+
68
+ transition: inline-size 200ms var(--ease-out);
69
+ @apply motion-reduce:transition-none;
70
+ }
71
+
72
+ /* Under the pointer the panel follows it exactly. Easing towards each frame's width instead makes
73
+ * the edge lag the cursor by the whole duration, which reads as the panel chasing the drag rather
74
+ * than being dragged. */
75
+ .sidebar__panel[data-resizing="true"] {
76
+ transition: none;
77
+ }
78
+
79
+ /* The separating line is a border rather than a background, so it is one of the few things Forced
80
+ * Colors Mode recolours instead of flattening — no restatement needed here. */
81
+ .sidebar__panel--left {
82
+ @apply border-e border-separator/50;
83
+ }
84
+
85
+ .sidebar__panel--right {
86
+ @apply border-s border-separator/50;
87
+ }
88
+
89
+ /* Neither fill nor line: the panel has stopped being a surface, and the inset beside it is the one
90
+ * that paints. After the side modifiers, whose border this has to beat at equal specificity, and
91
+ * before `--drawer`, which stays last for the reason given there. */
92
+ .sidebar__panel--bare {
93
+ @apply border-0 bg-transparent;
94
+ }
95
+
96
+ /* The panel as a card of its own, standing off the page on every side. A whole border rather than
97
+ * the side modifiers' single edge, which at equal specificity is enough to overwrite it — no
98
+ * `border-0` first.
99
+ *
100
+ * The margin is what the rail then sits in, and that gap is already dividing the two, which is why
101
+ * this variant quiets the line: a hairline inside it would be a second answer to the same question.
102
+ */
103
+ .sidebar__panel--floating {
104
+ @apply border border-separator/50 shadow-surface;
105
+
106
+ margin: var(--sidebar-card-gap);
107
+ border-radius: var(--sidebar-card-radius);
108
+ }
109
+
110
+ /* Collapsing to icons is the default, so the width it collapses to lives on the mode that has one
111
+ * and `--none` is the empty rule. */
112
+ .sidebar__panel--icon[data-collapsed="true"] {
113
+ inline-size: var(--sidebar-width-icon);
114
+ }
115
+
116
+ /* Nothing left to divide once the panel is gone, and a hairline standing alone at the edge of the
117
+ * page reads as a rendering fault.
118
+ *
119
+ * The fill goes with it, and that is a declaration rather than a change: a box zero wide with no
120
+ * border paints no background either way. Saying so is what keeps the Storybook forced-colors
121
+ * sweep honest — it reads this panel as one that painted a surface and then lost it to the mode,
122
+ * where in truth there was never anything on screen to lose. */
123
+ .sidebar__panel--offcanvas[data-collapsed="true"] {
124
+ @apply border-0 bg-transparent;
125
+
126
+ inline-size: 0;
127
+ /* And the standing-off a card variant asks for, which on a box no longer there is a gap held
128
+ * open around nothing — the same fault as the hairline, a little wider. */
129
+ margin: 0;
130
+ }
131
+
132
+ .sidebar__panel--none {
133
+ }
134
+
135
+ /* Inside a drawer the panel is no longer the surface — the dialog is, and it paints, sizes and
136
+ * slides itself. The panel gives all of that up and becomes the column that fills what it was
137
+ * handed, the standing-off and the corner of a card variant included: the dialog is already the
138
+ * shape, and a card inset within it would be a second one drawn inside the first.
139
+ * Last of the panel's modifiers, so it beats the widths and the card geometry above at equal
140
+ * specificity. */
141
+ .sidebar__panel--drawer {
142
+ @apply size-full border-0 bg-transparent;
143
+
144
+ inline-size: auto;
145
+ margin: 0;
146
+ border-radius: 0;
147
+ transition: none;
148
+ }
149
+
150
+ /* ==========================================================================
151
+ Header, content, footer
152
+ ========================================================================== */
153
+
154
+ .sidebar__header {
155
+ @apply flex shrink-0 flex-col gap-2 p-2;
156
+ }
157
+
158
+ /* The scroll region. `overflow-x-hidden` because the panel narrows around its contents during the
159
+ * collapse transition, and without it every frame of that transition would flash a scrollbar.
160
+ *
161
+ * `scroll-padding` is what makes the fade safe rather than merely pretty. Focus scrolls an item to
162
+ * the nearest edge, which is exactly where the fade is, so a keyboard user tabbing down the nav
163
+ * would land on item after item with its focus ring faded out. The padding moves that landing
164
+ * point clear of the fade. Sized from the same property the fade is, so retuning one moves both. */
165
+ .sidebar__content {
166
+ @apply flex min-h-0 flex-1 flex-col gap-1 overflow-x-hidden overflow-y-auto p-2;
167
+
168
+ scroll-padding-block: var(--scroll-shadow-size, 0px);
169
+ }
170
+
171
+ /* Two classes to beat one: `scroll-shadow.css` is imported under Layout, long after this file under
172
+ * Navigation, so `.scroll-shadow--vertical`'s `scrollbar` wins at equal specificity and the nav
173
+ * would grow a full-width scrollbar the moment it became a shadow container. */
174
+ .sidebar__content.scroll-shadow {
175
+ @apply scrollbar-thin;
176
+
177
+ /* The fade is a `mask-image` over the rows, which in Forced Colors Mode means system-coloured
178
+ * text dissolving into the background. That mode is chosen by people who need the opposite of
179
+ * subtlety, so the affordance is dropped rather than softened — the scrollbar still says the
180
+ * region scrolls. */
181
+ @media (forced-colors: active) {
182
+ mask-image: none;
183
+ -webkit-mask-image: none;
184
+ }
185
+ }
186
+
187
+ .sidebar__footer {
188
+ @apply flex shrink-0 flex-col gap-2 p-2;
189
+ }
190
+
191
+ /* ==========================================================================
192
+ Groups
193
+ ========================================================================== */
194
+
195
+ .sidebar__group {
196
+ @apply flex w-full min-w-0 flex-col gap-0.5;
197
+ }
198
+
199
+ /* Matches `.header`, which is the same muted label above the same kind of list. */
200
+ .sidebar__group-label {
201
+ @apply w-full truncate px-2 pt-1.5 pb-1 text-start text-xs font-medium text-muted;
202
+ }
203
+
204
+ /* `sr-only` rather than `hidden`: the label is what tells a screen reader which group an item
205
+ * belongs to, and a collapsed sidebar is still being read. Taking it out of flow also takes it out
206
+ * of the group's `gap`, so the icons close up rather than leaving a hole where the label was. */
207
+ .sidebar__group-label[data-collapsed="true"] {
208
+ @apply sr-only;
209
+ }
210
+
211
+ /* Spacing only, and no width of its own. `separator.css` is imported under Layout, after this file
212
+ * under Navigation, so its `w-full` wins at equal specificity and nothing here can take it back —
213
+ * tailwind-merge cannot help either, because the width lives inside a BEM rule rather than in the
214
+ * class string. It needs no taking back: the rule runs the width of the content's padding box,
215
+ * which is exactly the box every item beside it already fills. */
216
+ .sidebar__separator {
217
+ @apply my-1;
218
+ }
219
+
220
+ /* ==========================================================================
221
+ Nav item
222
+ ========================================================================== */
223
+
224
+ .sidebar__item {
225
+ @apply relative flex min-h-9 w-full min-w-0 items-center justify-start gap-2 rounded-component px-2 text-sm font-medium no-underline outline-none no-highlight;
226
+
227
+ /* The row's fill and label, per state. A caller retuning the resting pair used to flatten the
228
+ * current page's along with it, since one declaration cannot tell the states apart. */
229
+ --sidebar-item-bg: transparent;
230
+ --sidebar-item-bg-hover: var(--state-layer);
231
+ --sidebar-item-bg-pressed: var(--sidebar-item-bg-hover);
232
+ --sidebar-item-bg-current: var(--accent-soft);
233
+ --sidebar-item-fg: var(--foreground);
234
+ --sidebar-item-fg-current: var(--accent-soft-foreground);
235
+
236
+ background-color: var(--sidebar-item-bg);
237
+ color: var(--sidebar-item-fg);
238
+
239
+ /* Cursor */
240
+ cursor: var(--cursor-interactive);
241
+
242
+ transition:
243
+ background-color 150ms var(--ease-out),
244
+ color 150ms var(--ease-out);
245
+ @apply motion-reduce:transition-none;
246
+
247
+ /* Hover state */
248
+ @media (hover: hover) {
249
+ &:hover,
250
+ &[data-hovered="true"] {
251
+ background-color: var(--sidebar-item-bg-hover);
252
+ }
253
+ }
254
+
255
+ /* Focus visible state (keyboard focus only) */
256
+ &:focus-visible,
257
+ &[data-focus-visible="true"] {
258
+ @apply status-focused;
259
+ }
260
+
261
+ /* Pressed state */
262
+ &:active,
263
+ &[data-pressed="true"] {
264
+ background-color: var(--sidebar-item-bg-pressed);
265
+ }
266
+
267
+ /* The current page. Its whole job is to be the one item that stands out, so the mode flattening
268
+ * the fill onto the page would take exactly the distinction away — hence `forced-selected`,
269
+ * which is what the rest of the library reaches for to say "this one". */
270
+ &[data-current="true"] {
271
+ background-color: var(--sidebar-item-bg-current);
272
+ color: var(--sidebar-item-fg-current);
273
+
274
+ @apply forced-selected;
275
+ }
276
+
277
+ /* Disabled state */
278
+ &[data-disabled="true"] {
279
+ @apply status-disabled;
280
+ }
281
+
282
+ /* Collapsed to the rail there is only the icon left to place, so it takes the centre and gives
283
+ * up the padding that was holding a label beside it. */
284
+ &[data-collapsed="true"] {
285
+ @apply justify-center px-0;
286
+ }
287
+ }
288
+
289
+ .sidebar__item-icon {
290
+ @apply flex size-4 shrink-0 items-center justify-center;
291
+
292
+ svg {
293
+ @apply size-4;
294
+ }
295
+ }
296
+
297
+ .sidebar__item-label {
298
+ @apply min-w-0 flex-1 truncate text-start;
299
+ }
300
+
301
+ /* Hidden the same way the group label is, and for the same reason — this *is* the item's
302
+ * accessible name. */
303
+ .sidebar__item-label[data-collapsed="true"] {
304
+ @apply sr-only;
305
+ }
306
+
307
+ .sidebar__item-trailing {
308
+ @apply flex shrink-0 items-center;
309
+ }
310
+
311
+ .sidebar__item-trailing[data-collapsed="true"] {
312
+ @apply hidden;
313
+ }
314
+
315
+ /* The tooltip's own trigger is `inline-block`, and inside the nav's column that would shrink the
316
+ * item it wraps down to its contents. An item is a full-width row and has to stay one whether or
317
+ * not it has a tooltip hanging off it. */
318
+ .sidebar__item-tooltip {
319
+ @apply block w-full;
320
+ }
321
+
322
+ /* ==========================================================================
323
+ Nested items
324
+ ========================================================================== */
325
+
326
+ /* Points along the row while the children are shut and turns to point down at them once they are
327
+ * showing — a quarter turn, not the half turn `disclosure.css` uses.
328
+ *
329
+ * The difference is deliberate. A disclosure's indicator sits beside a heading and only has to say
330
+ * open or shut; this one sits at the end of a nav row and has a direction to mean, so the shut
331
+ * state points the way the rows will come from. `rtl` turns it the other way for the same reason
332
+ * it is `ms-auto` and not `ml-auto`. */
333
+ .sidebar__item-indicator {
334
+ @apply ms-auto size-3.5 shrink-0 -rotate-90 text-muted rtl:rotate-90;
335
+
336
+ /**
337
+ * Transitions
338
+ */
339
+ @apply transition duration-250 motion-reduce:transition-none;
340
+
341
+ &[data-expanded="true"] {
342
+ @apply rotate-0;
343
+ }
344
+ }
345
+
346
+ /* Nothing left to point at once the sidebar is a rail: the children are not rendered there at all,
347
+ * and a chevron promising something that never opens is worse than no chevron. */
348
+ .sidebar__item-indicator[data-collapsed="true"] {
349
+ @apply hidden;
350
+ }
351
+
352
+ /* The rows under a nav item, indented and gathered by a line down the gutter they share. One border
353
+ * on the container rather than one per row, so the line runs unbroken from the first child to the
354
+ * last however many there are. */
355
+ .sidebar__sub-menu {
356
+ /* The row's own inline padding plus half its icon, which lands the line on the icon's centre.
357
+ * Written from those two rather than as the number they come to, so retuning either moves the
358
+ * line with it instead of leaving it a few pixels off. */
359
+ --sidebar-sub-indent: calc(var(--spacing) * 2 + calc(var(--spacing) * 4) / 2);
360
+
361
+ @apply flex min-w-0 flex-col gap-0.5 border-s border-separator;
362
+
363
+ margin-inline-start: var(--sidebar-sub-indent);
364
+ padding-inline-start: calc(var(--spacing) * 2);
365
+
366
+ /* A border is one of the few things Forced Colors Mode recolours rather than flattens, so the
367
+ * line survives on its own — but only while it has a colour left to recolour. */
368
+ @media (forced-colors: active) {
369
+ border-inline-start-color: CanvasText;
370
+ }
371
+ }
372
+
373
+ /*
374
+ * The collapse is a JS contract, not a CSS one: `useDisclosurePanel` writes both the height and the
375
+ * `hidden` attribute, and nothing in any stylesheet ever sets `--disclosure-panel-height`. The
376
+ * variable keeps that composable's name rather than taking a sidebar one, because the composable is
377
+ * what fills it in — a `--sidebar-sub-menu-height` here would stay empty forever.
378
+ *
379
+ * Held apart from the base because a submenu that is always open has nobody writing the variable,
380
+ * and `height: var(--disclosure-panel-height)` with nothing behind it would leave the rows sitting
381
+ * under an `opacity: 0` that never lifts.
382
+ */
383
+ .sidebar__sub-menu--collapsible {
384
+ opacity: 0;
385
+ height: var(--disclosure-panel-height);
386
+ overflow: clip;
387
+
388
+ transition:
389
+ height 200ms var(--ease-out-quad),
390
+ opacity 200ms var(--ease-out);
391
+ @apply motion-reduce:transition-none;
392
+
393
+ &[data-expanded="true"] {
394
+ will-change: height, opacity;
395
+ opacity: 1;
396
+ }
397
+ }
398
+
399
+ /* A child row carries no icon, so it gives up the gap that was holding one and drops to the weight
400
+ * of what it is — a destination under a heading rather than another heading. */
401
+ .sidebar__item--sub {
402
+ @apply gap-0 font-normal;
403
+ }
404
+
405
+ /* ==========================================================================
406
+ Inset — the content beside the sidebar
407
+ ========================================================================== */
408
+
409
+ /* `min-w-0` for the reason `.splitter__panel` documents: without it a wide table inside the inset
410
+ * sets a floor under the whole shell and the sidebar can never be given its width back. */
411
+ .sidebar__inset {
412
+ @apply relative flex min-w-0 flex-1 flex-col overflow-hidden bg-background;
413
+ }
414
+
415
+ /* The page beside a bare panel, and now the thing that paints. `--surface` rather than the base
416
+ * rule's `--background`: this stopped being the page's background the moment it became a plate
417
+ * lying on one, which is what `--surface` names.
418
+ *
419
+ * The first of this component's modifiers to *add* paint rather than drop it, which is what puts it
420
+ * in front of the forced-colors sweep as a box with something to lose. Under the mode the fill
421
+ * flattens onto the page behind it and the shadow goes, leaving the contents sitting loose — so it
422
+ * takes the same inset outline every container that paints takes, offset inwards so nothing moves
423
+ * and the radius above still holds it. */
424
+ .sidebar__inset--card {
425
+ @apply bg-surface text-surface-foreground shadow-surface;
426
+
427
+ margin: var(--sidebar-card-gap);
428
+ border-radius: var(--sidebar-card-radius);
429
+
430
+ @media (forced-colors: active) {
431
+ outline: 1px solid CanvasText;
432
+ outline-offset: -1px;
433
+ }
434
+ }
435
+
436
+ /* ==========================================================================
437
+ Rail — the edge strip
438
+ ========================================================================== */
439
+
440
+ /* Built like `.splitter__handle`, down to the `::after`: the flex item is the gutter and paints
441
+ * nothing, so the line can thicken on hover without the gutter changing width and shifting the
442
+ * whole page by a pixel. */
443
+ .sidebar__rail {
444
+ @apply relative z-10 flex shrink-0 grow-0 items-center justify-center self-stretch;
445
+ @apply bg-transparent outline-none select-none no-highlight;
446
+
447
+ /* The gesture is ours; the browser must not also pan or scroll from a drag on this. */
448
+ @apply touch-none;
449
+
450
+ inline-size: var(--sidebar-rail-gutter);
451
+ cursor: var(--cursor-interactive);
452
+
453
+ /* On the host, not in `::after` where the transition lives: the variant reaches a pseudo-element
454
+ * from its originating element, and appending its condition after `::after` matches nothing. */
455
+ @apply motion-reduce:transition-none;
456
+
457
+ &::after {
458
+ @apply pointer-events-none absolute inset-y-0 rounded-sm content-[''];
459
+
460
+ background-color: var(--sidebar-rail-line);
461
+ inline-size: var(--sidebar-rail-gutter);
462
+
463
+ /* Flattened to `Canvas` the line stops existing rather than weakening — the whole part *is*
464
+ * its background, the trap `separator.css` and `splitter.css` both restate their colour for. */
465
+ @media (forced-colors: active) {
466
+ background-color: CanvasText;
467
+ }
468
+
469
+ transition:
470
+ background-color 150ms var(--ease-out),
471
+ inline-size 150ms var(--ease-out);
472
+ }
473
+
474
+ &:hover::after,
475
+ &[data-hovered="true"]::after,
476
+ &[data-dragging="true"]::after {
477
+ inline-size: calc(var(--sidebar-rail-gutter) * 2);
478
+ }
479
+
480
+ /* Delayed on the way in and immediate on the way out. A grab area many times the width of the
481
+ * line is one a pointer crosses on its way to the page, and a rail that flares at every crossing
482
+ * reads as a twitch rather than an invitation. */
483
+ &:hover::after,
484
+ &[data-hovered="true"]::after {
485
+ background-color: var(--sidebar-rail-line-hover);
486
+ transition-delay: var(--sidebar-highlight-delay);
487
+ }
488
+
489
+ /* A drag under way is not in question, so it skips the wait. Last, because a drag usually starts
490
+ * from a hover and has to win. */
491
+ &[data-dragging="true"]::after {
492
+ background-color: var(--sidebar-rail-line-dragging);
493
+ transition-delay: 0ms;
494
+ }
495
+
496
+ /* Every lit colour flattens to the same `Canvas` as the resting one, so without this the two are
497
+ * byte-identical under the mode. One block for all of them, after the colours it overrides. */
498
+ @media (forced-colors: active) {
499
+ &:hover::after,
500
+ &[data-hovered="true"]::after,
501
+ &[data-dragging="true"]::after {
502
+ background-color: Highlight;
503
+ }
504
+ }
505
+
506
+ &:focus-visible,
507
+ &[data-focus-visible="true"] {
508
+ @apply status-focused;
509
+ }
510
+
511
+ &[data-disabled="true"] {
512
+ @apply status-disabled;
513
+
514
+ /* `status-disabled` reaches `color` and `border-color`, and the line is neither. */
515
+ &::after {
516
+ @media (forced-colors: active) {
517
+ background-color: GrayText;
518
+ }
519
+ }
520
+ }
521
+ }
522
+
523
+ /* Only a rail that resizes says so with the cursor. One that merely toggles is a button, and
524
+ * `--cursor-interactive` on the base rule already covers it. */
525
+ .sidebar__rail--resizable {
526
+ @apply cursor-col-resize;
527
+ }
528
+
529
+ /* Nothing left for the line to divide once something else is drawing that division — a card's own
530
+ * edge, or the gap it stands in — so it goes quiet in every state.
531
+ *
532
+ * Re-pointing the properties rather than restating the state rules, the shape `.input--secondary`
533
+ * takes: a `background-color` here would lose to `.sidebar__rail:hover::after` on specificity, and
534
+ * restating that set drifts the day a state is added to it.
535
+ *
536
+ * All three, and the third is not redundant. `--sidebar-rail-line-dragging` is declared on the
537
+ * shell as a `var()` of the hover one, so it is substituted there, against the shell's value; only
538
+ * the resolved colour reaches this far. Re-pointing hover here arrives too late for it, and a drag
539
+ * would still flare.
540
+ *
541
+ * The line comes back under Forced Colors Mode, on purpose: those blocks paint system keywords,
542
+ * which are not a component's to quiet. */
543
+ .sidebar__rail--quiet {
544
+ --sidebar-rail-line: transparent;
545
+ --sidebar-rail-line-hover: transparent;
546
+ --sidebar-rail-line-dragging: transparent;
547
+ }
548
+
549
+ /* The invisible hit area, overhanging the hairline on both sides. A negative `inset` rather than a
550
+ * transparent border, because Forced Colors Mode turns a transparent border-color opaque and every
551
+ * rail would render as a solid block. */
552
+ .sidebar__rail-target {
553
+ @apply absolute inset-0 bg-transparent;
554
+
555
+ inset-inline: calc((var(--sidebar-rail-target) - var(--sidebar-rail-gutter)) / -2);
556
+ }
557
+
558
+ /* ==========================================================================
559
+ Trigger
560
+ ========================================================================== */
561
+
562
+ .sidebar__trigger {
563
+ @apply inline-flex size-8 shrink-0 items-center justify-center rounded-component outline-none select-none no-highlight;
564
+
565
+ --sidebar-trigger-bg: transparent;
566
+ --sidebar-trigger-bg-hover: var(--state-layer);
567
+ --sidebar-trigger-fg: var(--muted);
568
+ --sidebar-trigger-fg-hover: var(--foreground);
569
+
570
+ background-color: var(--sidebar-trigger-bg);
571
+ color: var(--sidebar-trigger-fg);
572
+
573
+ /* Cursor */
574
+ cursor: var(--cursor-interactive);
575
+
576
+ transition:
577
+ background-color 150ms var(--ease-out),
578
+ color 150ms var(--ease-out);
579
+ @apply motion-reduce:transition-none;
580
+
581
+ svg {
582
+ @apply size-4;
583
+ }
584
+
585
+ /* Hover state */
586
+ @media (hover: hover) {
587
+ &:hover,
588
+ &[data-hovered="true"] {
589
+ background-color: var(--sidebar-trigger-bg-hover);
590
+ color: var(--sidebar-trigger-fg-hover);
591
+ }
592
+ }
593
+
594
+ /* Focus visible state (keyboard focus only) */
595
+ &:focus-visible,
596
+ &[data-focus-visible="true"] {
597
+ @apply status-focused;
598
+ }
599
+
600
+ /* Disabled state */
601
+ &[data-disabled="true"] {
602
+ @apply status-disabled;
603
+ }
604
+ }