@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.
- package/README.md +89 -5
- package/dist/components/accordion.css +6 -14
- package/dist/components/alert-dialog.css +1 -13
- package/dist/components/alert.css +1 -1
- package/dist/components/autocomplete/autocomplete.styles.d.ts +33 -0
- package/dist/components/autocomplete/autocomplete.styles.d.ts.map +1 -1
- package/dist/components/autocomplete/autocomplete.styles.js +12 -0
- package/dist/components/autocomplete.css +54 -36
- package/dist/components/breadcrumbs.css +9 -2
- package/dist/components/button-group.css +6 -10
- package/dist/components/button.css +10 -18
- package/dist/components/calendar-year-picker.css +20 -8
- package/dist/components/calendar.css +46 -21
- package/dist/components/card.css +1 -1
- package/dist/components/checkbox.css +54 -56
- package/dist/components/chip.css +3 -1
- package/dist/components/close-button.css +7 -6
- package/dist/components/color-area.css +6 -6
- package/dist/components/color-input-group/color-input-group.styles.d.ts +27 -0
- package/dist/components/color-input-group/color-input-group.styles.d.ts.map +1 -1
- package/dist/components/color-input-group/color-input-group.styles.js +6 -0
- package/dist/components/color-input-group.css +31 -28
- package/dist/components/color-picker.css +1 -5
- package/dist/components/color-slider.css +14 -12
- package/dist/components/color-swatch-picker.css +2 -85
- package/dist/components/color-swatch.css +1 -1
- package/dist/components/combo-box/combo-box.styles.d.ts +27 -0
- package/dist/components/combo-box/combo-box.styles.d.ts.map +1 -1
- package/dist/components/combo-box/combo-box.styles.js +17 -7
- package/dist/components/combo-box.css +34 -4
- package/dist/components/date-input-group/date-input-group.styles.d.ts +27 -0
- package/dist/components/date-input-group/date-input-group.styles.d.ts.map +1 -1
- package/dist/components/date-input-group/date-input-group.styles.js +6 -0
- package/dist/components/date-input-group.css +55 -33
- package/dist/components/date-picker.css +1 -5
- package/dist/components/date-range-picker.css +1 -5
- package/dist/components/disclosure.css +0 -4
- package/dist/components/drawer.css +4 -8
- package/dist/components/drop-zone/drop-zone.styles.d.ts +34 -0
- package/dist/components/drop-zone/drop-zone.styles.d.ts.map +1 -0
- package/dist/components/drop-zone/drop-zone.styles.js +8 -0
- package/dist/components/drop-zone/index.d.ts +2 -0
- package/dist/components/drop-zone/index.d.ts.map +1 -0
- package/dist/components/drop-zone/index.js +2 -0
- package/dist/components/drop-zone.css +78 -0
- package/dist/components/dropdown.css +1 -5
- package/dist/components/error-message.css +0 -4
- package/dist/components/field-error.css +0 -4
- package/dist/components/index.css +5 -0
- package/dist/components/index.d.ts +5 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/input/input.styles.d.ts +15 -0
- package/dist/components/input/input.styles.d.ts.map +1 -1
- package/dist/components/input/input.styles.js +6 -0
- package/dist/components/input-group/input-group.styles.d.ts +27 -0
- package/dist/components/input-group/input-group.styles.d.ts.map +1 -1
- package/dist/components/input-group/input-group.styles.js +6 -0
- package/dist/components/input-group.css +35 -38
- package/dist/components/input-otp.css +14 -26
- package/dist/components/input.css +23 -32
- package/dist/components/kbd.css +3 -1
- package/dist/components/label.css +7 -2
- package/dist/components/link.css +13 -11
- package/dist/components/list-box/list-box.styles.d.ts +15 -0
- package/dist/components/list-box/list-box.styles.d.ts.map +1 -1
- package/dist/components/list-box/list-box.styles.js +15 -5
- package/dist/components/list-box-item.css +8 -12
- package/dist/components/list-box.css +19 -0
- package/dist/components/menu/menu.styles.d.ts +19 -1
- package/dist/components/menu/menu.styles.d.ts.map +1 -1
- package/dist/components/menu/menu.styles.js +9 -1
- package/dist/components/menu-item.css +4 -10
- package/dist/components/menu.css +19 -0
- package/dist/components/meter.css +8 -16
- package/dist/components/modal.css +1 -13
- package/dist/components/number-field/number-field.styles.d.ts +27 -0
- package/dist/components/number-field/number-field.styles.d.ts.map +1 -1
- package/dist/components/number-field/number-field.styles.js +6 -0
- package/dist/components/number-field.css +83 -36
- package/dist/components/pagination.css +6 -10
- package/dist/components/popover.css +15 -5
- package/dist/components/progress-bar/progress-bar.styles.d.ts +36 -0
- package/dist/components/progress-bar/progress-bar.styles.d.ts.map +1 -1
- package/dist/components/progress-bar/progress-bar.styles.js +10 -0
- package/dist/components/progress-bar.css +82 -16
- package/dist/components/progress-circle.css +0 -4
- package/dist/components/radio.css +7 -9
- package/dist/components/range-calendar.css +73 -31
- package/dist/components/search-field/search-field.styles.d.ts +27 -0
- package/dist/components/search-field/search-field.styles.d.ts.map +1 -1
- package/dist/components/search-field/search-field.styles.js +6 -0
- package/dist/components/search-field.css +36 -29
- package/dist/components/segmented-control/index.d.ts +2 -0
- package/dist/components/segmented-control/index.d.ts.map +1 -0
- package/dist/components/segmented-control/index.js +2 -0
- package/dist/components/segmented-control/segmented-control.styles.d.ts +73 -0
- package/dist/components/segmented-control/segmented-control.styles.d.ts.map +1 -0
- package/dist/components/segmented-control/segmented-control.styles.js +27 -0
- package/dist/components/segmented-control.css +188 -0
- package/dist/components/select/select.styles.d.ts +33 -0
- package/dist/components/select/select.styles.d.ts.map +1 -1
- package/dist/components/select/select.styles.js +12 -0
- package/dist/components/select.css +47 -33
- package/dist/components/sidebar/index.d.ts +2 -0
- package/dist/components/sidebar/index.d.ts.map +1 -0
- package/dist/components/sidebar/index.js +2 -0
- package/dist/components/sidebar/sidebar.styles.d.ts +226 -0
- package/dist/components/sidebar/sidebar.styles.d.ts.map +1 -0
- package/dist/components/sidebar/sidebar.styles.js +65 -0
- package/dist/components/sidebar.css +604 -0
- package/dist/components/slider.css +10 -11
- package/dist/components/splitter/index.d.ts +2 -0
- package/dist/components/splitter/index.d.ts.map +1 -0
- package/dist/components/splitter/index.js +2 -0
- package/dist/components/splitter/splitter.styles.d.ts +61 -0
- package/dist/components/splitter/splitter.styles.d.ts.map +1 -0
- package/dist/components/splitter/splitter.styles.js +26 -0
- package/dist/components/splitter.css +249 -0
- package/dist/components/switch.css +16 -15
- package/dist/components/table.css +72 -30
- package/dist/components/tabs.css +9 -20
- package/dist/components/tag.css +32 -24
- package/dist/components/textarea/textarea.styles.d.ts +15 -0
- package/dist/components/textarea/textarea.styles.d.ts.map +1 -1
- package/dist/components/textarea/textarea.styles.js +6 -0
- package/dist/components/textarea.css +23 -32
- package/dist/components/toast.css +4 -2
- package/dist/components/toggle-button-group.css +7 -11
- package/dist/components/toggle-button.css +9 -17
- package/dist/components/toolbar.css +1 -1
- package/dist/components/tooltip.css +15 -6
- package/dist/components/typography.css +12 -4
- package/dist/components/virtualizer/index.d.ts +2 -0
- package/dist/components/virtualizer/index.d.ts.map +1 -0
- package/dist/components/virtualizer/index.js +2 -0
- package/dist/components/virtualizer/virtualizer.styles.d.ts +44 -0
- package/dist/components/virtualizer/virtualizer.styles.d.ts.map +1 -0
- package/dist/components/virtualizer/virtualizer.styles.js +15 -0
- package/dist/components/virtualizer.css +72 -0
- package/dist/index.js +6 -1
- package/dist/ropav.min.css +2 -2
- package/dist/themes/airbnb.css +11 -4
- package/dist/themes/all.css +1 -0
- package/dist/themes/coinbase.css +11 -4
- package/dist/themes/default.css +40 -8
- package/dist/themes/discord.css +11 -4
- package/dist/themes/hero.css +282 -0
- package/dist/themes/lavender.css +11 -4
- package/dist/themes/mint.css +11 -4
- package/dist/themes/netflix.css +11 -4
- package/dist/themes/rabbit.css +11 -4
- package/dist/themes/shared/theme.css +20 -2
- package/dist/themes/sky.css +11 -4
- package/dist/themes/spotify.css +11 -4
- package/dist/themes/uber.css +11 -4
- package/dist/utilities/index.css +27 -0
- package/package.json +1 -1
- package/src/components/autocomplete/autocomplete.styles.ts +12 -0
- package/src/components/color-input-group/color-input-group.styles.ts +10 -0
- package/src/components/combo-box/combo-box.styles.ts +12 -0
- package/src/components/date-input-group/date-input-group.styles.ts +10 -0
- package/src/components/drop-zone/drop-zone.styles.ts +12 -0
- package/src/components/drop-zone/index.ts +1 -0
- package/src/components/index.ts +5 -0
- package/src/components/input/input.styles.ts +6 -0
- package/src/components/input-group/input-group.styles.ts +10 -0
- package/src/components/list-box/list-box.styles.ts +6 -0
- package/src/components/menu/menu.styles.ts +10 -0
- package/src/components/number-field/number-field.styles.ts +10 -0
- package/src/components/progress-bar/progress-bar.styles.ts +14 -0
- package/src/components/search-field/search-field.styles.ts +10 -0
- package/src/components/segmented-control/index.ts +1 -0
- package/src/components/segmented-control/segmented-control.styles.ts +37 -0
- package/src/components/select/select.styles.ts +12 -0
- package/src/components/sidebar/index.ts +1 -0
- package/src/components/sidebar/sidebar.styles.ts +117 -0
- package/src/components/splitter/index.ts +1 -0
- package/src/components/splitter/splitter.styles.ts +44 -0
- package/src/components/textarea/textarea.styles.ts +6 -0
- package/src/components/virtualizer/index.ts +1 -0
- 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
|
+
}
|