contain-css-svelte 1.2.3 → 1.4.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/dist/Card.svelte +216 -288
- package/dist/controls/Button.svelte +39 -1
- package/dist/controls/ButtonLink.svelte +39 -1
- package/dist/controls/Checkbox.svelte +38 -0
- package/dist/controls/CircleButton.svelte +39 -1
- package/dist/controls/Input.svelte +39 -1
- package/dist/controls/RadioButton.svelte +38 -0
- package/dist/controls/Select.svelte +38 -0
- package/dist/controls/Slider.svelte +39 -1
- package/dist/controls/TabItem.svelte +39 -1
- package/dist/controls/Toggle.svelte +39 -1
- package/dist/dropdowns/DropdownMenu.svelte +39 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/layout/Accordion.svelte +39 -1
- package/dist/layout/Bar.svelte +49 -1
- package/dist/layout/ColumnContainer.svelte +39 -1
- package/dist/layout/Columns.svelte +39 -1
- package/dist/layout/Container.svelte +217 -292
- package/dist/layout/DataList.svelte +39 -1
- package/dist/layout/DataListItem.svelte +217 -292
- package/dist/layout/Fieldset.svelte +39 -1
- package/dist/layout/Form.svelte +217 -292
- package/dist/layout/FormItem.svelte +39 -1
- package/dist/layout/GridLayout.svelte +38 -0
- package/dist/layout/Hero.svelte +217 -292
- package/dist/layout/Inline.svelte +45 -1
- package/dist/layout/MenuList.svelte +39 -1
- package/dist/layout/Page.svelte +44 -2
- package/dist/layout/RowContainer.svelte +39 -1
- package/dist/layout/Sidebar.svelte +757 -65
- package/dist/layout/Sidebar.svelte.d.ts +58 -1
- package/dist/layout/SidebarContainer.svelte +198 -0
- package/dist/layout/SidebarContainer.svelte.d.ts +20 -0
- package/dist/layout/SplitPane.svelte +39 -1
- package/dist/layout/Stack.svelte +45 -1
- package/dist/layout/TabBar.svelte +39 -1
- package/dist/layout/Table.svelte +469 -443
- package/dist/layout/Tile.svelte +216 -291
- package/dist/misc/CodeInner.svelte +39 -1
- package/dist/misc/Progress.svelte +217 -292
- package/dist/misc/Tag.svelte +39 -1
- package/dist/overlays/Dialog.svelte +217 -292
- package/dist/overlays/Tooltip.svelte +267 -349
- package/dist/sass/_responsive.scss +15 -0
- package/dist/sass/_typography.scss +203 -191
- package/dist/typography/TextLayout.svelte +494 -292
- package/dist/vars/defaults.css +258 -0
- package/dist/vars/typography-context.css +271 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">import { elementProps } from "../util";
|
|
2
2
|
import { COLOR_VARS, TYPOGRAPHY_VARS, } from "../styleProps";
|
|
3
|
-
let { left, right, children, expandLabel = "Expand sidebar", collapseLabel = "Collapse sidebar", class: className, ...restProps } = $props();
|
|
3
|
+
let { left, right, children, overlay = false, sticky = false, sheetButton = "tab", open = $bindable(undefined), expandLabel = "Expand sidebar", collapseLabel = "Collapse sidebar", class: className, ...restProps } = $props();
|
|
4
4
|
/* The shorthands sidebar's own CSS backs, one group per mixin it
|
|
5
5
|
includes. The Props type is derived from this same array, so what the
|
|
6
6
|
component accepts and what it emits cannot drift apart. */
|
|
@@ -12,14 +12,24 @@ const el = $derived(elementProps(restProps, "sidebar", [
|
|
|
12
12
|
...SIDEBAR_VARS,
|
|
13
13
|
"width",
|
|
14
14
|
]));
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
/* One piece of state, two layouts. The wide rail and the compact sheet
|
|
16
|
+
disagree about what "untouched" should look like -- the rail starts open,
|
|
17
|
+
the sheet starts closed -- so `undefined` stands for "this layout's
|
|
18
|
+
default" and each layout resolves it its own way. The moment anything
|
|
19
|
+
(either built-in toggle, or a caller through `bind:open`) writes a
|
|
20
|
+
boolean, both layouts read that same boolean, which is what keeps
|
|
21
|
+
programmatic control and the internal toggles in sync. */
|
|
22
|
+
const expandedBar = $derived(open ?? true);
|
|
23
|
+
const expandedHamburger = $derived(open ?? false);
|
|
17
24
|
</script>
|
|
18
25
|
|
|
19
26
|
<aside
|
|
20
27
|
class={["sidebar", className]}
|
|
21
28
|
class:right
|
|
22
29
|
class:left
|
|
30
|
+
class:overlay
|
|
31
|
+
class:sticky
|
|
32
|
+
class:button-sheet-toggle={sheetButton === "button"}
|
|
23
33
|
class:expandedHamburger
|
|
24
34
|
class:expandedBar
|
|
25
35
|
{...el}
|
|
@@ -28,25 +38,53 @@ let expandedBar = $state(true);
|
|
|
28
38
|
class:expander={!expandedHamburger}
|
|
29
39
|
class:close={expandedHamburger}
|
|
30
40
|
aria-label={expandedHamburger ? collapseLabel : expandLabel}
|
|
41
|
+
aria-expanded={expandedHamburger}
|
|
31
42
|
data-audit-action="toggle-sidebar-sheet"
|
|
32
|
-
onclick={() => (
|
|
43
|
+
onclick={() => (open = !expandedHamburger)}
|
|
33
44
|
></button>
|
|
34
45
|
<div class="content">
|
|
35
46
|
{@render children?.()}
|
|
36
47
|
</div>
|
|
37
48
|
<label class="edge-bar">
|
|
38
49
|
<button
|
|
39
|
-
onclick={() => (
|
|
50
|
+
onclick={() => (open = !expandedBar)}
|
|
40
51
|
class="expander"
|
|
41
52
|
class:expander={!expandedBar}
|
|
42
53
|
class:close={expandedBar}
|
|
43
|
-
aria-label={expandedBar ?
|
|
54
|
+
aria-label={expandedBar ? collapseLabel : expandLabel}
|
|
55
|
+
aria-expanded={expandedBar}
|
|
44
56
|
data-audit-action="toggle-sidebar-rail"
|
|
45
57
|
></button>
|
|
46
58
|
</label>
|
|
47
59
|
</aside>
|
|
48
60
|
|
|
49
61
|
<style>@charset "UTF-8";
|
|
62
|
+
/* Per-level heading (h1-h6) box/typography properties, resolved to PRIVATE,
|
|
63
|
+
inheriting custom properties (--_h1-color, --_h2-font-size, etc) rather than
|
|
64
|
+
assigned directly to a matched h1-h6 selector. Nothing in this file matches
|
|
65
|
+
an actual heading element anymore -- see typography-context.css (vars/), which
|
|
66
|
+
is the single, global, :where()-scoped rule that actually reads these vars
|
|
67
|
+
and applies them to real elements. That split is what lets a component
|
|
68
|
+
nested arbitrarily deep (through <article>, <Tile>, whatever) still get the
|
|
69
|
+
nearest ancestor's heading styling: these are ordinary inherited custom
|
|
70
|
+
properties, so DOM proximity decides which component's values apply, not
|
|
71
|
+
selector specificity or stylesheet order. See AGENTS-INTERNAL.md. */
|
|
72
|
+
/* All six heading levels' context vars in one call -- what every
|
|
73
|
+
"typography container" component (Container, Card, Tile, TextLayout, ...)
|
|
74
|
+
includes on itself once, alongside the shared --_heading-* vars below. */
|
|
75
|
+
/* Shared prose-measure group: p, blockquote, dl, ul, ol. li/dt/dd don't get
|
|
76
|
+
their own vars -- they're never wider than their ul/ol/dl parent's own
|
|
77
|
+
max-width box, so they're already bounded for free by ordinary block
|
|
78
|
+
layout, not by anything selector-based reaching them individually. */
|
|
79
|
+
/* p specifically also gets the fuller typography-props-bare treatment
|
|
80
|
+
(paragraph-prefixed), which -- same as in the original selector-based
|
|
81
|
+
version -- is declared/consumed AFTER the shared prose vars above, so it
|
|
82
|
+
wins for whichever properties both groups set. */
|
|
83
|
+
/* The first paragraph in a typography context (p:first-of-type, or a p right
|
|
84
|
+
after a heading) gets its own, slightly different treatment -- larger lede
|
|
85
|
+
text, no top margin. Structural matching (:first-of-type, `h1 + p`) still
|
|
86
|
+
has to happen where the real element is, in the global consumer rule; only
|
|
87
|
+
the VALUES move to context vars here. */
|
|
50
88
|
/* Logical, not top/bottom/left/right: margin-block/margin-inline already take
|
|
51
89
|
one value (both sides equal) or two (start end), so there is no separate
|
|
52
90
|
"which side" prop to remember and no 4-value shorthand order to get wrong.
|
|
@@ -55,6 +93,18 @@ let expandedBar = $state(true);
|
|
|
55
93
|
day this mixin got added to a new component. Callers opt in with an actual
|
|
56
94
|
value. (Container does not use this mixin: it wants --gap/auto as its
|
|
57
95
|
defaults, which do not fit the "0 unless asked" contract here.) */
|
|
96
|
+
/* The width at or below which a Sidebar swaps its grab-bar rail for the sheet
|
|
97
|
+
affordance -- a floating button that opens a panel over the content.
|
|
98
|
+
|
|
99
|
+
Page has to agree with Sidebar about this, because Page is what floats the
|
|
100
|
+
aside out of flow and reserves the strip of padding the sheet button sits
|
|
101
|
+
in. They used to disagree: Sidebar switched at 512px and Page at 600px,
|
|
102
|
+
which left a band between 513 and 600 where Page had already lifted the
|
|
103
|
+
aside out of the layout while the Sidebar inside it was still showing a
|
|
104
|
+
rail -- a grab bar floating on top of the content, which is the affordance
|
|
105
|
+
we deliberately avoid everywhere else.
|
|
106
|
+
|
|
107
|
+
One definition, used by both, so they cannot drift again. */
|
|
58
108
|
/* Warning: because we define a fallback
|
|
59
109
|
media query, the media query can override the container
|
|
60
110
|
if we stack a bunch of these in a row and aren't thoughtful about the order.
|
|
@@ -63,6 +113,9 @@ let expandedBar = $state(true);
|
|
|
63
113
|
*/
|
|
64
114
|
/* Convenience groupings */
|
|
65
115
|
aside {
|
|
116
|
+
/* Set only by `sheetButton="button"` (below); cleared here so it never
|
|
117
|
+
leaks in from an enclosing sidebar. */
|
|
118
|
+
--_sidebar-sheet-edge-override: initial;
|
|
66
119
|
--link-bg: var(--sidebar-link-bg, var(--surface-link-bg, inherit));
|
|
67
120
|
--link-fg: var(--sidebar-link-fg, var(--surface-link-fg, inherit));
|
|
68
121
|
--_bg-mix-color: var(--sidebar-bg-mix-color, var(--surface-bg-mix-color, var(--bg-mix-color, transparent)));
|
|
@@ -294,38 +347,491 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
|
|
|
294
347
|
}
|
|
295
348
|
|
|
296
349
|
/* Expander doo-dad */
|
|
350
|
+
/* The sheet affordance: a floating panel you summon with a button and
|
|
351
|
+
dismiss when you are done with it.
|
|
352
|
+
|
|
353
|
+
Used by the compact layout, and by `overlay` at any width. Overlay wants
|
|
354
|
+
this rather than the rail: a panel that starts open on top of the content
|
|
355
|
+
is nonsense, and a grab-bar rail floating over the content is a strange
|
|
356
|
+
thing to ask anyone to grab. A button that opens a sheet is the
|
|
357
|
+
affordance people already know.
|
|
358
|
+
|
|
359
|
+
It keys on .expandedHamburger, which resolves `undefined` to false -- so
|
|
360
|
+
a sheet starts shut, where the rail starts open. That falls out of the
|
|
361
|
+
shared `open` state for free. */
|
|
297
362
|
/* Responsive sidebar... */
|
|
298
363
|
@container (min-width: 513px) {
|
|
299
|
-
/*
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
aside {
|
|
364
|
+
/* The rail layout. Everything here is scoped away from `overlay`, which
|
|
365
|
+
uses the sheet affordance at every width instead. */
|
|
366
|
+
aside:not(.overlay) {
|
|
303
367
|
transition: width var(--sidebar-transition);
|
|
304
368
|
position: relative;
|
|
305
369
|
height: 100%;
|
|
306
370
|
width: var(--grab-bar-width);
|
|
307
371
|
}
|
|
308
|
-
aside.expandedBar {
|
|
372
|
+
aside:not(.overlay).expandedBar {
|
|
309
373
|
width: calc(var(--sidebar-width) + var(--grab-bar-width));
|
|
310
374
|
}
|
|
311
|
-
aside .content {
|
|
375
|
+
aside:not(.overlay) .content {
|
|
312
376
|
transition: transform var(--sidebar-transition);
|
|
313
377
|
transform: translateX(-110%);
|
|
314
378
|
}
|
|
315
|
-
aside.right .content {
|
|
379
|
+
aside:not(.overlay).right .content {
|
|
316
380
|
transform: translateX(100%);
|
|
317
381
|
}
|
|
318
|
-
|
|
319
|
-
aside.expandedBar .content {
|
|
382
|
+
aside:not(.overlay).expandedBar .content {
|
|
320
383
|
transform: translateX(0);
|
|
321
384
|
position: absolute;
|
|
322
385
|
top: 0;
|
|
323
386
|
left: 0;
|
|
324
387
|
width: var(--sidebar-width);
|
|
325
388
|
}
|
|
326
|
-
aside.expandedBar.right .content {
|
|
389
|
+
aside:not(.overlay).expandedBar.right .content {
|
|
390
|
+
left: auto;
|
|
391
|
+
right: 0;
|
|
392
|
+
}
|
|
393
|
+
aside.overlay {
|
|
394
|
+
/* The sheet and its button are absolutely positioned, so the sidebar has
|
|
395
|
+
to be their containing block. Without this they resolve against
|
|
396
|
+
whatever positioned ancestor happens to be up the tree -- inside <Page>
|
|
397
|
+
that is Page's own aside and it looks fine, but drop a Sidebar into a
|
|
398
|
+
Card or a Container and the button escapes to the top-left of the page,
|
|
399
|
+
over unrelated content.
|
|
400
|
+
|
|
401
|
+
`overflow` has to be released in the same breath. Every child here is
|
|
402
|
+
out of flow, so the aside computes to zero height -- harmless while it
|
|
403
|
+
was not a containing block, but the moment it becomes one the inherited
|
|
404
|
+
`overflow: hidden` clips the button and the sheet away to nothing.
|
|
405
|
+
Nothing needs clipping here: the shut sheet is hidden by opacity and
|
|
406
|
+
pointer-events, not by the box. */
|
|
407
|
+
/* Sized here rather than on the button, because the panel needs them too
|
|
408
|
+
-- see the padding it reserves below. */
|
|
409
|
+
--_sidebar-expander-width: max(
|
|
410
|
+
var(--sidebar-icon-width, 0.65rem),
|
|
411
|
+
var(--icon-size, 32px)
|
|
412
|
+
);
|
|
413
|
+
--_sidebar-expander-height: max(
|
|
414
|
+
var(--sidebar-icon-height, 1rem),
|
|
415
|
+
var(--icon-size, 32px)
|
|
416
|
+
);
|
|
417
|
+
/* The sheet button's corners. Its "edge" corners are the pair on the
|
|
418
|
+
side it attaches to -- the page edge while shut, the panel's inner edge
|
|
419
|
+
while open -- and default to 0, which is what makes it read as a tab
|
|
420
|
+
fastened to that edge. `sheetButton="button"` rounds them like the
|
|
421
|
+
other two, for a sheet that does not sit against an edge. */
|
|
422
|
+
--_sidebar-sheet-button-radius: var(--circle-button-radius, var(--mini-button-radius, var(--button-radius, var(--radius, 50%))));
|
|
423
|
+
--_sidebar-sheet-expand-edge-radius: var(
|
|
424
|
+
--sidebar-sheet-expand-edge-radius,
|
|
425
|
+
var(
|
|
426
|
+
--_sidebar-sheet-edge-override,
|
|
427
|
+
var(--sidebar-sheet-edge-radius, 0)
|
|
428
|
+
)
|
|
429
|
+
);
|
|
430
|
+
--_sidebar-sheet-collapse-edge-radius: var(
|
|
431
|
+
--sidebar-sheet-collapse-edge-radius,
|
|
432
|
+
var(
|
|
433
|
+
--_sidebar-sheet-edge-override,
|
|
434
|
+
var(--sidebar-sheet-edge-radius, 0)
|
|
435
|
+
)
|
|
436
|
+
);
|
|
437
|
+
position: relative;
|
|
438
|
+
overflow: visible;
|
|
439
|
+
background: transparent;
|
|
440
|
+
width: calc(var(--gap) + max(var(--sidebar-icon-width, 0.65rem), var(--icon-size, 32px)));
|
|
441
|
+
flex: 0 0 auto;
|
|
442
|
+
/* Every child is out of flow, so the aside has no content to hold it
|
|
443
|
+
open and the base `height: 100%` resolves against an auto-height
|
|
444
|
+
parent -- which is to say, nothing. Stretch to the row instead, so the
|
|
445
|
+
aside is as tall as whatever it sits beside, and the open sheet's own
|
|
446
|
+
`height: 100%` has something real to measure against. */
|
|
447
|
+
height: auto;
|
|
448
|
+
align-self: stretch;
|
|
449
|
+
}
|
|
450
|
+
aside.overlay .edge-bar {
|
|
451
|
+
display: none;
|
|
452
|
+
}
|
|
453
|
+
aside.overlay.right, aside.overlay.left {
|
|
454
|
+
border-left: none;
|
|
455
|
+
border-right: none;
|
|
456
|
+
}
|
|
457
|
+
aside.overlay > .content {
|
|
458
|
+
position: absolute;
|
|
459
|
+
top: 0;
|
|
460
|
+
left: 0;
|
|
461
|
+
width: var(--sidebar-width);
|
|
462
|
+
/* Above a sticky Table head (z-index 2), which a sheet beside a long table
|
|
463
|
+
otherwise slides under. */
|
|
464
|
+
z-index: var(--sidebar-overlay-z-index, 3);
|
|
465
|
+
transform: translateX(-100%);
|
|
466
|
+
opacity: 0;
|
|
467
|
+
background: transparent;
|
|
468
|
+
pointer-events: none;
|
|
469
|
+
transition: transform var(--sidebar-transition) ease-in-out, opacity var(--sidebar-transition) ease-in-out;
|
|
470
|
+
padding: var(--padding);
|
|
471
|
+
/* The close button floats over the panel's inner edge, so the panel has
|
|
472
|
+
to keep that strip clear or the first nav item sits underneath it.
|
|
473
|
+
|
|
474
|
+
Reserved on the inline axis, not the block one: a nav wants the full
|
|
475
|
+
height, and a blank band across the top of it to clear one button is
|
|
476
|
+
a poor trade. The cost is that the whole column is a button's width
|
|
477
|
+
narrower, even well below the button -- override
|
|
478
|
+
--sidebar-sheet-content-inset to 0 and set your own padding if you
|
|
479
|
+
would rather have the width back. */
|
|
480
|
+
padding-inline-end: var(--sidebar-sheet-content-inset, calc(var(--padding) + var(--_sidebar-expander-width)));
|
|
481
|
+
}
|
|
482
|
+
aside.overlay.left > .content {
|
|
483
|
+
border-right: var(--border-width) var(--border-style) var(--border-color);
|
|
484
|
+
}
|
|
485
|
+
aside.overlay {
|
|
486
|
+
/* A right-hand sheet slides out of the right edge, not the left -- and
|
|
487
|
+
its button lands on the panel's left edge, so the reserved strip
|
|
488
|
+
mirrors with it. */
|
|
489
|
+
}
|
|
490
|
+
aside.overlay.right > .content {
|
|
327
491
|
left: auto;
|
|
328
492
|
right: 0;
|
|
493
|
+
transform: translateX(100%);
|
|
494
|
+
border-left: var(--border-width) var(--border-style) var(--border-color);
|
|
495
|
+
padding-inline-end: var(--padding);
|
|
496
|
+
padding-inline-start: var(--sidebar-sheet-content-inset, calc(var(--padding) + var(--_sidebar-expander-width)));
|
|
497
|
+
}
|
|
498
|
+
aside.overlay.expandedHamburger > .content {
|
|
499
|
+
transform: translateX(0);
|
|
500
|
+
height: 100%;
|
|
501
|
+
opacity: 1;
|
|
502
|
+
pointer-events: all;
|
|
503
|
+
box-shadow: var(--sidebar-overlay-box-shadow, var(--overlay-box-shadow, 0 0 var(--space, 8px) rgba(127, 127, 127, 0.4)));
|
|
504
|
+
--link-bg: var(--sidebar-link-bg, var(--surface-link-bg, inherit));
|
|
505
|
+
--link-fg: var(--sidebar-link-fg, var(--surface-link-fg, inherit));
|
|
506
|
+
--_bg-mix-color: var(--sidebar-bg-mix-color, var(--surface-bg-mix-color, var(--bg-mix-color, transparent)));
|
|
507
|
+
--_bg-mix-amount: var(--sidebar-bg-mix-amount, var(--surface-bg-mix-amount, var(--bg-mix-amount, 0%)));
|
|
508
|
+
--_fg-mix-color: var(--sidebar-fg-mix-color, var(--surface-fg-mix-color, var(--fg-mix-color, transparent)));
|
|
509
|
+
--_fg-mix-amount: var(--sidebar-fg-mix-amount, var(--surface-fg-mix-amount, var(--fg-mix-amount, 0%)));
|
|
510
|
+
--_bg-base: var(--sidebar-bg, var(--surface-bg, var(--bg, unset)));
|
|
511
|
+
--_fg-base: var(--sidebar-fg, var(--surface-fg, var(--fg, unset)));
|
|
512
|
+
--_background-color: color-mix(
|
|
513
|
+
in srgb,
|
|
514
|
+
var(--_bg-base),
|
|
515
|
+
var(--_bg-mix-color) var(--_bg-mix-amount)
|
|
516
|
+
);
|
|
517
|
+
--_color: color-mix(
|
|
518
|
+
in srgb,
|
|
519
|
+
var(--_fg-base),
|
|
520
|
+
var(--_fg-mix-color) var(--_fg-mix-amount)
|
|
521
|
+
);
|
|
522
|
+
--muted-color: var(--muted-color-override, var(--_background-color));
|
|
523
|
+
--primary-text-fg: var(
|
|
524
|
+
--primary-text-fg-override,
|
|
525
|
+
color-mix(
|
|
526
|
+
in srgb,
|
|
527
|
+
var(--_color),
|
|
528
|
+
var(--primary-color)
|
|
529
|
+
var(--primary-text-amount, var(--text-amount, 50%))
|
|
530
|
+
)
|
|
531
|
+
);
|
|
532
|
+
--secondary-text-fg: var(
|
|
533
|
+
--secondary-text-fg-override,
|
|
534
|
+
color-mix(
|
|
535
|
+
in srgb,
|
|
536
|
+
var(--_color),
|
|
537
|
+
var(--secondary-color)
|
|
538
|
+
var(--secondary-text-amount, var(--text-amount, 50%))
|
|
539
|
+
)
|
|
540
|
+
);
|
|
541
|
+
--info-text-fg: var(
|
|
542
|
+
--info-text-fg-override,
|
|
543
|
+
color-mix(
|
|
544
|
+
in srgb,
|
|
545
|
+
var(--_color),
|
|
546
|
+
var(--info-color)
|
|
547
|
+
var(--info-text-amount, var(--text-amount, 50%))
|
|
548
|
+
)
|
|
549
|
+
);
|
|
550
|
+
--success-text-fg: var(
|
|
551
|
+
--success-text-fg-override,
|
|
552
|
+
color-mix(
|
|
553
|
+
in srgb,
|
|
554
|
+
var(--_color),
|
|
555
|
+
var(--success-color)
|
|
556
|
+
var(--success-text-amount, var(--text-amount, 50%))
|
|
557
|
+
)
|
|
558
|
+
);
|
|
559
|
+
--warning-text-fg: var(
|
|
560
|
+
--warning-text-fg-override,
|
|
561
|
+
color-mix(
|
|
562
|
+
in srgb,
|
|
563
|
+
var(--_color),
|
|
564
|
+
var(--warning-color)
|
|
565
|
+
var(--warning-text-amount, var(--text-amount, 50%))
|
|
566
|
+
)
|
|
567
|
+
);
|
|
568
|
+
--danger-text-fg: var(
|
|
569
|
+
--danger-text-fg-override,
|
|
570
|
+
color-mix(
|
|
571
|
+
in srgb,
|
|
572
|
+
var(--_color),
|
|
573
|
+
var(--danger-color)
|
|
574
|
+
var(--danger-text-amount, var(--text-amount, 50%))
|
|
575
|
+
)
|
|
576
|
+
);
|
|
577
|
+
--muted-text-fg: var(
|
|
578
|
+
--muted-text-fg-override,
|
|
579
|
+
color-mix(
|
|
580
|
+
in srgb,
|
|
581
|
+
var(--_color),
|
|
582
|
+
var(--muted-color)
|
|
583
|
+
var(--muted-text-amount, var(--text-amount, 50%))
|
|
584
|
+
)
|
|
585
|
+
);
|
|
586
|
+
background-color: var(--_background-color);
|
|
587
|
+
color: var(--_color);
|
|
588
|
+
}
|
|
589
|
+
aside.overlay > button {
|
|
590
|
+
transition: left var(--sidebar-transition);
|
|
591
|
+
transform: translateX(0);
|
|
592
|
+
z-index: calc(var(--sidebar-overlay-z-index, 3) + 1);
|
|
593
|
+
position: absolute;
|
|
594
|
+
opacity: 1;
|
|
595
|
+
pointer-events: all;
|
|
596
|
+
display: block;
|
|
597
|
+
top: var(--padding);
|
|
598
|
+
left: 0;
|
|
599
|
+
border-radius: var(--_sidebar-sheet-button-radius);
|
|
600
|
+
border-top-left-radius: var(--_sidebar-sheet-expand-edge-radius);
|
|
601
|
+
border-bottom-left-radius: var(--_sidebar-sheet-expand-edge-radius);
|
|
602
|
+
border: var(--circle-button-border, var(--mini-button-border));
|
|
603
|
+
width: var(--_sidebar-expander-width);
|
|
604
|
+
height: var(--_sidebar-expander-height);
|
|
605
|
+
--link-bg: var(--circle-button-link-bg, var(--mini-button-link-bg, var(--button-link-bg, var(--control-link-bg, var(--secondary-link-bg, inherit)))));
|
|
606
|
+
--link-fg: var(--circle-button-link-fg, var(--mini-button-link-fg, var(--button-link-fg, var(--control-link-fg, var(--secondary-link-fg, inherit)))));
|
|
607
|
+
--_bg-mix-color: var(--circle-button-bg-mix-color, var(--mini-button-bg-mix-color, var(--button-bg-mix-color, var(--control-bg-mix-color, var(--secondary-bg-mix-color, var(--bg-mix-color, transparent))))));
|
|
608
|
+
--_bg-mix-amount: var(--circle-button-bg-mix-amount, var(--mini-button-bg-mix-amount, var(--button-bg-mix-amount, var(--control-bg-mix-amount, var(--secondary-bg-mix-amount, var(--bg-mix-amount, 0%))))));
|
|
609
|
+
--_fg-mix-color: var(--circle-button-fg-mix-color, var(--mini-button-fg-mix-color, var(--button-fg-mix-color, var(--control-fg-mix-color, var(--secondary-fg-mix-color, var(--fg-mix-color, transparent))))));
|
|
610
|
+
--_fg-mix-amount: var(--circle-button-fg-mix-amount, var(--mini-button-fg-mix-amount, var(--button-fg-mix-amount, var(--control-fg-mix-amount, var(--secondary-fg-mix-amount, var(--fg-mix-amount, 0%))))));
|
|
611
|
+
--_bg-base: var(--circle-button-bg, var(--mini-button-bg, var(--button-bg, var(--control-bg, var(--secondary-bg, var(--bg, unset))))));
|
|
612
|
+
--_fg-base: var(--circle-button-fg, var(--mini-button-fg, var(--button-fg, var(--control-fg, var(--secondary-fg, var(--fg, unset))))));
|
|
613
|
+
--_background-color: color-mix(
|
|
614
|
+
in srgb,
|
|
615
|
+
var(--_bg-base),
|
|
616
|
+
var(--_bg-mix-color) var(--_bg-mix-amount)
|
|
617
|
+
);
|
|
618
|
+
--_color: color-mix(
|
|
619
|
+
in srgb,
|
|
620
|
+
var(--_fg-base),
|
|
621
|
+
var(--_fg-mix-color) var(--_fg-mix-amount)
|
|
622
|
+
);
|
|
623
|
+
--muted-color: var(--muted-color-override, var(--_background-color));
|
|
624
|
+
--primary-text-fg: var(
|
|
625
|
+
--primary-text-fg-override,
|
|
626
|
+
color-mix(
|
|
627
|
+
in srgb,
|
|
628
|
+
var(--_color),
|
|
629
|
+
var(--primary-color)
|
|
630
|
+
var(--primary-text-amount, var(--text-amount, 50%))
|
|
631
|
+
)
|
|
632
|
+
);
|
|
633
|
+
--secondary-text-fg: var(
|
|
634
|
+
--secondary-text-fg-override,
|
|
635
|
+
color-mix(
|
|
636
|
+
in srgb,
|
|
637
|
+
var(--_color),
|
|
638
|
+
var(--secondary-color)
|
|
639
|
+
var(--secondary-text-amount, var(--text-amount, 50%))
|
|
640
|
+
)
|
|
641
|
+
);
|
|
642
|
+
--info-text-fg: var(
|
|
643
|
+
--info-text-fg-override,
|
|
644
|
+
color-mix(
|
|
645
|
+
in srgb,
|
|
646
|
+
var(--_color),
|
|
647
|
+
var(--info-color)
|
|
648
|
+
var(--info-text-amount, var(--text-amount, 50%))
|
|
649
|
+
)
|
|
650
|
+
);
|
|
651
|
+
--success-text-fg: var(
|
|
652
|
+
--success-text-fg-override,
|
|
653
|
+
color-mix(
|
|
654
|
+
in srgb,
|
|
655
|
+
var(--_color),
|
|
656
|
+
var(--success-color)
|
|
657
|
+
var(--success-text-amount, var(--text-amount, 50%))
|
|
658
|
+
)
|
|
659
|
+
);
|
|
660
|
+
--warning-text-fg: var(
|
|
661
|
+
--warning-text-fg-override,
|
|
662
|
+
color-mix(
|
|
663
|
+
in srgb,
|
|
664
|
+
var(--_color),
|
|
665
|
+
var(--warning-color)
|
|
666
|
+
var(--warning-text-amount, var(--text-amount, 50%))
|
|
667
|
+
)
|
|
668
|
+
);
|
|
669
|
+
--danger-text-fg: var(
|
|
670
|
+
--danger-text-fg-override,
|
|
671
|
+
color-mix(
|
|
672
|
+
in srgb,
|
|
673
|
+
var(--_color),
|
|
674
|
+
var(--danger-color)
|
|
675
|
+
var(--danger-text-amount, var(--text-amount, 50%))
|
|
676
|
+
)
|
|
677
|
+
);
|
|
678
|
+
--muted-text-fg: var(
|
|
679
|
+
--muted-text-fg-override,
|
|
680
|
+
color-mix(
|
|
681
|
+
in srgb,
|
|
682
|
+
var(--_color),
|
|
683
|
+
var(--muted-color)
|
|
684
|
+
var(--muted-text-amount, var(--text-amount, 50%))
|
|
685
|
+
)
|
|
686
|
+
);
|
|
687
|
+
background-color: var(--_background-color);
|
|
688
|
+
color: var(--_color);
|
|
689
|
+
cursor: var(--circle-button-cursor, var(--mini-button-cursor, var(--button-cursor, var(--control-cursor, var(--cursor, pointer)))));
|
|
690
|
+
transition: filter var(--circle-button-transition, var(--mini-button-transition, var(--button-transition, var(--control-transition, var(--transition, 300ms))))), background-color var(--circle-button-transition, var(--mini-button-transition, var(--button-transition, var(--control-transition, var(--transition, 300ms))))), border-color var(--circle-button-transition, var(--mini-button-transition, var(--button-transition, var(--control-transition, var(--transition, 300ms))))), box-shadow var(--circle-button-transition, var(--mini-button-transition, var(--button-transition, var(--control-transition, var(--transition, 300ms))))), transform var(--circle-button-transition, var(--mini-button-transition, var(--button-transition, var(--control-transition, var(--transition, 300ms)))));
|
|
691
|
+
}
|
|
692
|
+
aside.overlay > button:hover {
|
|
693
|
+
filter: var(--circle-button-hover-filter, var(--mini-button-hover-filter, var(--button-hover-filter, var(--control-hover-filter, var(--hover-filter, brightness(1.05))))));
|
|
694
|
+
transform: var(--circle-button-hover-transform, var(--mini-button-hover-transform, var(--button-hover-transform, var(--control-hover-transform, var(--hover-transform, none)))));
|
|
695
|
+
color: var(--_color);
|
|
696
|
+
background-color: color-mix(in oklch, var(--_background-color) var(--hover-base-color-percentage, 90%), var(--hover-color-mix, white) calc(100% - var(--hover-base-color-percentage, 90%)));
|
|
697
|
+
box-shadow: var(--circle-button-hover-box-shadow, var(--mini-button-hover-box-shadow, var(--button-hover-box-shadow, var(--control-hover-box-shadow, var(--hover-box-shadow, var(--_box-shadow, none))))));
|
|
698
|
+
}
|
|
699
|
+
aside.overlay > button:active {
|
|
700
|
+
filter: var(--circle-button-active-filter, var(--mini-button-active-filter, var(--button-active-filter, var(--control-active-filter, var(--active-filter, brightness(0.9))))));
|
|
701
|
+
transform: var(--circle-button-active-transform, var(--mini-button-active-transform, var(--button-active-transform, var(--control-active-transform, var(--active-transform, none)))));
|
|
702
|
+
background-color: color-mix(in oklch, var(--_background-color) var(--active-base-color-percentage, 90%), var(--active-color-mix, black) calc(100% - var(--active-base-color-percentage, 90%)));
|
|
703
|
+
box-shadow: var(--circle-button-active-box-shadow, var(--mini-button-active-box-shadow, var(--button-active-box-shadow, var(--control-active-box-shadow, var(--active-box-shadow, var(--_box-shadow, none))))));
|
|
704
|
+
}
|
|
705
|
+
aside.overlay > button:disabled {
|
|
706
|
+
cursor: var(--circle-button-disabled-cursor, var(--mini-button-disabled-cursor, var(--button-disabled-cursor, var(--control-disabled-cursor, var(--disabled-cursor, not-allowed)))));
|
|
707
|
+
transform: var(--circle-button-disabled-transform, var(--mini-button-disabled-transform, var(--button-disabled-transform, var(--control-disabled-transform, var(--disabled-transform, none)))));
|
|
708
|
+
filter: var(--circle-button-disabled-filter, var(--mini-button-disabled-filter, var(--button-disabled-filter, var(--control-disabled-filter, var(--disabled-filter, grayscale(0.5))))));
|
|
709
|
+
background-color: color-mix(in oklch, var(--_background-color) var(--disabled-base-color-percentage, 50%), var(--disabled-color-mix, gray) calc(100% - var(--disabled-base-color-percentage, 50%)));
|
|
710
|
+
box-shadow: var(--circle-button-disabled-box-shadow, var(--mini-button-disabled-box-shadow, var(--button-disabled-box-shadow, var(--control-disabled-box-shadow, var(--disabled-box-shadow, var(--_box-shadow, none))))));
|
|
711
|
+
}
|
|
712
|
+
aside.overlay > button:focus-visible {
|
|
713
|
+
outline: var(--focus-color, -webkit-focus-ring-color) auto 1px;
|
|
714
|
+
outline-offset: var(--focus-outline-offset, 2px);
|
|
715
|
+
box-shadow: var(--focus-ring-box-shadow, 0 0 0 3px var(--focus-shadow-color, rgba(100, 150, 250, 0.5)));
|
|
716
|
+
}
|
|
717
|
+
aside.overlay > button::after {
|
|
718
|
+
color: var(--circle-button-fg, var(--mini-button-fg, currentColor));
|
|
719
|
+
filter: var(--sidebar-mobile-icon-filter, none);
|
|
720
|
+
}
|
|
721
|
+
aside.overlay > button.close {
|
|
722
|
+
/* Flush inside the panel's inner edge. Adding --padding here pushed the
|
|
723
|
+
button a padding's worth further in, which left it straddling the
|
|
724
|
+
edge -- part of it hanging outside the panel -- while the content
|
|
725
|
+
inset below still reserved the full button width, so the gap between
|
|
726
|
+
the button and the first nav item came out wider than intended.
|
|
727
|
+
Either the button pops out of the panel or it sits inside it; this
|
|
728
|
+
sits inside it, and the inset is then exactly button + one gutter. */
|
|
729
|
+
left: calc(var(--sidebar-width) - var(--_sidebar-expander-width));
|
|
730
|
+
border-radius: var(--_sidebar-sheet-button-radius);
|
|
731
|
+
border-top-right-radius: var(--_sidebar-sheet-collapse-edge-radius);
|
|
732
|
+
border-bottom-right-radius: var(--_sidebar-sheet-collapse-edge-radius);
|
|
733
|
+
}
|
|
734
|
+
aside.overlay {
|
|
735
|
+
/* Everything above anchors the button to the left, which is right for a
|
|
736
|
+
left-hand sidebar and nonsense for a right-hand one: the sheet slides
|
|
737
|
+
out of the right edge, so the button has to hug that edge and then step
|
|
738
|
+
*inwards* -- leftwards -- by the panel's width when it opens. Anchored
|
|
739
|
+
from the left it stepped the other way, off the far side of the page
|
|
740
|
+
entirely, where it could not even be clicked. The flattened corners
|
|
741
|
+
mirror too, so the button still reads as attached to its own edge. */
|
|
742
|
+
}
|
|
743
|
+
aside.overlay.right > button {
|
|
744
|
+
left: auto;
|
|
745
|
+
right: 0;
|
|
746
|
+
border-radius: var(--_sidebar-sheet-button-radius);
|
|
747
|
+
border-top-right-radius: var(--_sidebar-sheet-expand-edge-radius);
|
|
748
|
+
border-bottom-right-radius: var(--_sidebar-sheet-expand-edge-radius);
|
|
749
|
+
}
|
|
750
|
+
aside.overlay.right > button.close {
|
|
751
|
+
left: auto;
|
|
752
|
+
right: calc(var(--sidebar-width) - var(--_sidebar-expander-width));
|
|
753
|
+
border-radius: var(--_sidebar-sheet-button-radius);
|
|
754
|
+
border-top-left-radius: var(--_sidebar-sheet-collapse-edge-radius);
|
|
755
|
+
border-bottom-left-radius: var(--_sidebar-sheet-collapse-edge-radius);
|
|
756
|
+
}
|
|
757
|
+
aside.overlay {
|
|
758
|
+
/* `sticky`: the sheet follows the reader down the page instead of
|
|
759
|
+
stretching to the row.
|
|
760
|
+
|
|
761
|
+
Sticky needs the sheet back *in flow* -- an absolutely positioned box
|
|
762
|
+
cannot stick -- but the aside is only a button's width wide and the
|
|
763
|
+
row's full height, and that height is exactly the range the sheet
|
|
764
|
+
should be free to travel. So the aside becomes a one-cell grid as tall
|
|
765
|
+
as its row, and the sheet and its button share that cell, each sticky
|
|
766
|
+
within it.
|
|
767
|
+
|
|
768
|
+
Horizontal placement moves from `left`/`right` onto margins, because on
|
|
769
|
+
a sticky box `left` and `right` stop being offsets and become sticky
|
|
770
|
+
insets -- they would pin the button against a sideways-scrolling
|
|
771
|
+
ancestor rather than place it. The margins reproduce the absolute
|
|
772
|
+
geometry exactly: the aside is `--gap` plus one button wide, and a
|
|
773
|
+
sheet or button wider than that spills out of its cell on the side
|
|
774
|
+
that faces the content. */
|
|
775
|
+
}
|
|
776
|
+
aside.overlay.sticky {
|
|
777
|
+
--_sidebar-sheet-top: var(--sidebar-sheet-top, var(--padding));
|
|
778
|
+
--_sidebar-aside-width: calc(var(--gap) + var(--_sidebar-expander-width));
|
|
779
|
+
display: grid;
|
|
780
|
+
grid-template-columns: 100%;
|
|
781
|
+
align-content: stretch;
|
|
782
|
+
/* A shut sheet is still in flow now, and would otherwise hold the row
|
|
783
|
+
open to its own height. The aside's width is set explicitly, so
|
|
784
|
+
size containment only ever zeroes out the height it would have
|
|
785
|
+
contributed; the row still stretches it to full height. */
|
|
786
|
+
contain: size;
|
|
787
|
+
}
|
|
788
|
+
aside.overlay.sticky > .content, aside.overlay.sticky > button.expander, aside.overlay.sticky > button.close {
|
|
789
|
+
grid-area: 1/1;
|
|
790
|
+
position: sticky;
|
|
791
|
+
align-self: start;
|
|
792
|
+
justify-self: start;
|
|
793
|
+
left: auto;
|
|
794
|
+
right: auto;
|
|
795
|
+
}
|
|
796
|
+
aside.overlay.sticky > .content {
|
|
797
|
+
top: var(--_sidebar-sheet-top);
|
|
798
|
+
margin-left: 0;
|
|
799
|
+
height: auto;
|
|
800
|
+
/* The reserve is a budget for everything that is not the sheet, rather
|
|
801
|
+
than just the sticky offset: before the sheet sticks -- page scrolled
|
|
802
|
+
to the top -- its top edge is its row's, which may sit well below the
|
|
803
|
+
viewport top, and a sheet sized only from the offset would overflow
|
|
804
|
+
the viewport by exactly that much. Twice the top gives it an even
|
|
805
|
+
margin at both ends once it has stuck. */
|
|
806
|
+
max-height: var(--sidebar-sheet-max-height, calc(100dvh - var(--sidebar-sheet-reserve, calc(2 * var(--_sidebar-sheet-top)))));
|
|
807
|
+
overflow-y: auto;
|
|
808
|
+
/* A column, so a child with `min-height: 0` and its own overflow can
|
|
809
|
+
keep a header or footer in view and scroll only its middle. Children
|
|
810
|
+
that do not opt in keep their size, and the sheet scrolls as a
|
|
811
|
+
whole. */
|
|
812
|
+
display: flex;
|
|
813
|
+
flex-direction: column;
|
|
814
|
+
}
|
|
815
|
+
aside.overlay.sticky.expandedHamburger > .content {
|
|
816
|
+
height: auto;
|
|
817
|
+
}
|
|
818
|
+
aside.overlay.sticky.right > .content {
|
|
819
|
+
margin-left: calc(var(--_sidebar-aside-width) - var(--sidebar-width));
|
|
820
|
+
}
|
|
821
|
+
aside.overlay.sticky > button.expander, aside.overlay.sticky > button.close {
|
|
822
|
+
top: calc(var(--_sidebar-sheet-top) + var(--padding));
|
|
823
|
+
margin-top: var(--padding);
|
|
824
|
+
margin-left: 0;
|
|
825
|
+
transition: margin-left var(--sidebar-transition);
|
|
826
|
+
}
|
|
827
|
+
aside.overlay.sticky > button.close {
|
|
828
|
+
margin-left: calc(var(--sidebar-width) - var(--_sidebar-expander-width));
|
|
829
|
+
}
|
|
830
|
+
aside.overlay.sticky.right > button.expander {
|
|
831
|
+
margin-left: var(--gap);
|
|
832
|
+
}
|
|
833
|
+
aside.overlay.sticky.right > button.close {
|
|
834
|
+
margin-left: calc(var(--_sidebar-aside-width) - var(--sidebar-width));
|
|
329
835
|
}
|
|
330
836
|
.edge-bar {
|
|
331
837
|
--link-bg: var(--grab-bar-link-bg, var(--sidebar-link-bg, var(--surface-link-bg, inherit)));
|
|
@@ -457,43 +963,123 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
|
|
|
457
963
|
aside:has(.edge-bar:hover) .content {
|
|
458
964
|
filter: var(--greyed-out-filter);
|
|
459
965
|
}
|
|
460
|
-
/* Hide
|
|
461
|
-
|
|
462
|
-
button {
|
|
966
|
+
/* Hide the sheet button in rail mode */
|
|
967
|
+
aside:not(.overlay) > button {
|
|
463
968
|
display: none;
|
|
464
969
|
}
|
|
465
970
|
}
|
|
466
971
|
@container (max-width: 512px) {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
972
|
+
aside {
|
|
973
|
+
/* The sheet and its button are absolutely positioned, so the sidebar has
|
|
974
|
+
to be their containing block. Without this they resolve against
|
|
975
|
+
whatever positioned ancestor happens to be up the tree -- inside <Page>
|
|
976
|
+
that is Page's own aside and it looks fine, but drop a Sidebar into a
|
|
977
|
+
Card or a Container and the button escapes to the top-left of the page,
|
|
978
|
+
over unrelated content.
|
|
979
|
+
|
|
980
|
+
`overflow` has to be released in the same breath. Every child here is
|
|
981
|
+
out of flow, so the aside computes to zero height -- harmless while it
|
|
982
|
+
was not a containing block, but the moment it becomes one the inherited
|
|
983
|
+
`overflow: hidden` clips the button and the sheet away to nothing.
|
|
984
|
+
Nothing needs clipping here: the shut sheet is hidden by opacity and
|
|
985
|
+
pointer-events, not by the box. */
|
|
986
|
+
/* Sized here rather than on the button, because the panel needs them too
|
|
987
|
+
-- see the padding it reserves below. */
|
|
988
|
+
--_sidebar-expander-width: max(
|
|
989
|
+
var(--sidebar-icon-width, 0.65rem),
|
|
990
|
+
var(--icon-size, 32px)
|
|
991
|
+
);
|
|
992
|
+
--_sidebar-expander-height: max(
|
|
993
|
+
var(--sidebar-icon-height, 1rem),
|
|
994
|
+
var(--icon-size, 32px)
|
|
995
|
+
);
|
|
996
|
+
/* The sheet button's corners. Its "edge" corners are the pair on the
|
|
997
|
+
side it attaches to -- the page edge while shut, the panel's inner edge
|
|
998
|
+
while open -- and default to 0, which is what makes it read as a tab
|
|
999
|
+
fastened to that edge. `sheetButton="button"` rounds them like the
|
|
1000
|
+
other two, for a sheet that does not sit against an edge. */
|
|
1001
|
+
--_sidebar-sheet-button-radius: var(--circle-button-radius, var(--mini-button-radius, var(--button-radius, var(--radius, 50%))));
|
|
1002
|
+
--_sidebar-sheet-expand-edge-radius: var(
|
|
1003
|
+
--sidebar-sheet-expand-edge-radius,
|
|
1004
|
+
var(
|
|
1005
|
+
--_sidebar-sheet-edge-override,
|
|
1006
|
+
var(--sidebar-sheet-edge-radius, 0)
|
|
1007
|
+
)
|
|
1008
|
+
);
|
|
1009
|
+
--_sidebar-sheet-collapse-edge-radius: var(
|
|
1010
|
+
--sidebar-sheet-collapse-edge-radius,
|
|
1011
|
+
var(
|
|
1012
|
+
--_sidebar-sheet-edge-override,
|
|
1013
|
+
var(--sidebar-sheet-edge-radius, 0)
|
|
1014
|
+
)
|
|
1015
|
+
);
|
|
1016
|
+
position: relative;
|
|
1017
|
+
overflow: visible;
|
|
471
1018
|
background: transparent;
|
|
1019
|
+
width: calc(var(--gap) + max(var(--sidebar-icon-width, 0.65rem), var(--icon-size, 32px)));
|
|
1020
|
+
flex: 0 0 auto;
|
|
1021
|
+
/* Every child is out of flow, so the aside has no content to hold it
|
|
1022
|
+
open and the base `height: 100%` resolves against an auto-height
|
|
1023
|
+
parent -- which is to say, nothing. Stretch to the row instead, so the
|
|
1024
|
+
aside is as tall as whatever it sits beside, and the open sheet's own
|
|
1025
|
+
`height: 100%` has something real to measure against. */
|
|
1026
|
+
height: auto;
|
|
1027
|
+
align-self: stretch;
|
|
1028
|
+
}
|
|
1029
|
+
aside .edge-bar {
|
|
1030
|
+
display: none;
|
|
472
1031
|
}
|
|
473
|
-
.right,
|
|
474
|
-
.left {
|
|
1032
|
+
aside.right, aside.left {
|
|
475
1033
|
border-left: none;
|
|
476
1034
|
border-right: none;
|
|
477
1035
|
}
|
|
478
1036
|
aside > .content {
|
|
1037
|
+
position: absolute;
|
|
1038
|
+
top: 0;
|
|
1039
|
+
left: 0;
|
|
1040
|
+
width: var(--sidebar-width);
|
|
1041
|
+
/* Above a sticky Table head (z-index 2), which a sheet beside a long table
|
|
1042
|
+
otherwise slides under. */
|
|
1043
|
+
z-index: var(--sidebar-overlay-z-index, 3);
|
|
479
1044
|
transform: translateX(-100%);
|
|
480
1045
|
opacity: 0;
|
|
481
1046
|
background: transparent;
|
|
482
1047
|
pointer-events: none;
|
|
483
|
-
transition: transform var(--sidebar-transition) ease-in-out;
|
|
1048
|
+
transition: transform var(--sidebar-transition) ease-in-out, opacity var(--sidebar-transition) ease-in-out;
|
|
484
1049
|
padding: var(--padding);
|
|
1050
|
+
/* The close button floats over the panel's inner edge, so the panel has
|
|
1051
|
+
to keep that strip clear or the first nav item sits underneath it.
|
|
1052
|
+
|
|
1053
|
+
Reserved on the inline axis, not the block one: a nav wants the full
|
|
1054
|
+
height, and a blank band across the top of it to clear one button is
|
|
1055
|
+
a poor trade. The cost is that the whole column is a button's width
|
|
1056
|
+
narrower, even well below the button -- override
|
|
1057
|
+
--sidebar-sheet-content-inset to 0 and set your own padding if you
|
|
1058
|
+
would rather have the width back. */
|
|
1059
|
+
padding-inline-end: var(--sidebar-sheet-content-inset, calc(var(--padding) + var(--_sidebar-expander-width)));
|
|
485
1060
|
}
|
|
486
1061
|
aside.left > .content {
|
|
487
1062
|
border-right: var(--border-width) var(--border-style) var(--border-color);
|
|
488
1063
|
}
|
|
1064
|
+
aside {
|
|
1065
|
+
/* A right-hand sheet slides out of the right edge, not the left -- and
|
|
1066
|
+
its button lands on the panel's left edge, so the reserved strip
|
|
1067
|
+
mirrors with it. */
|
|
1068
|
+
}
|
|
489
1069
|
aside.right > .content {
|
|
1070
|
+
left: auto;
|
|
1071
|
+
right: 0;
|
|
1072
|
+
transform: translateX(100%);
|
|
490
1073
|
border-left: var(--border-width) var(--border-style) var(--border-color);
|
|
1074
|
+
padding-inline-end: var(--padding);
|
|
1075
|
+
padding-inline-start: var(--sidebar-sheet-content-inset, calc(var(--padding) + var(--_sidebar-expander-width)));
|
|
491
1076
|
}
|
|
492
1077
|
aside.expandedHamburger > .content {
|
|
493
1078
|
transform: translateX(0);
|
|
494
1079
|
height: 100%;
|
|
495
1080
|
opacity: 1;
|
|
496
1081
|
pointer-events: all;
|
|
1082
|
+
box-shadow: var(--sidebar-overlay-box-shadow, var(--overlay-box-shadow, 0 0 var(--space, 8px) rgba(127, 127, 127, 0.4)));
|
|
497
1083
|
--link-bg: var(--sidebar-link-bg, var(--surface-link-bg, inherit));
|
|
498
1084
|
--link-fg: var(--sidebar-link-fg, var(--surface-link-fg, inherit));
|
|
499
1085
|
--_bg-mix-color: var(--sidebar-bg-mix-color, var(--surface-bg-mix-color, var(--bg-mix-color, transparent)));
|
|
@@ -580,27 +1166,18 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
|
|
|
580
1166
|
color: var(--_color);
|
|
581
1167
|
}
|
|
582
1168
|
aside > button {
|
|
583
|
-
--_sidebar-expander-width: max(
|
|
584
|
-
var(--sidebar-icon-width, 0.65rem),
|
|
585
|
-
var(--icon-size, 32px)
|
|
586
|
-
);
|
|
587
|
-
--_sidebar-expander-height: max(
|
|
588
|
-
var(--sidebar-icon-height, 1rem),
|
|
589
|
-
var(--icon-size, 32px)
|
|
590
|
-
);
|
|
591
1169
|
transition: left var(--sidebar-transition);
|
|
592
1170
|
transform: translateX(0);
|
|
593
|
-
z-index: 3;
|
|
1171
|
+
z-index: calc(var(--sidebar-overlay-z-index, 3) + 1);
|
|
594
1172
|
position: absolute;
|
|
595
1173
|
opacity: 1;
|
|
596
1174
|
pointer-events: all;
|
|
597
1175
|
display: block;
|
|
598
|
-
position: absolute;
|
|
599
1176
|
top: var(--padding);
|
|
600
1177
|
left: 0;
|
|
601
|
-
border-radius: var(--
|
|
602
|
-
border-top-left-radius:
|
|
603
|
-
border-bottom-left-radius:
|
|
1178
|
+
border-radius: var(--_sidebar-sheet-button-radius);
|
|
1179
|
+
border-top-left-radius: var(--_sidebar-sheet-expand-edge-radius);
|
|
1180
|
+
border-bottom-left-radius: var(--_sidebar-sheet-expand-edge-radius);
|
|
604
1181
|
border: var(--circle-button-border, var(--mini-button-border));
|
|
605
1182
|
width: var(--_sidebar-expander-width);
|
|
606
1183
|
height: var(--_sidebar-expander-height);
|
|
@@ -721,30 +1298,125 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
|
|
|
721
1298
|
filter: var(--sidebar-mobile-icon-filter, none);
|
|
722
1299
|
}
|
|
723
1300
|
aside > button.close {
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
1301
|
+
/* Flush inside the panel's inner edge. Adding --padding here pushed the
|
|
1302
|
+
button a padding's worth further in, which left it straddling the
|
|
1303
|
+
edge -- part of it hanging outside the panel -- while the content
|
|
1304
|
+
inset below still reserved the full button width, so the gap between
|
|
1305
|
+
the button and the first nav item came out wider than intended.
|
|
1306
|
+
Either the button pops out of the panel or it sits inside it; this
|
|
1307
|
+
sits inside it, and the inset is then exactly button + one gutter. */
|
|
1308
|
+
left: calc(var(--sidebar-width) - var(--_sidebar-expander-width));
|
|
1309
|
+
border-radius: var(--_sidebar-sheet-button-radius);
|
|
1310
|
+
border-top-right-radius: var(--_sidebar-sheet-collapse-edge-radius);
|
|
1311
|
+
border-bottom-right-radius: var(--_sidebar-sheet-collapse-edge-radius);
|
|
1312
|
+
}
|
|
733
1313
|
aside {
|
|
734
|
-
|
|
735
|
-
|
|
1314
|
+
/* Everything above anchors the button to the left, which is right for a
|
|
1315
|
+
left-hand sidebar and nonsense for a right-hand one: the sheet slides
|
|
1316
|
+
out of the right edge, so the button has to hug that edge and then step
|
|
1317
|
+
*inwards* -- leftwards -- by the panel's width when it opens. Anchored
|
|
1318
|
+
from the left it stepped the other way, off the far side of the page
|
|
1319
|
+
entirely, where it could not even be clicked. The flattened corners
|
|
1320
|
+
mirror too, so the button still reads as attached to its own edge. */
|
|
736
1321
|
}
|
|
737
|
-
aside
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
);
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
1322
|
+
aside.right > button {
|
|
1323
|
+
left: auto;
|
|
1324
|
+
right: 0;
|
|
1325
|
+
border-radius: var(--_sidebar-sheet-button-radius);
|
|
1326
|
+
border-top-right-radius: var(--_sidebar-sheet-expand-edge-radius);
|
|
1327
|
+
border-bottom-right-radius: var(--_sidebar-sheet-expand-edge-radius);
|
|
1328
|
+
}
|
|
1329
|
+
aside.right > button.close {
|
|
1330
|
+
left: auto;
|
|
1331
|
+
right: calc(var(--sidebar-width) - var(--_sidebar-expander-width));
|
|
1332
|
+
border-radius: var(--_sidebar-sheet-button-radius);
|
|
1333
|
+
border-top-left-radius: var(--_sidebar-sheet-collapse-edge-radius);
|
|
1334
|
+
border-bottom-left-radius: var(--_sidebar-sheet-collapse-edge-radius);
|
|
1335
|
+
}
|
|
1336
|
+
aside {
|
|
1337
|
+
/* `sticky`: the sheet follows the reader down the page instead of
|
|
1338
|
+
stretching to the row.
|
|
1339
|
+
|
|
1340
|
+
Sticky needs the sheet back *in flow* -- an absolutely positioned box
|
|
1341
|
+
cannot stick -- but the aside is only a button's width wide and the
|
|
1342
|
+
row's full height, and that height is exactly the range the sheet
|
|
1343
|
+
should be free to travel. So the aside becomes a one-cell grid as tall
|
|
1344
|
+
as its row, and the sheet and its button share that cell, each sticky
|
|
1345
|
+
within it.
|
|
1346
|
+
|
|
1347
|
+
Horizontal placement moves from `left`/`right` onto margins, because on
|
|
1348
|
+
a sticky box `left` and `right` stop being offsets and become sticky
|
|
1349
|
+
insets -- they would pin the button against a sideways-scrolling
|
|
1350
|
+
ancestor rather than place it. The margins reproduce the absolute
|
|
1351
|
+
geometry exactly: the aside is `--gap` plus one button wide, and a
|
|
1352
|
+
sheet or button wider than that spills out of its cell on the side
|
|
1353
|
+
that faces the content. */
|
|
1354
|
+
}
|
|
1355
|
+
aside.sticky {
|
|
1356
|
+
--_sidebar-sheet-top: var(--sidebar-sheet-top, var(--padding));
|
|
1357
|
+
--_sidebar-aside-width: calc(var(--gap) + var(--_sidebar-expander-width));
|
|
1358
|
+
display: grid;
|
|
1359
|
+
grid-template-columns: 100%;
|
|
1360
|
+
align-content: stretch;
|
|
1361
|
+
/* A shut sheet is still in flow now, and would otherwise hold the row
|
|
1362
|
+
open to its own height. The aside's width is set explicitly, so
|
|
1363
|
+
size containment only ever zeroes out the height it would have
|
|
1364
|
+
contributed; the row still stretches it to full height. */
|
|
1365
|
+
contain: size;
|
|
1366
|
+
}
|
|
1367
|
+
aside.sticky > .content, aside.sticky > button.expander, aside.sticky > button.close {
|
|
1368
|
+
grid-area: 1/1;
|
|
1369
|
+
position: sticky;
|
|
1370
|
+
align-self: start;
|
|
1371
|
+
justify-self: start;
|
|
1372
|
+
left: auto;
|
|
1373
|
+
right: auto;
|
|
1374
|
+
}
|
|
1375
|
+
aside.sticky > .content {
|
|
1376
|
+
top: var(--_sidebar-sheet-top);
|
|
1377
|
+
margin-left: 0;
|
|
1378
|
+
height: auto;
|
|
1379
|
+
/* The reserve is a budget for everything that is not the sheet, rather
|
|
1380
|
+
than just the sticky offset: before the sheet sticks -- page scrolled
|
|
1381
|
+
to the top -- its top edge is its row's, which may sit well below the
|
|
1382
|
+
viewport top, and a sheet sized only from the offset would overflow
|
|
1383
|
+
the viewport by exactly that much. Twice the top gives it an even
|
|
1384
|
+
margin at both ends once it has stuck. */
|
|
1385
|
+
max-height: var(--sidebar-sheet-max-height, calc(100dvh - var(--sidebar-sheet-reserve, calc(2 * var(--_sidebar-sheet-top)))));
|
|
1386
|
+
overflow-y: auto;
|
|
1387
|
+
/* A column, so a child with `min-height: 0` and its own overflow can
|
|
1388
|
+
keep a header or footer in view and scroll only its middle. Children
|
|
1389
|
+
that do not opt in keep their size, and the sheet scrolls as a
|
|
1390
|
+
whole. */
|
|
1391
|
+
display: flex;
|
|
1392
|
+
flex-direction: column;
|
|
1393
|
+
}
|
|
1394
|
+
aside.sticky.expandedHamburger > .content {
|
|
1395
|
+
height: auto;
|
|
1396
|
+
}
|
|
1397
|
+
aside.sticky.right > .content {
|
|
1398
|
+
margin-left: calc(var(--_sidebar-aside-width) - var(--sidebar-width));
|
|
746
1399
|
}
|
|
1400
|
+
aside.sticky > button.expander, aside.sticky > button.close {
|
|
1401
|
+
top: calc(var(--_sidebar-sheet-top) + var(--padding));
|
|
1402
|
+
margin-top: var(--padding);
|
|
1403
|
+
margin-left: 0;
|
|
1404
|
+
transition: margin-left var(--sidebar-transition);
|
|
1405
|
+
}
|
|
1406
|
+
aside.sticky > button.close {
|
|
1407
|
+
margin-left: calc(var(--sidebar-width) - var(--_sidebar-expander-width));
|
|
1408
|
+
}
|
|
1409
|
+
aside.sticky.right > button.expander {
|
|
1410
|
+
margin-left: var(--gap);
|
|
1411
|
+
}
|
|
1412
|
+
aside.sticky.right > button.close {
|
|
1413
|
+
margin-left: calc(var(--_sidebar-aside-width) - var(--sidebar-width));
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
aside.button-sheet-toggle {
|
|
1417
|
+
--_sidebar-sheet-edge-override: var(--_sidebar-sheet-button-radius);
|
|
747
1418
|
}
|
|
1419
|
+
|
|
748
1420
|
button::after {
|
|
749
1421
|
color: var(--sidebar-icon-fg, var(--fg, currentColor));
|
|
750
1422
|
background-position: center;
|
|
@@ -758,14 +1430,34 @@ button::after {
|
|
|
758
1430
|
line-height: 1;
|
|
759
1431
|
}
|
|
760
1432
|
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
1433
|
+
/* The rail and the sheet are different affordances and want different
|
|
1434
|
+
glyphs. The rail slides a panel out from the edge it sits on, so a
|
|
1435
|
+
chevron pointing that way says what will happen. The sheet is summoned
|
|
1436
|
+
from nowhere by a free-floating button, where the menu glyph is what
|
|
1437
|
+
people already recognize -- and it closes with an x, not a back-chevron,
|
|
1438
|
+
because there is no edge to slide back into.
|
|
1439
|
+
|
|
1440
|
+
Both still fall through --sidebar-expand / --sidebar-collapse, so setting
|
|
1441
|
+
those restyles every button at once and themes that swap in SVGs keep
|
|
1442
|
+
working unchanged. */
|
|
1443
|
+
.edge-bar button.expander::after {
|
|
1444
|
+
content: var(--grab-bar-expand, var(--sidebar-expand, "›"));
|
|
1445
|
+
background-image: var(--grab-bar-expand-image, var(--sidebar-expand-image, none));
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
.edge-bar button.close::after {
|
|
1449
|
+
content: var(--grab-bar-collapse, var(--sidebar-collapse, "‹"));
|
|
1450
|
+
background-image: var(--grab-bar-collapse-image, var(--sidebar-collapse-image, none));
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
aside > button.expander::after {
|
|
1454
|
+
content: var(--sidebar-sheet-expand, var(--sidebar-expand, "☰"));
|
|
1455
|
+
background-image: var(--sidebar-sheet-expand-image, var(--sidebar-expand-image, none));
|
|
764
1456
|
}
|
|
765
1457
|
|
|
766
|
-
button.close::after {
|
|
767
|
-
content: var(--sidebar-collapse, "
|
|
768
|
-
background-image: var(--sidebar-collapse-image, none);
|
|
1458
|
+
aside > button.close::after {
|
|
1459
|
+
content: var(--sidebar-sheet-collapse, var(--sidebar-collapse, "✕"));
|
|
1460
|
+
background-image: var(--sidebar-sheet-collapse-image, var(--sidebar-collapse-image, none));
|
|
769
1461
|
}
|
|
770
1462
|
|
|
771
1463
|
.right button::after {
|