@keenmate/pure-css 1.0.0-rc03 → 1.0.0-rc05
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/CHANGELOG.md +195 -0
- package/README.md +69 -23
- package/dist/css/base.css +48 -204
- package/dist/css/grid.css +364 -364
- package/dist/css/pure-css.css +1726 -612
- package/dist/css/reboot.css +5 -5
- package/dist/css/scrollbars.css +9 -9
- package/dist/css/utilities.css +78 -30
- package/package.json +18 -3
- package/src/js/container-breakpoint.js +308 -0
- package/src/js/fit.js +890 -0
- package/src/js/navbar-dropdown.js +164 -0
- package/src/js/pure-css.js +305 -0
- package/src/js/sidebar-resize.js +210 -0
- package/src/scss/_base-css-variables.scss +360 -385
- package/src/scss/_fit-flyout.scss +76 -0
- package/src/scss/_fonts.scss +36 -36
- package/src/scss/_layout-container.scss +168 -0
- package/src/scss/_layout-responsive.scss +181 -0
- package/src/scss/_navbar-elements.scss +419 -0
- package/src/scss/_navbar.scss +120 -0
- package/src/scss/_pa-grid.scss +43 -43
- package/src/scss/_resize-handle.scss +76 -0
- package/src/scss/_sidebar-states.scss +243 -0
- package/src/scss/_sidebar.scss +420 -0
- package/src/scss/base.scss +2 -3
- package/src/scss/grid.scss +1 -1
- package/src/scss/pure-css.scss +20 -5
- package/src/scss/reboot.scss +5 -5
- package/src/scss/scrollbars.scss +10 -10
- package/src/scss/utilities.scss +791 -758
- package/src/scss/variables/_base.scss +232 -232
- package/src/scss/variables/_components.scss +840 -835
- package/src/scss/variables/_layout.scss +62 -62
- package/src/scss/variables/_system.scss +1 -1
- package/src/scss/variables/_typography.scss +37 -37
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
@use 'variables/index' as *;
|
|
2
|
+
/* ========================================
|
|
3
|
+
Navbar/Header Elements
|
|
4
|
+
Burger menu, brand, navigation, title, profile button, theme switcher
|
|
5
|
+
======================================== */
|
|
6
|
+
|
|
7
|
+
// Fit engine (navbar-fit.js): the class it toggles to remove a slot or an
|
|
8
|
+
// inactive step from the row. `!important` beats component display rules (e.g.
|
|
9
|
+
// `.pc-navbar-search { display: flex }`) that would otherwise defeat it; using a
|
|
10
|
+
// class (not inline display) means "show" simply reverts to the element's own
|
|
11
|
+
// stylesheet display. Author non-first steps with this class too, so before JS
|
|
12
|
+
// runs only the largest variant shows (no stacked-variants flash, no-JS-safe).
|
|
13
|
+
.pc-fit-hidden {
|
|
14
|
+
display: none !important;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// --- App identity: brand wordmark + version tag -------------------------------
|
|
18
|
+
// The brand container. In markup: <div class="pc-app-header"><h1>…</h1></div>.
|
|
19
|
+
.pc-app-header {
|
|
20
|
+
flex-shrink: 0;
|
|
21
|
+
// No brand padding: the .pc-navbar__start flex `gap` (see _navbar.scss) is the
|
|
22
|
+
// single separator between the burger and the brand.
|
|
23
|
+
|
|
24
|
+
h1 {
|
|
25
|
+
margin: 0;
|
|
26
|
+
font-size: $font-size-lg;
|
|
27
|
+
font-weight: $font-weight-semibold;
|
|
28
|
+
color: var(--pc-navbar-text);
|
|
29
|
+
white-space: nowrap;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Version tag beside the wordmark (e.g. "Pure Admin v2.9.0"). Smaller and
|
|
33
|
+
// muted so it reads as metadata rather than part of the product name.
|
|
34
|
+
&__version {
|
|
35
|
+
margin-inline-start: $spacing-xs;
|
|
36
|
+
font-size: $font-size-xs;
|
|
37
|
+
font-weight: $font-weight-normal;
|
|
38
|
+
color: var(--pc-navbar-text-secondary);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// --- Burger (fixed navbar anchor, never degrades) -----------------------------
|
|
43
|
+
.pc-navbar__burger {
|
|
44
|
+
flex-shrink: 0;
|
|
45
|
+
margin-inline-start: $burger-menu-margin-left; // RTL: flips to margin-right
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// --- Navbar menu: horizontal nav + dropdowns + collapse "More" ----------------
|
|
49
|
+
// Its own block (reused in both the start and end navbar zones).
|
|
50
|
+
.pc-navmenu {
|
|
51
|
+
flex-shrink: 0;
|
|
52
|
+
|
|
53
|
+
// Only style the direct child ul (main navigation)
|
|
54
|
+
> ul {
|
|
55
|
+
list-style: none;
|
|
56
|
+
margin: 0;
|
|
57
|
+
padding: 0;
|
|
58
|
+
display: flex;
|
|
59
|
+
gap: $spacing-md;
|
|
60
|
+
|
|
61
|
+
> li {
|
|
62
|
+
margin: 0; // Disable base margins from _base.scss
|
|
63
|
+
position: relative; // For dropdown positioning
|
|
64
|
+
display: flex;
|
|
65
|
+
align-items: center;
|
|
66
|
+
|
|
67
|
+
> a {
|
|
68
|
+
text-decoration: none;
|
|
69
|
+
color: var(--pc-navbar-text-secondary);
|
|
70
|
+
font-weight: $font-weight-medium;
|
|
71
|
+
padding: $spacing-xs $spacing-sm;
|
|
72
|
+
border-radius: var(--pc-border-radius);
|
|
73
|
+
transition: all $transition-fast $easing-snappy;
|
|
74
|
+
font-size: $font-size-sm;
|
|
75
|
+
display: flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
|
|
78
|
+
// Hover/active derive their pill background from `currentColor` — a
|
|
79
|
+
// translucent tint of the link's OWN rendered text colour — not the
|
|
80
|
+
// content-area --pc-accent. The navbar sits on --pc-navbar-bg, which
|
|
81
|
+
// several themes set dark or to the accent colour itself (e.g. NATO's
|
|
82
|
+
// navy bar) and then force the nav text light with !important while
|
|
83
|
+
// leaving --pc-navbar-text at its default dark value. A currentColor
|
|
84
|
+
// tint tracks the actual text colour (light there, dark on light
|
|
85
|
+
// bars), so the pill contrasts the bar on every theme; an accent
|
|
86
|
+
// or --pc-navbar-text tint would be dark-on-dark and vanish. Text
|
|
87
|
+
// brightens to --pc-navbar-text, which those themes override to match.
|
|
88
|
+
&:hover {
|
|
89
|
+
color: var(--pc-navbar-text);
|
|
90
|
+
background-color: color-mix(in srgb, currentColor 12%, transparent);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Active top-level item — the current section. Held state = a stronger
|
|
95
|
+
// currentColor pill + semibold, reading clearly above the hover tint on
|
|
96
|
+
// any bar. Modifier goes on the <li> (.pc-navmenu__item--active);
|
|
97
|
+
// scoped through the nav > ul > li chain so it outweighs the base
|
|
98
|
+
// `> li > a` colour.
|
|
99
|
+
&.pc-navmenu__item--active > a {
|
|
100
|
+
&,
|
|
101
|
+
&:hover {
|
|
102
|
+
color: var(--pc-navbar-text);
|
|
103
|
+
background-color: color-mix(in srgb, currentColor 22%, transparent);
|
|
104
|
+
font-weight: $font-weight-semibold;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Dropdown navigation items
|
|
111
|
+
&__item {
|
|
112
|
+
&--has-dropdown {
|
|
113
|
+
position: static; // Position dropdown relative to navbar, not this item
|
|
114
|
+
|
|
115
|
+
// Hover-reveal only where hover exists. On touch it never fires (and
|
|
116
|
+
// sticky-hover would fight the JS), so it's gated out and the `.is-open`
|
|
117
|
+
// class below — toggled by navbar-dropdown.js on tap — drives the menu.
|
|
118
|
+
@media (hover: hover) {
|
|
119
|
+
&:hover > .pc-navmenu__dropdown {
|
|
120
|
+
display: block !important; // Override the !important hide
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Touch open state (first-tap-opens; navbar-dropdown.js). Works on all
|
|
125
|
+
// devices, so the same class also drives keyboard/JS opens if wired.
|
|
126
|
+
&.is-open > .pc-navmenu__dropdown {
|
|
127
|
+
display: block !important;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
&__link {
|
|
133
|
+
display: flex;
|
|
134
|
+
align-items: center;
|
|
135
|
+
gap: $spacing-xs;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Dropdown menus
|
|
139
|
+
&__dropdown {
|
|
140
|
+
display: none !important; // Force hide by default
|
|
141
|
+
position: absolute;
|
|
142
|
+
top: 100%;
|
|
143
|
+
inset-inline-start: 0; // RTL: flips to right: 0
|
|
144
|
+
min-width: 19.2rem;
|
|
145
|
+
background-color: var(--pc-card-bg);
|
|
146
|
+
border: $border-width-base solid var(--pc-border-color);
|
|
147
|
+
border-radius: var(--pc-border-radius);
|
|
148
|
+
box-shadow: $shadow-lg;
|
|
149
|
+
z-index: 1100;
|
|
150
|
+
padding: $spacing-xs 0;
|
|
151
|
+
margin-top: 0;
|
|
152
|
+
list-style: none;
|
|
153
|
+
flex-direction: column; // Override any flex from parent
|
|
154
|
+
|
|
155
|
+
li {
|
|
156
|
+
margin: 0; // Disable base margins from _base.scss
|
|
157
|
+
display: block;
|
|
158
|
+
position: relative;
|
|
159
|
+
|
|
160
|
+
// Create invisible bridge for nested dropdowns (horizontal)
|
|
161
|
+
&.pc-navmenu__item--has-dropdown::after {
|
|
162
|
+
content: '';
|
|
163
|
+
position: absolute;
|
|
164
|
+
top: 0;
|
|
165
|
+
inset-inline-start: 100%; // RTL: flips to right: 100%
|
|
166
|
+
width: 6.4px; // Small invisible bridge
|
|
167
|
+
height: 100%;
|
|
168
|
+
z-index: 1099;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Show nested dropdown on hover (hover devices only — see above).
|
|
172
|
+
@media (hover: hover) {
|
|
173
|
+
&:hover > .pc-navmenu__dropdown {
|
|
174
|
+
display: block !important;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Touch open state for nested submenus (navbar-dropdown.js).
|
|
179
|
+
&.is-open > .pc-navmenu__dropdown {
|
|
180
|
+
display: block !important;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
a {
|
|
184
|
+
display: block;
|
|
185
|
+
padding: $spacing-sm $spacing-base;
|
|
186
|
+
color: var(--pc-text-color-1);
|
|
187
|
+
border-radius: 0;
|
|
188
|
+
white-space: nowrap;
|
|
189
|
+
text-decoration: none;
|
|
190
|
+
font-weight: $font-weight-medium;
|
|
191
|
+
font-size: $font-size-sm;
|
|
192
|
+
|
|
193
|
+
&:hover {
|
|
194
|
+
background-color: var(--pc-accent-light);
|
|
195
|
+
color: var(--pc-accent);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Level 2 dropdown (nested)
|
|
201
|
+
&--level2 {
|
|
202
|
+
top: 0;
|
|
203
|
+
inset-inline-start: 100%; // RTL: flips to right: 100%
|
|
204
|
+
margin-top: 0;
|
|
205
|
+
margin-inline-start: 0; // RTL: flips to margin-right
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// On touch, a nested submenu can't be a sideways flyout — `inset-inline-start:
|
|
210
|
+
// 100%` runs it straight off narrow screens (nowhere to fly). So on
|
|
211
|
+
// `(hover: none)` nested dropdowns stack INLINE (accordion) inside the level-1
|
|
212
|
+
// panel: static box, indented items, no shadow/border — the same shape the
|
|
213
|
+
// flattened "More" panel uses, but toggled open per-branch by the `.is-open`
|
|
214
|
+
// class (navbar-dropdown.js) rather than shown all at once. The level-1 panel
|
|
215
|
+
// itself stays an absolute drop below the bar.
|
|
216
|
+
@media (hover: none) {
|
|
217
|
+
&__dropdown &__dropdown {
|
|
218
|
+
position: static;
|
|
219
|
+
inset: auto;
|
|
220
|
+
min-width: 0;
|
|
221
|
+
margin: 0;
|
|
222
|
+
border: none;
|
|
223
|
+
box-shadow: none;
|
|
224
|
+
padding: 0;
|
|
225
|
+
|
|
226
|
+
li a {
|
|
227
|
+
padding-inline-start: $spacing-xl; // indent under the group header
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// --- Responsive collapse (driven by fit.js, data-pc-fit-nav) -----------
|
|
233
|
+
// A nav opted into progressive collapse must be able to shrink BELOW its
|
|
234
|
+
// content so fit.js's bbox-sum vs clientWidth overflow test becomes truthful.
|
|
235
|
+
// Make the nav shrinkable, and (via :has) let the enclosing start section
|
|
236
|
+
// shrink too — it's flex-shrink:0 by default — so the nav absorbs the squeeze
|
|
237
|
+
// while the burger and brand (also flex-shrink:0) keep their size.
|
|
238
|
+
&[data-pc-fit-nav] {
|
|
239
|
+
flex-shrink: 1;
|
|
240
|
+
min-width: 0;
|
|
241
|
+
// No overflow:hidden here — it would clip the nav items' OWN hover
|
|
242
|
+
// dropdowns (they drop below the bar, and any ancestor overflow:hidden
|
|
243
|
+
// cuts them off). Items stay at natural width (flex-shrink:0) and
|
|
244
|
+
// fit.js detects overflow by summing item widths, so clipping
|
|
245
|
+
// isn't needed to measure it.
|
|
246
|
+
|
|
247
|
+
> ul > li {
|
|
248
|
+
flex-shrink: 0;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// The generated "More" trigger. Its panel is parented to <body> (to escape
|
|
253
|
+
// the nav's overflow:hidden) and positioned by fit.js, so there's
|
|
254
|
+
// no inline anchoring to do here — just the trigger's own affordance.
|
|
255
|
+
&__item--more {
|
|
256
|
+
position: relative;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
&__more-chevron {
|
|
260
|
+
display: inline-block;
|
|
261
|
+
margin-inline-start: $spacing-xs;
|
|
262
|
+
font-size: $font-size-xs;
|
|
263
|
+
transition: transform $transition-fast $easing-snappy;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Chevron points down while the panel is open (click-toggled).
|
|
267
|
+
&__item--more.is-open &__more-chevron {
|
|
268
|
+
transform: rotate(90deg);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// The More panel is parented to <body> (escapes the nav's overflow:hidden)
|
|
272
|
+
// and positioned by JS; fit.js toggles --open to reveal it. The
|
|
273
|
+
// compound selector outweighs the base `.pc-navmenu__dropdown { display:none }`.
|
|
274
|
+
&__more-menu {
|
|
275
|
+
z-index: $z-index-navbar + 10; // above the fixed navbar
|
|
276
|
+
|
|
277
|
+
&.pc-navmenu__more-menu--open {
|
|
278
|
+
display: block !important;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Flatten — the answer to "what about multi-level menus" in menu mode.
|
|
283
|
+
// Inside the More panel, a collapsed item that had its own dropdown renders
|
|
284
|
+
// that submenu INLINE (indented, static) instead of a hover flyout, so the
|
|
285
|
+
// whole thing is one scrollable panel with no cascading menus, at any depth.
|
|
286
|
+
&__more-menu {
|
|
287
|
+
.pc-navmenu__dropdown {
|
|
288
|
+
display: block !important; // always shown; overrides the hover-reveal
|
|
289
|
+
position: static;
|
|
290
|
+
min-width: 0;
|
|
291
|
+
border: none;
|
|
292
|
+
box-shadow: none;
|
|
293
|
+
padding: 0;
|
|
294
|
+
margin: 0;
|
|
295
|
+
|
|
296
|
+
li a {
|
|
297
|
+
padding-inline-start: $spacing-xl; // indent under the group header
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// A parent link that owns a submenu reads as a group header.
|
|
302
|
+
.pc-navmenu__item--has-dropdown > a {
|
|
303
|
+
font-weight: $font-weight-semibold;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// No flyout bridge inside the flattened panel.
|
|
307
|
+
.pc-navmenu__item--has-dropdown::after {
|
|
308
|
+
content: none;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// The navbar start zone must shrink when it hosts a collapsible menu, so the
|
|
314
|
+
// nav can absorb the squeeze (see the collapse note above). It's flex-shrink:0
|
|
315
|
+
// by default in _navbar.scss; :has() opts it back into shrinking only when a
|
|
316
|
+
// collapsible menu is present, leaving the burger and brand at their size.
|
|
317
|
+
.pc-navbar__start:has(.pc-navmenu[data-pc-fit-nav]) {
|
|
318
|
+
flex-shrink: 1;
|
|
319
|
+
min-width: 0;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// --- Page title (the center zone's page header) -------------------------------
|
|
323
|
+
.pc-page-header {
|
|
324
|
+
flex: 1;
|
|
325
|
+
min-width: 0; // Enable ellipsis
|
|
326
|
+
text-align: center;
|
|
327
|
+
|
|
328
|
+
h2 {
|
|
329
|
+
margin: 0;
|
|
330
|
+
font-size: $font-size-lg;
|
|
331
|
+
font-weight: $font-weight-semibold;
|
|
332
|
+
color: var(--pc-navbar-text);
|
|
333
|
+
white-space: nowrap;
|
|
334
|
+
overflow: hidden;
|
|
335
|
+
text-overflow: ellipsis;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// --- Profile trigger (navbar end zone) ----------------------------------------
|
|
340
|
+
.pc-navbar__profile-btn {
|
|
341
|
+
flex-shrink: 0;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Burger menu button
|
|
345
|
+
.burger-menu {
|
|
346
|
+
display: flex;
|
|
347
|
+
flex-direction: column;
|
|
348
|
+
justify-content: space-around;
|
|
349
|
+
width: $burger-width;
|
|
350
|
+
height: $burger-height;
|
|
351
|
+
background: transparent;
|
|
352
|
+
border: none;
|
|
353
|
+
cursor: pointer;
|
|
354
|
+
padding: 0;
|
|
355
|
+
|
|
356
|
+
span {
|
|
357
|
+
width: 100%;
|
|
358
|
+
height: $burger-bar-height;
|
|
359
|
+
background-color: var(--pc-navbar-text);
|
|
360
|
+
border-radius: var(--pc-border-radius);
|
|
361
|
+
transition: all $transition-normal $easing-snappy;
|
|
362
|
+
transform-origin: center;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
&.active {
|
|
366
|
+
span:nth-child(1) {
|
|
367
|
+
transform: rotate(45deg) translate($burger-transform-offset, $burger-transform-offset);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
span:nth-child(2) {
|
|
371
|
+
opacity: 0;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
span:nth-child(3) {
|
|
375
|
+
transform: rotate(-45deg) translate($burger-transform-offset, -$burger-transform-offset);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// RTL: reverse X translation direction
|
|
380
|
+
[dir="rtl"] &.active {
|
|
381
|
+
span:nth-child(1) {
|
|
382
|
+
transform: rotate(45deg) translate(-$burger-transform-offset, $burger-transform-offset);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
span:nth-child(3) {
|
|
386
|
+
transform: rotate(-45deg) translate(-$burger-transform-offset, -$burger-transform-offset);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
&:hover span {
|
|
391
|
+
background-color: var(--pc-accent);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Theme switcher styling
|
|
396
|
+
.theme-switcher {
|
|
397
|
+
display: flex;
|
|
398
|
+
align-items: center;
|
|
399
|
+
gap: $spacing-sm;
|
|
400
|
+
|
|
401
|
+
label {
|
|
402
|
+
color: var(--pc-navbar-text-secondary);
|
|
403
|
+
font-size: $font-size-sm;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
select {
|
|
407
|
+
padding: $spacing-xs $spacing-sm;
|
|
408
|
+
border: $border-width-base solid var(--pc-border-color);
|
|
409
|
+
border-radius: var(--pc-border-radius);
|
|
410
|
+
background-color: var(--pc-navbar-bg);
|
|
411
|
+
color: var(--pc-navbar-text);
|
|
412
|
+
font-size: $font-size-sm;
|
|
413
|
+
|
|
414
|
+
&:focus {
|
|
415
|
+
outline: none;
|
|
416
|
+
border-color: var(--pc-accent);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
@use 'variables/index' as *;
|
|
2
|
+
/* ========================================
|
|
3
|
+
Navbar Component
|
|
4
|
+
Fixed navbar and constrained container widths
|
|
5
|
+
======================================== */
|
|
6
|
+
|
|
7
|
+
// Width the centre slot reserves when it hosts a command-palette search pill
|
|
8
|
+
// (see .pc-navbar__center below). A floor, not a fixed width — the slot still
|
|
9
|
+
// grows via flex:1; this just stops it collapsing to a sliver.
|
|
10
|
+
$navbar-search-reserve-width: 18rem !default;
|
|
11
|
+
|
|
12
|
+
// Navbar - Fixed at top, full width by default
|
|
13
|
+
.pc-navbar {
|
|
14
|
+
position: fixed;
|
|
15
|
+
top: 0;
|
|
16
|
+
inset-inline: 0; // RTL: works for both LTR and RTL
|
|
17
|
+
height: $navbar-height;
|
|
18
|
+
background-color: var(--pc-navbar-bg);
|
|
19
|
+
border-bottom: $border-width-base solid var(--pc-navbar-border-color);
|
|
20
|
+
z-index: $z-index-navbar; // 2000
|
|
21
|
+
display: flex;
|
|
22
|
+
align-items: center;
|
|
23
|
+
justify-content: center; // Center for constrained mode
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Constrained navbar - limit navbar width to match container
|
|
27
|
+
// Body class is just a marker, no width styles on body itself
|
|
28
|
+
body.pc-container-sm .pc-navbar {
|
|
29
|
+
max-width: $layout-container-sm;
|
|
30
|
+
inset-inline: 0; // RTL: works for both LTR and RTL
|
|
31
|
+
margin-inline: auto; // RTL: centers in both directions
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
body.pc-container-md .pc-navbar {
|
|
35
|
+
max-width: $layout-container-md;
|
|
36
|
+
inset-inline: 0;
|
|
37
|
+
margin-inline: auto;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
body.pc-container-lg .pc-navbar {
|
|
41
|
+
max-width: $layout-container-lg;
|
|
42
|
+
inset-inline: 0;
|
|
43
|
+
margin-inline: auto;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
body.pc-container-xl .pc-navbar {
|
|
47
|
+
max-width: $layout-container-xl;
|
|
48
|
+
inset-inline: 0;
|
|
49
|
+
margin-inline: auto;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
body.pc-container-2xl .pc-navbar {
|
|
53
|
+
max-width: $layout-container-2xl;
|
|
54
|
+
inset-inline: 0;
|
|
55
|
+
margin-inline: auto;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Navbar inner content. Its direct children are a FIXED burger anchor
|
|
59
|
+
// (.pc-navbar__burger — always visible, never in the fit engine) followed by
|
|
60
|
+
// the three degradable zones (.pc-navbar__start / __center / __end). The burger
|
|
61
|
+
// is a SIBLING of the zones, not inside start, so it stays put while the zones
|
|
62
|
+
// collapse. Its `gap` here is the single burger→start separator (same
|
|
63
|
+
// $spacing-base as .pc-navbar__start's own gap). navbar-fit.js measures these
|
|
64
|
+
// direct children.
|
|
65
|
+
.pc-navbar__inner {
|
|
66
|
+
width: 100%;
|
|
67
|
+
padding: 0 $spacing-base;
|
|
68
|
+
display: flex;
|
|
69
|
+
align-items: center;
|
|
70
|
+
gap: $spacing-base;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Header sections - Start/Center/End layout
|
|
74
|
+
.pc-navbar__start {
|
|
75
|
+
display: flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
gap: $spacing-base;
|
|
78
|
+
flex-shrink: 0;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.pc-navbar__center {
|
|
82
|
+
flex: 1;
|
|
83
|
+
min-width: 0; // Allow shrinking for ellipsis
|
|
84
|
+
display: flex;
|
|
85
|
+
align-items: center;
|
|
86
|
+
justify-content: center;
|
|
87
|
+
|
|
88
|
+
// When the centre slot holds a command-palette search pill, reserve real
|
|
89
|
+
// width for it. The search's min-width lives on a *child* (.pc-navbar-search),
|
|
90
|
+
// which flexbox ignores — so this slot (min-width:0) would collapse to a
|
|
91
|
+
// sliver, letting the pill overflow onto the start/end slots. Worse: that
|
|
92
|
+
// deficit never shrinks the NAV's own slot, so fit.js — which
|
|
93
|
+
// measures nav.clientWidth — never sees it and stops folding while items still
|
|
94
|
+
// overlap. Reserving a floor here forces the squeeze onto the shrinkable
|
|
95
|
+
// .pc-navbar__start (see the :has rule in _navbar-elements.scss) instead, so
|
|
96
|
+
// the nav narrows and the collapse loop fires honestly.
|
|
97
|
+
&:has(.pc-navbar-search) {
|
|
98
|
+
min-width: $navbar-search-reserve-width;
|
|
99
|
+
|
|
100
|
+
// Let the reserved slot's DIRECT flex child shrink, whatever it is. The
|
|
101
|
+
// shrink flexbox needs (min-width:0) has to land on the flex item itself —
|
|
102
|
+
// not on the pill by name. Consumers (svelte-pure-admin etc.) wrap the pill
|
|
103
|
+
// in their own element; a descendant selector shrank the pill but left the
|
|
104
|
+
// wrapper at min-width:auto, so it refused to collapse and overflowed onto
|
|
105
|
+
// the start/end slots. Targeting `> *` is position-agnostic: pill-as-child
|
|
106
|
+
// and pill-in-wrapper both shrink. Guarded by :has(.pc-navbar-search) so it
|
|
107
|
+
// only touches the slot when it actually hosts a search.
|
|
108
|
+
> * {
|
|
109
|
+
min-width: 0;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.pc-navbar__end {
|
|
115
|
+
display: flex;
|
|
116
|
+
align-items: center;
|
|
117
|
+
gap: $spacing-base;
|
|
118
|
+
flex-shrink: 0;
|
|
119
|
+
margin-inline-start: auto;
|
|
120
|
+
}
|