@uncinq/css-components 1.8.1 → 1.9.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/css/components/button.css +3 -0
- package/css/components/drawer.css +46 -153
- package/css/components/dropdown.css +1 -0
- package/css/components/map.css +1 -1
- package/css/components/modal.css +45 -140
- package/css/components/nav.css +6 -11
- package/css/components/pagination.css +1 -1
- package/css/components/panel.css +432 -0
- package/css/index.css +1 -0
- package/package.json +1 -1
|
@@ -1,106 +1,51 @@
|
|
|
1
1
|
/* components/drawer.css */
|
|
2
2
|
/*
|
|
3
|
-
* .drawer —
|
|
3
|
+
* .drawer — panel sliding in from an edge.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* .hiding — panel becoming hidden (close transition)
|
|
9
|
-
* aria-hidden="true" when closed
|
|
10
|
-
* body.drawer-open when panel is open
|
|
5
|
+
* A variant is pure configuration: this file declares nothing but --panel-*
|
|
6
|
+
* tokens. components/panel.css owns every rule — structure, placement,
|
|
7
|
+
* open/closed contract and backdrop.
|
|
11
8
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* Works on a <dialog> opened with showModal() and on any element carrying
|
|
10
|
+
* popover="auto", opened with showPopover(). Both land in the top layer and
|
|
11
|
+
* get the native ::backdrop. Reach for the second form when the panel is not
|
|
12
|
+
* always modal: combined with .panel-inline-* it becomes a region of the
|
|
13
|
+
* page above a breakpoint, which a <dialog> cannot be without claiming
|
|
14
|
+
* role="dialog" the whole time.
|
|
14
15
|
*
|
|
15
16
|
* Position variants: .drawer-start | .drawer-end | .drawer-top | .drawer-bottom
|
|
17
|
+
* Inline variants: .panel-inline-* — see components/panel.css
|
|
16
18
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* <div class="header">
|
|
20
|
-
* <span class="title">...</span>
|
|
21
|
-
* <button class="js-drawer-close btn">...</button>
|
|
22
|
-
* </div>
|
|
23
|
-
* <div class="content">...</div>
|
|
24
|
-
* </div>
|
|
19
|
+
* Triggers: .js-dialog-toggle[data-target="#id"]
|
|
20
|
+
* Close: .js-dialog-close inside the panel, Escape, or a click outside
|
|
25
21
|
*/
|
|
26
22
|
@layer components {
|
|
27
|
-
|
|
28
|
-
/* ── Panel ───────────────────────────────────────────────────────── */
|
|
29
|
-
|
|
30
23
|
.drawer {
|
|
31
|
-
|
|
32
|
-
--
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
color: var(--drawer-color-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
/* Visible during transitions and when open */
|
|
58
|
-
&.show,
|
|
59
|
-
&.showing,
|
|
60
|
-
&.hiding {
|
|
61
|
-
visibility: visible;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/* Fully open — remove transform */
|
|
65
|
-
&.show {
|
|
66
|
-
transform: none;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/* ── Header ────────────────────────────────────────────────────── */
|
|
70
|
-
|
|
71
|
-
& > .header {
|
|
72
|
-
align-items: center;
|
|
73
|
-
border-block-end: var(--drawer-border-width) var(--drawer-border-style) var(--drawer-border-color);
|
|
74
|
-
display: flex;
|
|
75
|
-
flex-shrink: 0;
|
|
76
|
-
justify-content: space-between;
|
|
77
|
-
padding-block: var(--drawer-header-padding-block, var(--drawer-padding-block));
|
|
78
|
-
padding-inline: var(--drawer-header-padding-inline, var(--drawer-padding-inline));
|
|
79
|
-
|
|
80
|
-
& .title {
|
|
81
|
-
font-size: var(--font-size-md);
|
|
82
|
-
font-weight: var(--font-weight-heading);
|
|
83
|
-
margin: 0;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/* ── Body ──────────────────────────────────────────────────────── */
|
|
88
|
-
|
|
89
|
-
& > .content {
|
|
90
|
-
flex-grow: 1;
|
|
91
|
-
overflow-y: auto;
|
|
92
|
-
padding-block: var(--drawer-header-padding-block, var(--drawer-padding-block));
|
|
93
|
-
padding-inline: var(--drawer-header-padding-inline, var(--drawer-padding-inline));
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/* ── Footer ──────────────────────────────────────────────────────── */
|
|
97
|
-
|
|
98
|
-
& > .footer {
|
|
99
|
-
border-block-start: var(--drawer-border-width) var(--drawer-border-style) var(--drawer-border-color);
|
|
100
|
-
flex-shrink: 0;
|
|
101
|
-
padding-block: var(--drawer-header-padding-block, var(--drawer-padding-block));
|
|
102
|
-
padding-inline: var(--drawer-header-padding-inline, var(--drawer-padding-inline));
|
|
103
|
-
}
|
|
24
|
+
/* Structure */
|
|
25
|
+
--panel-backdrop-blur: var(--drawer-backdrop-blur);
|
|
26
|
+
--panel-backdrop-color: var(--drawer-backdrop-color);
|
|
27
|
+
--panel-border-style: var(--drawer-border-style);
|
|
28
|
+
--panel-border-width: var(--drawer-border-width);
|
|
29
|
+
--panel-color-background: var(--drawer-color-background);
|
|
30
|
+
--panel-color-border: var(--drawer-color-border);
|
|
31
|
+
--panel-color-text: var(--drawer-color-text);
|
|
32
|
+
--panel-content-padding-block: var(--drawer-content-padding-block, var(--drawer-padding-block));
|
|
33
|
+
--panel-content-padding-inline: var(--drawer-content-padding-inline, var(--drawer-padding-inline));
|
|
34
|
+
--panel-footer-padding-block: var(--drawer-footer-padding-block, var(--drawer-padding-block));
|
|
35
|
+
--panel-footer-padding-inline: var(--drawer-footer-padding-inline, var(--drawer-padding-inline));
|
|
36
|
+
--panel-header-padding-block: var(--drawer-header-padding-block, var(--drawer-padding-block));
|
|
37
|
+
--panel-header-padding-inline: var(--drawer-header-padding-inline, var(--drawer-padding-inline));
|
|
38
|
+
--panel-shadow: var(--drawer-shadow);
|
|
39
|
+
--panel-transition-duration: var(--drawer-transition-duration);
|
|
40
|
+
--panel-transition-easing: var(--drawer-transition-easing);
|
|
41
|
+
|
|
42
|
+
/* Placement — two edges pinned, the opposite ones auto. The margin stays
|
|
43
|
+
at 0 so the UA rules that centre a modal <dialog> do not apply. */
|
|
44
|
+
--panel-inline-size: var(--drawer-width);
|
|
45
|
+
--panel-inset-block: var(--drawer-top) var(--drawer-bottom);
|
|
46
|
+
--panel-inset-inline: var(--drawer-start) var(--drawer-end);
|
|
47
|
+
--panel-max-inline-size: 100%;
|
|
48
|
+
--panel-translate: var(--drawer-translate);
|
|
104
49
|
}
|
|
105
50
|
|
|
106
51
|
/* ── Position variants ───────────────────────────────────────────── */
|
|
@@ -112,80 +57,28 @@
|
|
|
112
57
|
}
|
|
113
58
|
|
|
114
59
|
.drawer-end {
|
|
115
|
-
--drawer-start: auto;
|
|
116
60
|
--drawer-end: 0;
|
|
61
|
+
--drawer-start: auto;
|
|
117
62
|
--drawer-translate: translateX(100%);
|
|
118
63
|
}
|
|
119
64
|
|
|
120
65
|
.drawer-top {
|
|
66
|
+
--drawer-bottom: auto;
|
|
121
67
|
--drawer-end: 0;
|
|
122
68
|
--drawer-start: 0;
|
|
123
69
|
--drawer-top: 0;
|
|
124
70
|
--drawer-translate: translateY(-100%);
|
|
125
|
-
|
|
71
|
+
--panel-block-size: var(--drawer-height);
|
|
72
|
+
--panel-inline-size: auto;
|
|
126
73
|
}
|
|
127
74
|
|
|
128
75
|
.drawer-bottom {
|
|
76
|
+
--drawer-bottom: 0;
|
|
129
77
|
--drawer-end: 0;
|
|
130
78
|
--drawer-start: 0;
|
|
131
|
-
--drawer-
|
|
79
|
+
--drawer-top: auto;
|
|
132
80
|
--drawer-translate: translateY(100%);
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
/* ── Responsive variants (static above breakpoint) ──────────────── */
|
|
137
|
-
|
|
138
|
-
.drawer-tablet {
|
|
139
|
-
@media (--tablet) {
|
|
140
|
-
box-shadow: none;
|
|
141
|
-
overflow: visible;
|
|
142
|
-
position: static;
|
|
143
|
-
transform: none;
|
|
144
|
-
transition: none;
|
|
145
|
-
visibility: visible;
|
|
146
|
-
width: auto;
|
|
147
|
-
z-index: auto;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
.drawer-laptop {
|
|
152
|
-
@media (--laptop) {
|
|
153
|
-
box-shadow: none;
|
|
154
|
-
overflow: visible;
|
|
155
|
-
position: static;
|
|
156
|
-
transform: none;
|
|
157
|
-
transition: none;
|
|
158
|
-
visibility: visible;
|
|
159
|
-
width: auto;
|
|
160
|
-
z-index: auto;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/* ── Backdrop ────────────────────────────────────────────────────── */
|
|
165
|
-
|
|
166
|
-
.drawer-backdrop {
|
|
167
|
-
backdrop-filter: blur(var(--drawer-backdrop-blur));
|
|
168
|
-
background-color: var(--drawer-backdrop-color);
|
|
169
|
-
inset: 0;
|
|
170
|
-
position: fixed;
|
|
171
|
-
z-index: calc(var(--drawer-z-index) - 1);
|
|
172
|
-
|
|
173
|
-
&.fade {
|
|
174
|
-
opacity: 0;
|
|
175
|
-
|
|
176
|
-
@media (prefers-reduced-motion: no-preference) {
|
|
177
|
-
transition: opacity var(--drawer-transition-duration) var(--drawer-transition-easing);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
&.show {
|
|
182
|
-
opacity: 1;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/* ── Body scroll lock ────────────────────────────────────────────── */
|
|
187
|
-
|
|
188
|
-
body.drawer-open {
|
|
189
|
-
overflow: hidden;
|
|
81
|
+
--panel-block-size: var(--drawer-height);
|
|
82
|
+
--panel-inline-size: auto;
|
|
190
83
|
}
|
|
191
84
|
}
|
package/css/components/map.css
CHANGED
package/css/components/modal.css
CHANGED
|
@@ -1,154 +1,59 @@
|
|
|
1
1
|
/* components/modal.css */
|
|
2
2
|
/*
|
|
3
|
-
* .modal —
|
|
3
|
+
* .modal — centred panel.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* A variant is pure configuration: this file declares nothing but --panel-*
|
|
6
|
+
* tokens. components/panel.css owns every rule — structure, placement,
|
|
7
|
+
* open/closed contract and backdrop.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* Works on a <dialog> opened with showModal() and on any element carrying
|
|
10
|
+
* popover="auto", opened with showPopover(). Both land in the top layer and
|
|
11
|
+
* get the native ::backdrop. Reach for the second form only when the panel is not
|
|
12
|
+
* always modal, typically when .panel-inline-* turns it into a region of the
|
|
13
|
+
* page above a breakpoint.
|
|
13
14
|
*
|
|
14
15
|
* Per-instance sizing: override --modal-max-width on the element.
|
|
15
16
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* <div class="header">
|
|
19
|
-
* <p class="title" id="navigationLabel">...</p>
|
|
20
|
-
* <button class="btn-close js-dialog-close">...</button>
|
|
21
|
-
* </div>
|
|
22
|
-
* <div class="content">...</div>
|
|
23
|
-
* <div class="footer">...</div>
|
|
24
|
-
* </dialog>
|
|
17
|
+
* Triggers: .js-dialog-toggle[data-target="#id"]
|
|
18
|
+
* Close: .js-dialog-close inside the panel, Escape, or a click outside
|
|
25
19
|
*/
|
|
26
20
|
@layer components {
|
|
27
|
-
|
|
28
|
-
/* ── Panel ───────────────────────────────────────────────────────── */
|
|
29
|
-
|
|
30
21
|
.modal {
|
|
31
|
-
|
|
32
|
-
--
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
border:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
color: var(--modal-color-text);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
22
|
+
/* Structure */
|
|
23
|
+
--panel-backdrop-blur: var(--modal-backdrop-blur);
|
|
24
|
+
--panel-backdrop-color: var(--modal-backdrop-color);
|
|
25
|
+
--panel-border-style: var(--modal-border-style);
|
|
26
|
+
--panel-border-width: var(--modal-border-width);
|
|
27
|
+
--panel-color-background: var(--modal-color-background);
|
|
28
|
+
--panel-color-border: var(--modal-color-border);
|
|
29
|
+
--panel-color-text: var(--modal-color-text);
|
|
30
|
+
--panel-content-padding-block: var(--modal-content-padding-block, var(--modal-padding-block));
|
|
31
|
+
--panel-content-padding-inline: var(--modal-content-padding-inline, var(--modal-padding-inline));
|
|
32
|
+
--panel-footer-padding-block: var(--modal-footer-padding-block, var(--modal-padding-block));
|
|
33
|
+
--panel-footer-padding-inline: var(--modal-footer-padding-inline, var(--modal-padding-inline));
|
|
34
|
+
--panel-header-padding-block: var(--modal-header-padding-block, var(--modal-padding-block));
|
|
35
|
+
--panel-header-padding-inline: var(--modal-header-padding-inline, var(--modal-padding-inline));
|
|
36
|
+
--panel-radius: var(--modal-radius);
|
|
37
|
+
--panel-shadow: var(--modal-shadow);
|
|
38
|
+
--panel-transition-duration: var(--modal-transition-duration);
|
|
39
|
+
--panel-transition-easing: var(--modal-transition-easing);
|
|
40
|
+
|
|
41
|
+
/* Placement — inset 0 with auto margins centres the panel on both axes.
|
|
42
|
+
The height is fit-content and not auto: with both block insets set, an
|
|
43
|
+
auto height is resolved to fill the gap and the auto margins collapse to
|
|
44
|
+
0, so the panel would neither centre nor shrink to its content. The UA
|
|
45
|
+
dialog stylesheet uses fit-content for that very reason. */
|
|
46
|
+
--panel-block-size: fit-content;
|
|
47
|
+
--panel-inline-size: 100%;
|
|
48
|
+
--panel-inset-block: 0;
|
|
49
|
+
--panel-inset-inline: 0;
|
|
50
|
+
--panel-margin: auto;
|
|
42
51
|
/* min() replaces the mobile breakpoint: the panel always keeps
|
|
43
52
|
--modal-margin clear of the viewport edges, whatever the max is set to */
|
|
44
|
-
max-block-size: min(var(--modal-max-height), 100% - var(--modal-margin) * 2);
|
|
45
|
-
max-inline-size: min(var(--modal-max-width), 100% - var(--modal-margin) * 2);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
overflow: hidden;
|
|
50
|
-
padding: 0;
|
|
51
|
-
transform: var(--modal-translate);
|
|
52
|
-
|
|
53
|
-
/* display: flex would override the UA rule that hides a closed dialog */
|
|
54
|
-
&:not([open]) {
|
|
55
|
-
display: none;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/* Fully open — settle the panel */
|
|
59
|
-
&[open] {
|
|
60
|
-
opacity: 1;
|
|
61
|
-
transform: none;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/* allow-discrete keeps the dialog in the top layer while it fades out */
|
|
65
|
-
@media (prefers-reduced-motion: no-preference) {
|
|
66
|
-
transition-behavior: allow-discrete;
|
|
67
|
-
transition-duration: var(--modal-transition-duration);
|
|
68
|
-
transition-property: opacity, transform, display, overlay;
|
|
69
|
-
transition-timing-function: var(--modal-transition-easing);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/* ── Header ────────────────────────────────────────────────────── */
|
|
73
|
-
|
|
74
|
-
& > .header {
|
|
75
|
-
align-items: center;
|
|
76
|
-
border-block-end: var(--modal-border-width) var(--modal-border-style) var(--modal-border-color);
|
|
77
|
-
display: flex;
|
|
78
|
-
flex-shrink: 0;
|
|
79
|
-
justify-content: space-between;
|
|
80
|
-
padding-block: var(--modal-header-padding-block, var(--modal-padding-block));
|
|
81
|
-
padding-inline: var(--modal-header-padding-inline, var(--modal-padding-inline));
|
|
82
|
-
|
|
83
|
-
& .title {
|
|
84
|
-
font-size: var(--font-size-md);
|
|
85
|
-
font-weight: var(--font-weight-heading);
|
|
86
|
-
margin: 0;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/* ── Body ──────────────────────────────────────────────────────── */
|
|
91
|
-
|
|
92
|
-
& > .content {
|
|
93
|
-
flex-grow: 1;
|
|
94
|
-
overflow-y: auto;
|
|
95
|
-
overscroll-behavior: contain;
|
|
96
|
-
padding-block: var(--modal-content-padding-block, var(--modal-padding-block));
|
|
97
|
-
padding-inline: var(--modal-content-padding-inline, var(--modal-padding-inline));
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/* ── Footer ────────────────────────────────────────────────────── */
|
|
101
|
-
|
|
102
|
-
& > .footer {
|
|
103
|
-
border-block-start:var(--modal-border-width) var(--modal-border-style) var(--modal-border-color);
|
|
104
|
-
display: flex;
|
|
105
|
-
flex-direction: column;
|
|
106
|
-
flex-shrink: 0;
|
|
107
|
-
gap: var(--spacing-sm);
|
|
108
|
-
padding-block: var(--modal-footer-padding-block, var(--modal-padding-block));
|
|
109
|
-
padding-inline: var(--modal-footer-padding-inline, var(--modal-padding-inline));
|
|
110
|
-
|
|
111
|
-
@media (--tablet) {
|
|
112
|
-
flex-direction: row;
|
|
113
|
-
justify-content: flex-end;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/* ── Backdrop ────────────────────────────────────────────────────── */
|
|
119
|
-
|
|
120
|
-
.modal::backdrop {
|
|
121
|
-
backdrop-filter: blur(var(--modal-backdrop-blur));
|
|
122
|
-
background-color: var(--modal-backdrop-color);
|
|
123
|
-
opacity: 0;
|
|
124
|
-
|
|
125
|
-
@media (prefers-reduced-motion: no-preference) {
|
|
126
|
-
transition-behavior: allow-discrete;
|
|
127
|
-
transition-duration: var(--modal-transition-duration);
|
|
128
|
-
transition-property: opacity, display, overlay;
|
|
129
|
-
transition-timing-function: var(--modal-transition-easing);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
.modal[open]::backdrop {
|
|
134
|
-
opacity: 1;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/* Opening is a discrete display change — both need an explicit start state */
|
|
138
|
-
@starting-style {
|
|
139
|
-
.modal[open] {
|
|
140
|
-
opacity: 0;
|
|
141
|
-
transform: var(--modal-translate);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
.modal[open]::backdrop {
|
|
145
|
-
opacity: 0;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/* ── Page scroll lock ────────────────────────────────────────────── */
|
|
150
|
-
|
|
151
|
-
body:has(.modal[open]) {
|
|
152
|
-
overflow: hidden;
|
|
53
|
+
--panel-max-block-size: min(var(--modal-max-height), 100% - var(--modal-margin) * 2);
|
|
54
|
+
--panel-max-inline-size: min(var(--modal-max-width), 100% - var(--modal-margin) * 2);
|
|
55
|
+
--panel-min-block-size: var(--modal-min-height);
|
|
56
|
+
--panel-opacity: 0;
|
|
57
|
+
--panel-translate: var(--modal-translate);
|
|
153
58
|
}
|
|
154
59
|
}
|
package/css/components/nav.css
CHANGED
|
@@ -40,27 +40,22 @@
|
|
|
40
40
|
|
|
41
41
|
/* ── Links ───────────────────────────────────────────────────── */
|
|
42
42
|
|
|
43
|
-
a
|
|
43
|
+
a:where(:not(.btn)),
|
|
44
|
+
li > span {
|
|
45
|
+
color: var(--nav-color-text);
|
|
44
46
|
display: block;
|
|
45
47
|
padding-block: var(--nav-link-padding-block);
|
|
46
48
|
padding-inline: var(--nav-link-padding-inline);
|
|
47
49
|
text-transform: var(--nav-link-text-transform);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
a:not(.btn) {
|
|
51
|
-
color: var(--nav-color-text);
|
|
52
50
|
&.active {
|
|
53
51
|
--nav-color-text: var(--nav-color-text-active);
|
|
54
52
|
}
|
|
55
53
|
}
|
|
56
54
|
|
|
57
|
-
/*
|
|
58
|
-
|
|
55
|
+
/* ── Dropdown ───────────────────────────────────────────────────── */
|
|
56
|
+
|
|
57
|
+
.dropdown-toggle {
|
|
59
58
|
color: var(--nav-color-text);
|
|
60
|
-
display: block;
|
|
61
|
-
padding-block: var(--nav-link-padding-block);
|
|
62
|
-
padding-inline: var(--nav-link-padding-inline);
|
|
63
|
-
text-transform: var(--nav-link-text-transform);
|
|
64
59
|
}
|
|
65
60
|
|
|
66
61
|
/* ── Item highlighted ───────────────────────────────────────────────────── */
|
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
/* components/panel.css */
|
|
2
|
+
/*
|
|
3
|
+
* Shared skeleton for .modal and .drawer.
|
|
4
|
+
*
|
|
5
|
+
* Both are the same panel — header / content / footer, same open/closed
|
|
6
|
+
* contract — and differ only in where they sit: modal.css centres them,
|
|
7
|
+
* drawer.css sticks them to an edge.
|
|
8
|
+
*
|
|
9
|
+
* The tag picks the strategy, not the class. Either variant can be:
|
|
10
|
+
* - <dialog> opened with showModal() — [open], native ::backdrop, top layer,
|
|
11
|
+
* and the browser handles Escape, the focus trap and inertness;
|
|
12
|
+
* - any element carrying popover="auto", opened by components/dialog.js
|
|
13
|
+
* with showPopover() — same top layer, same ::backdrop, but no role of its
|
|
14
|
+
* own. Use this when the panel becomes a static region of the page above a
|
|
15
|
+
* breakpoint (see .panel-inline-*): a <dialog> is exposed as role="dialog"
|
|
16
|
+
* even when it is only styled as a sidebar, whereas dialog.js sets that
|
|
17
|
+
* role solely while the panel is open.
|
|
18
|
+
*
|
|
19
|
+
* Most selectors here are :where()-wrapped so they carry no specificity: the
|
|
20
|
+
* variants and the host components always win without escalating. The
|
|
21
|
+
* .panel-inline-* variants are the exception — they must beat .modal and
|
|
22
|
+
* .drawer, which they do on source order, so this file is imported last.
|
|
23
|
+
*
|
|
24
|
+
* Structure reads --panel-* tokens; each variant aliases them from its own
|
|
25
|
+
* namespace, so --modal-* and --drawer-* stay the public API.
|
|
26
|
+
*
|
|
27
|
+
* Expected HTML:
|
|
28
|
+
* <dialog class="modal" id="x" aria-labelledby="xLabel">
|
|
29
|
+
* <div class="header">
|
|
30
|
+
* <p class="title" id="xLabel">...</p>
|
|
31
|
+
* <button class="btn-close js-dialog-close"></button>
|
|
32
|
+
* </div>
|
|
33
|
+
* <div class="content">...</div>
|
|
34
|
+
* <div class="footer">...</div>
|
|
35
|
+
* </dialog>
|
|
36
|
+
*/
|
|
37
|
+
@layer components {
|
|
38
|
+
|
|
39
|
+
/* ── Open / closed ─────────────────────────────────────────────────
|
|
40
|
+
* `display` is only ever declared on a state selector: declaring it on the
|
|
41
|
+
* panel itself would beat the UA rule that hides a closed <dialog>. Both
|
|
42
|
+
* rules carry no specificity, so .panel-inline-* can put the panel back
|
|
43
|
+
* into the flow with a single class. */
|
|
44
|
+
|
|
45
|
+
:where(.modal, .drawer):not(:where([open], :popover-open)) {
|
|
46
|
+
display: none;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* :is() and not :where() on the state: this has to beat the base rule below,
|
|
50
|
+
which is 0,0,0 and comes later in the file. 0,1,0 wins on specificity
|
|
51
|
+
rather than on source order, and still leaves a host component — .toc
|
|
52
|
+
.drawer is 0,2,0 — room to override without escalating. */
|
|
53
|
+
:where(.modal, .drawer):is([open], :popover-open) {
|
|
54
|
+
display: flex;
|
|
55
|
+
opacity: 1;
|
|
56
|
+
transform: none;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* ── Panel ─────────────────────────────────────────────────────── */
|
|
60
|
+
|
|
61
|
+
:where(.modal, .drawer) {
|
|
62
|
+
--color-link: var(--panel-color-text);
|
|
63
|
+
--color-link-hover: var(--panel-color-text);
|
|
64
|
+
--nav-color-text: var(--color-link);
|
|
65
|
+
|
|
66
|
+
background-color: var(--panel-color-background);
|
|
67
|
+
border: 0;
|
|
68
|
+
border-radius: var(--panel-radius, 0);
|
|
69
|
+
box-shadow: var(--panel-shadow);
|
|
70
|
+
color: var(--panel-color-text);
|
|
71
|
+
flex-direction: column;
|
|
72
|
+
overflow: hidden;
|
|
73
|
+
/* padding lives on the sections, so a click on the panel itself is
|
|
74
|
+
always a click on the backdrop */
|
|
75
|
+
padding: 0;
|
|
76
|
+
|
|
77
|
+
/* Placement — every value comes from the variant. A modal centres itself
|
|
78
|
+
with inset 0 + margin auto; a drawer pins two edges and leaves the
|
|
79
|
+
others auto. Declaring position/inset explicitly also covers <dialog>,
|
|
80
|
+
whose UA rules would otherwise centre an edge panel. */
|
|
81
|
+
block-size: var(--panel-block-size, auto);
|
|
82
|
+
inline-size: var(--panel-inline-size, auto);
|
|
83
|
+
inset-block: var(--panel-inset-block, auto);
|
|
84
|
+
inset-inline: var(--panel-inset-inline, auto);
|
|
85
|
+
margin: var(--panel-margin, 0);
|
|
86
|
+
max-block-size: var(--panel-max-block-size, none);
|
|
87
|
+
max-inline-size: var(--panel-max-inline-size, none);
|
|
88
|
+
min-block-size: var(--panel-min-block-size, auto);
|
|
89
|
+
/* Closed state, so also the values the panel animates FROM — the
|
|
90
|
+
@starting-style block below restates these two. A drawer leaves opacity
|
|
91
|
+
alone and only slides; a modal sets 0 and fades as well. */
|
|
92
|
+
opacity: var(--panel-opacity, 1);
|
|
93
|
+
position: fixed;
|
|
94
|
+
transform: var(--panel-translate, none);
|
|
95
|
+
|
|
96
|
+
/* allow-discrete keeps the panel displayed while it transitions out, and
|
|
97
|
+
keeps a <dialog> in the top layer until the transition ends */
|
|
98
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
99
|
+
transition-behavior: allow-discrete;
|
|
100
|
+
transition-duration: var(--panel-transition-duration);
|
|
101
|
+
transition-property: display, opacity, overlay, transform;
|
|
102
|
+
transition-timing-function: var(--panel-transition-easing);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* ── Header ──────────────────────────────────────────────────── */
|
|
106
|
+
|
|
107
|
+
& > .header {
|
|
108
|
+
align-items: center;
|
|
109
|
+
border-block-end: var(--panel-border-width) var(--panel-border-style) var(--panel-color-border);
|
|
110
|
+
display: flex;
|
|
111
|
+
flex-shrink: 0;
|
|
112
|
+
justify-content: space-between;
|
|
113
|
+
padding-block: var(--panel-header-padding-block);
|
|
114
|
+
padding-inline: var(--panel-header-padding-inline);
|
|
115
|
+
|
|
116
|
+
& .title {
|
|
117
|
+
font-size: var(--font-size-md);
|
|
118
|
+
font-weight: var(--font-weight-heading);
|
|
119
|
+
margin: 0;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* ── Body ────────────────────────────────────────────────────── */
|
|
124
|
+
|
|
125
|
+
& > .content {
|
|
126
|
+
flex-grow: 1;
|
|
127
|
+
overflow-y: auto;
|
|
128
|
+
overscroll-behavior: contain;
|
|
129
|
+
padding-block: var(--panel-content-padding-block);
|
|
130
|
+
padding-inline: var(--panel-content-padding-inline);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* ── Footer ──────────────────────────────────────────────────── */
|
|
134
|
+
|
|
135
|
+
& > .footer {
|
|
136
|
+
border-block-start: var(--panel-border-width) var(--panel-border-style) var(--panel-color-border);
|
|
137
|
+
display: flex;
|
|
138
|
+
flex-direction: column;
|
|
139
|
+
flex-shrink: 0;
|
|
140
|
+
gap: var(--spacing-sm);
|
|
141
|
+
padding-block: var(--panel-footer-padding-block);
|
|
142
|
+
padding-inline: var(--panel-footer-padding-inline);
|
|
143
|
+
|
|
144
|
+
@media (--sm) {
|
|
145
|
+
flex-direction: row;
|
|
146
|
+
justify-content: flex-end;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/* ── Inline variants ───────────────────────────────────────────────
|
|
152
|
+
* Above the chosen breakpoint the panel stops being an overlay and becomes
|
|
153
|
+
* a region of the page: the host component then only styles what is its
|
|
154
|
+
* own (sticky behaviour, labels, nav layout). Written once here, so adding
|
|
155
|
+
* a property to .modal or .drawer means neutralising it right below rather
|
|
156
|
+
* than patching every host component.
|
|
157
|
+
*
|
|
158
|
+
* The panel must NOT be a <dialog> when one of these is used — see the
|
|
159
|
+
* strategy note at the top of this file. components/dialog.js also closes
|
|
160
|
+
* an open panel carrying one of these classes when the viewport width
|
|
161
|
+
* changes, since CSS alone cannot release the backdrop, the scroll lock
|
|
162
|
+
* and the inert background. */
|
|
163
|
+
|
|
164
|
+
.panel-inline-sm {
|
|
165
|
+
@media (--sm) {
|
|
166
|
+
/* Colours go too, not just geometry: a region of the page carries none
|
|
167
|
+
of the panel's chrome and takes the page's own text and link colours.
|
|
168
|
+
`inherit` on a custom property hands it back to the parent, which is
|
|
169
|
+
exactly what is wanted here — and it has to be done token by token.
|
|
170
|
+
Resetting --panel-color-text, which the three derive from, would make
|
|
171
|
+
them invalid at computed-value time rather than page-valued, and every
|
|
172
|
+
link would fall back to the surrounding text colour instead of the
|
|
173
|
+
site's link colour. Keep this list in step with the panel rule above. */
|
|
174
|
+
--color-link: inherit;
|
|
175
|
+
--color-link-hover: inherit;
|
|
176
|
+
--nav-color-text: inherit;
|
|
177
|
+
|
|
178
|
+
background-color: transparent;
|
|
179
|
+
block-size: auto;
|
|
180
|
+
border-radius: 0;
|
|
181
|
+
box-shadow: none;
|
|
182
|
+
color: inherit;
|
|
183
|
+
display: flex;
|
|
184
|
+
inline-size: auto;
|
|
185
|
+
inset-block: auto;
|
|
186
|
+
inset-inline: auto;
|
|
187
|
+
margin: 0;
|
|
188
|
+
max-block-size: none;
|
|
189
|
+
max-inline-size: none;
|
|
190
|
+
min-block-size: auto;
|
|
191
|
+
opacity: 1;
|
|
192
|
+
overflow: visible;
|
|
193
|
+
position: static;
|
|
194
|
+
transform: none;
|
|
195
|
+
transition: none;
|
|
196
|
+
|
|
197
|
+
> .header,
|
|
198
|
+
> .footer {
|
|
199
|
+
display: none;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
> .content {
|
|
203
|
+
flex-grow: 0;
|
|
204
|
+
overflow-y: visible;
|
|
205
|
+
overscroll-behavior: auto;
|
|
206
|
+
padding: 0;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.panel-inline-md {
|
|
212
|
+
@media (--md) {
|
|
213
|
+
/* Colours go too, not just geometry: a region of the page carries none
|
|
214
|
+
of the panel's chrome and takes the page's own text and link colours.
|
|
215
|
+
`inherit` on a custom property hands it back to the parent, which is
|
|
216
|
+
exactly what is wanted here — and it has to be done token by token.
|
|
217
|
+
Resetting --panel-color-text, which the three derive from, would make
|
|
218
|
+
them invalid at computed-value time rather than page-valued, and every
|
|
219
|
+
link would fall back to the surrounding text colour instead of the
|
|
220
|
+
site's link colour. Keep this list in step with the panel rule above. */
|
|
221
|
+
--color-link: inherit;
|
|
222
|
+
--color-link-hover: inherit;
|
|
223
|
+
--nav-color-text: inherit;
|
|
224
|
+
|
|
225
|
+
background-color: transparent;
|
|
226
|
+
block-size: auto;
|
|
227
|
+
border-radius: 0;
|
|
228
|
+
box-shadow: none;
|
|
229
|
+
color: inherit;
|
|
230
|
+
display: flex;
|
|
231
|
+
inline-size: auto;
|
|
232
|
+
inset-block: auto;
|
|
233
|
+
inset-inline: auto;
|
|
234
|
+
margin: 0;
|
|
235
|
+
max-block-size: none;
|
|
236
|
+
max-inline-size: none;
|
|
237
|
+
min-block-size: auto;
|
|
238
|
+
opacity: 1;
|
|
239
|
+
overflow: visible;
|
|
240
|
+
position: static;
|
|
241
|
+
transform: none;
|
|
242
|
+
transition: none;
|
|
243
|
+
|
|
244
|
+
> .header,
|
|
245
|
+
> .footer {
|
|
246
|
+
display: none;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
> .content {
|
|
250
|
+
flex-grow: 0;
|
|
251
|
+
overflow-y: visible;
|
|
252
|
+
overscroll-behavior: auto;
|
|
253
|
+
padding: 0;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.panel-inline-lg {
|
|
259
|
+
@media (--lg) {
|
|
260
|
+
/* Colours go too, not just geometry: a region of the page carries none
|
|
261
|
+
of the panel's chrome and takes the page's own text and link colours.
|
|
262
|
+
`inherit` on a custom property hands it back to the parent, which is
|
|
263
|
+
exactly what is wanted here — and it has to be done token by token.
|
|
264
|
+
Resetting --panel-color-text, which the three derive from, would make
|
|
265
|
+
them invalid at computed-value time rather than page-valued, and every
|
|
266
|
+
link would fall back to the surrounding text colour instead of the
|
|
267
|
+
site's link colour. Keep this list in step with the panel rule above. */
|
|
268
|
+
--color-link: inherit;
|
|
269
|
+
--color-link-hover: inherit;
|
|
270
|
+
--nav-color-text: inherit;
|
|
271
|
+
|
|
272
|
+
background-color: transparent;
|
|
273
|
+
block-size: auto;
|
|
274
|
+
border-radius: 0;
|
|
275
|
+
box-shadow: none;
|
|
276
|
+
color: inherit;
|
|
277
|
+
display: flex;
|
|
278
|
+
inline-size: auto;
|
|
279
|
+
inset-block: auto;
|
|
280
|
+
inset-inline: auto;
|
|
281
|
+
margin: 0;
|
|
282
|
+
max-block-size: none;
|
|
283
|
+
max-inline-size: none;
|
|
284
|
+
min-block-size: auto;
|
|
285
|
+
opacity: 1;
|
|
286
|
+
overflow: visible;
|
|
287
|
+
position: static;
|
|
288
|
+
transform: none;
|
|
289
|
+
transition: none;
|
|
290
|
+
|
|
291
|
+
> .header,
|
|
292
|
+
> .footer {
|
|
293
|
+
display: none;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
> .content {
|
|
297
|
+
flex-grow: 0;
|
|
298
|
+
overflow-y: visible;
|
|
299
|
+
overscroll-behavior: auto;
|
|
300
|
+
padding: 0;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.panel-inline-xl {
|
|
306
|
+
@media (--xl) {
|
|
307
|
+
/* Colours go too, not just geometry: a region of the page carries none
|
|
308
|
+
of the panel's chrome and takes the page's own text and link colours.
|
|
309
|
+
`inherit` on a custom property hands it back to the parent, which is
|
|
310
|
+
exactly what is wanted here — and it has to be done token by token.
|
|
311
|
+
Resetting --panel-color-text, which the three derive from, would make
|
|
312
|
+
them invalid at computed-value time rather than page-valued, and every
|
|
313
|
+
link would fall back to the surrounding text colour instead of the
|
|
314
|
+
site's link colour. Keep this list in step with the panel rule above. */
|
|
315
|
+
--color-link: inherit;
|
|
316
|
+
--color-link-hover: inherit;
|
|
317
|
+
--nav-color-text: inherit;
|
|
318
|
+
|
|
319
|
+
background-color: transparent;
|
|
320
|
+
block-size: auto;
|
|
321
|
+
border-radius: 0;
|
|
322
|
+
box-shadow: none;
|
|
323
|
+
color: inherit;
|
|
324
|
+
display: flex;
|
|
325
|
+
inline-size: auto;
|
|
326
|
+
inset-block: auto;
|
|
327
|
+
inset-inline: auto;
|
|
328
|
+
margin: 0;
|
|
329
|
+
max-block-size: none;
|
|
330
|
+
max-inline-size: none;
|
|
331
|
+
min-block-size: auto;
|
|
332
|
+
opacity: 1;
|
|
333
|
+
overflow: visible;
|
|
334
|
+
position: static;
|
|
335
|
+
transform: none;
|
|
336
|
+
transition: none;
|
|
337
|
+
|
|
338
|
+
> .header,
|
|
339
|
+
> .footer {
|
|
340
|
+
display: none;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
> .content {
|
|
344
|
+
flex-grow: 0;
|
|
345
|
+
overflow-y: visible;
|
|
346
|
+
overscroll-behavior: auto;
|
|
347
|
+
padding: 0;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/* ── Trigger ───────────────────────────────────────────────────────
|
|
353
|
+
* Once the panel has gone inline there is nothing left to open, so its
|
|
354
|
+
* trigger goes with it. Kept here rather than restated by every host.
|
|
355
|
+
*
|
|
356
|
+
* Only reaches a trigger that is the panel's own previous sibling: adjacency
|
|
357
|
+
* is what proves intent, so any button there qualifies. A trigger placed
|
|
358
|
+
* further away — inside the heading that precedes the panel, as the filters
|
|
359
|
+
* do — is not covered and stays visible. */
|
|
360
|
+
|
|
361
|
+
@media (--sm) {
|
|
362
|
+
button:has(+ .panel-inline-sm) { display: none; }
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
@media (--md) {
|
|
366
|
+
button:has(+ .panel-inline-md) { display: none; }
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
@media (--lg) {
|
|
370
|
+
button:has(+ .panel-inline-lg) { display: none; }
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
@media (--xl) {
|
|
374
|
+
button:has(+ .panel-inline-xl) { display: none; }
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/* ── Entry transition ──────────────────────────────────────────────
|
|
378
|
+
* Opening is a discrete display change, so the start state has to be
|
|
379
|
+
* spelled out. It is exactly the closed state, which now lives in tokens —
|
|
380
|
+
* one block covers both variants, panel and ::backdrop alike. */
|
|
381
|
+
|
|
382
|
+
@starting-style {
|
|
383
|
+
:where(.modal, .drawer):is([open], :popover-open) {
|
|
384
|
+
opacity: var(--panel-opacity, 1);
|
|
385
|
+
transform: var(--panel-translate, none);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/* ── Backdrop ──────────────────────────────────────────────────────
|
|
390
|
+
* Both paths put the panel in the top layer — showModal() for a <dialog>,
|
|
391
|
+
* showPopover() for anything else — so both get the native ::backdrop, and
|
|
392
|
+
* it inherits from the panel: it reads the very --panel-* tokens the panel
|
|
393
|
+
* declares. No backdrop node is ever injected, and no z-index is involved.
|
|
394
|
+
*
|
|
395
|
+
* This is why the class-driven path uses popover rather than a plain
|
|
396
|
+
* element. A backdrop placed outside the panel sits in the root stacking
|
|
397
|
+
* context, while the panel stays confined to its nearest stacking ancestor
|
|
398
|
+
* — a sticky sidebar wrapper, a sticky header — so the veil paints over the
|
|
399
|
+
* panel and no z-index can lift it out. Placing the backdrop next to the
|
|
400
|
+
* panel only moves the problem: it then inherits the same confinement and
|
|
401
|
+
* anything painted later shows through it. The top layer removes the
|
|
402
|
+
* question entirely, which is exactly what it exists for. */
|
|
403
|
+
|
|
404
|
+
:where(.modal, .drawer)::backdrop {
|
|
405
|
+
backdrop-filter: blur(var(--panel-backdrop-blur));
|
|
406
|
+
background-color: var(--panel-backdrop-color);
|
|
407
|
+
opacity: 0;
|
|
408
|
+
|
|
409
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
410
|
+
transition-behavior: allow-discrete;
|
|
411
|
+
transition-duration: var(--panel-transition-duration);
|
|
412
|
+
transition-property: display, opacity, overlay;
|
|
413
|
+
transition-timing-function: var(--panel-transition-easing);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
:where(.modal, .drawer):is([open], :popover-open)::backdrop {
|
|
418
|
+
opacity: 1;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
@starting-style {
|
|
422
|
+
:where(.modal, .drawer):is([open], :popover-open)::backdrop {
|
|
423
|
+
opacity: 0;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/* ── Page scroll lock ────────────────────────────────────────────── */
|
|
428
|
+
|
|
429
|
+
body:has(:where(.modal, .drawer):where([open], :popover-open)) {
|
|
430
|
+
overflow: hidden;
|
|
431
|
+
}
|
|
432
|
+
}
|
package/css/index.css
CHANGED