contain-css-svelte 1.3.0 → 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.
Files changed (46) hide show
  1. package/dist/Card.svelte +204 -288
  2. package/dist/controls/Button.svelte +27 -1
  3. package/dist/controls/ButtonLink.svelte +27 -1
  4. package/dist/controls/Checkbox.svelte +26 -0
  5. package/dist/controls/CircleButton.svelte +27 -1
  6. package/dist/controls/Input.svelte +27 -1
  7. package/dist/controls/RadioButton.svelte +26 -0
  8. package/dist/controls/Select.svelte +26 -0
  9. package/dist/controls/Slider.svelte +27 -1
  10. package/dist/controls/TabItem.svelte +27 -1
  11. package/dist/controls/Toggle.svelte +27 -1
  12. package/dist/dropdowns/DropdownMenu.svelte +27 -1
  13. package/dist/layout/Accordion.svelte +27 -1
  14. package/dist/layout/Bar.svelte +37 -1
  15. package/dist/layout/ColumnContainer.svelte +27 -1
  16. package/dist/layout/Columns.svelte +27 -1
  17. package/dist/layout/Container.svelte +205 -292
  18. package/dist/layout/DataList.svelte +27 -1
  19. package/dist/layout/DataListItem.svelte +205 -292
  20. package/dist/layout/Fieldset.svelte +27 -1
  21. package/dist/layout/Form.svelte +205 -292
  22. package/dist/layout/FormItem.svelte +27 -1
  23. package/dist/layout/GridLayout.svelte +26 -0
  24. package/dist/layout/Hero.svelte +205 -292
  25. package/dist/layout/Inline.svelte +33 -1
  26. package/dist/layout/MenuList.svelte +27 -1
  27. package/dist/layout/Page.svelte +27 -1
  28. package/dist/layout/RowContainer.svelte +27 -1
  29. package/dist/layout/Sidebar.svelte +266 -33
  30. package/dist/layout/Sidebar.svelte.d.ts +34 -0
  31. package/dist/layout/SidebarContainer.svelte +27 -1
  32. package/dist/layout/SplitPane.svelte +27 -1
  33. package/dist/layout/Stack.svelte +33 -1
  34. package/dist/layout/TabBar.svelte +27 -1
  35. package/dist/layout/Table.svelte +238 -294
  36. package/dist/layout/Tile.svelte +204 -291
  37. package/dist/misc/CodeInner.svelte +27 -1
  38. package/dist/misc/Progress.svelte +205 -292
  39. package/dist/misc/Tag.svelte +27 -1
  40. package/dist/overlays/Dialog.svelte +205 -292
  41. package/dist/overlays/Tooltip.svelte +255 -349
  42. package/dist/sass/_typography.scss +203 -191
  43. package/dist/typography/TextLayout.svelte +482 -292
  44. package/dist/vars/defaults.css +258 -0
  45. package/dist/vars/typography-context.css +271 -0
  46. package/package.json +1 -1
@@ -32,7 +32,33 @@ const el = $derived(elementProps({ justify, align, gap, wrap, ...restProps }, "i
32
32
  {@render children?.()}
33
33
  </div>
34
34
 
35
- <style>/* Logical, not top/bottom/left/right: margin-block/margin-inline already take
35
+ <style>/* Per-level heading (h1-h6) box/typography properties, resolved to PRIVATE,
36
+ inheriting custom properties (--_h1-color, --_h2-font-size, etc) rather than
37
+ assigned directly to a matched h1-h6 selector. Nothing in this file matches
38
+ an actual heading element anymore -- see typography-context.css (vars/), which
39
+ is the single, global, :where()-scoped rule that actually reads these vars
40
+ and applies them to real elements. That split is what lets a component
41
+ nested arbitrarily deep (through <article>, <Tile>, whatever) still get the
42
+ nearest ancestor's heading styling: these are ordinary inherited custom
43
+ properties, so DOM proximity decides which component's values apply, not
44
+ selector specificity or stylesheet order. See AGENTS-INTERNAL.md. */
45
+ /* All six heading levels' context vars in one call -- what every
46
+ "typography container" component (Container, Card, Tile, TextLayout, ...)
47
+ includes on itself once, alongside the shared --_heading-* vars below. */
48
+ /* Shared prose-measure group: p, blockquote, dl, ul, ol. li/dt/dd don't get
49
+ their own vars -- they're never wider than their ul/ol/dl parent's own
50
+ max-width box, so they're already bounded for free by ordinary block
51
+ layout, not by anything selector-based reaching them individually. */
52
+ /* p specifically also gets the fuller typography-props-bare treatment
53
+ (paragraph-prefixed), which -- same as in the original selector-based
54
+ version -- is declared/consumed AFTER the shared prose vars above, so it
55
+ wins for whichever properties both groups set. */
56
+ /* The first paragraph in a typography context (p:first-of-type, or a p right
57
+ after a heading) gets its own, slightly different treatment -- larger lede
58
+ text, no top margin. Structural matching (:first-of-type, `h1 + p`) still
59
+ has to happen where the real element is, in the global consumer rule; only
60
+ the VALUES move to context vars here. */
61
+ /* Logical, not top/bottom/left/right: margin-block/margin-inline already take
36
62
  one value (both sides equal) or two (start end), so there is no separate
37
63
  "which side" prop to remember and no 4-value shorthand order to get wrong.
38
64
  Defaults to 0 on purpose -- unlike padding-props above, a margin that
@@ -72,6 +98,12 @@ const el = $derived(elementProps({ justify, align, gap, wrap, ...restProps }, "i
72
98
  border-bottom: var(--inline-border-bottom, var(--border-bottom, var(--inline-border, var(--border, none))));
73
99
  border-left: var(--inline-border-left, var(--border-left, var(--inline-border, var(--border, none))));
74
100
  border-radius: var(--inline-border-radius, var(--border-radius, none));
101
+ /* A layout helper, not a surface: padding comes only from
102
+ --inline-padding (or the `padding` prop), never from the theme's
103
+ --padding. box-props falls back to --padding, which is inherited, so
104
+ without this every Inline picked up the page's padding -- or
105
+ whatever padding an enclosing Card set. */
106
+ --_padding: var(--inline-padding, 0);
75
107
  --_margin-block: var(--inline-margin-block, var(--margin-block, 0));
76
108
  --_margin-inline: var(--inline-margin-inline, var(--margin-inline, 0));
77
109
  margin-block: var(--_margin-block);
@@ -26,7 +26,33 @@ const el = $derived(elementProps(restProps, "menu", [
26
26
  {@render children?.()}
27
27
  </ul>
28
28
 
29
- <style>/* Logical, not top/bottom/left/right: margin-block/margin-inline already take
29
+ <style>/* Per-level heading (h1-h6) box/typography properties, resolved to PRIVATE,
30
+ inheriting custom properties (--_h1-color, --_h2-font-size, etc) rather than
31
+ assigned directly to a matched h1-h6 selector. Nothing in this file matches
32
+ an actual heading element anymore -- see typography-context.css (vars/), which
33
+ is the single, global, :where()-scoped rule that actually reads these vars
34
+ and applies them to real elements. That split is what lets a component
35
+ nested arbitrarily deep (through <article>, <Tile>, whatever) still get the
36
+ nearest ancestor's heading styling: these are ordinary inherited custom
37
+ properties, so DOM proximity decides which component's values apply, not
38
+ selector specificity or stylesheet order. See AGENTS-INTERNAL.md. */
39
+ /* All six heading levels' context vars in one call -- what every
40
+ "typography container" component (Container, Card, Tile, TextLayout, ...)
41
+ includes on itself once, alongside the shared --_heading-* vars below. */
42
+ /* Shared prose-measure group: p, blockquote, dl, ul, ol. li/dt/dd don't get
43
+ their own vars -- they're never wider than their ul/ol/dl parent's own
44
+ max-width box, so they're already bounded for free by ordinary block
45
+ layout, not by anything selector-based reaching them individually. */
46
+ /* p specifically also gets the fuller typography-props-bare treatment
47
+ (paragraph-prefixed), which -- same as in the original selector-based
48
+ version -- is declared/consumed AFTER the shared prose vars above, so it
49
+ wins for whichever properties both groups set. */
50
+ /* The first paragraph in a typography context (p:first-of-type, or a p right
51
+ after a heading) gets its own, slightly different treatment -- larger lede
52
+ text, no top margin. Structural matching (:first-of-type, `h1 + p`) still
53
+ has to happen where the real element is, in the global consumer rule; only
54
+ the VALUES move to context vars here. */
55
+ /* Logical, not top/bottom/left/right: margin-block/margin-inline already take
30
56
  one value (both sides equal) or two (start end), so there is no separate
31
57
  "which side" prop to remember and no 4-value shorthand order to get wrong.
32
58
  Defaults to 0 on purpose -- unlike padding-props above, a margin that
@@ -89,7 +89,33 @@ onMount(() => {
89
89
  </footer>
90
90
  </section>
91
91
 
92
- <style>/* Logical, not top/bottom/left/right: margin-block/margin-inline already take
92
+ <style>/* Per-level heading (h1-h6) box/typography properties, resolved to PRIVATE,
93
+ inheriting custom properties (--_h1-color, --_h2-font-size, etc) rather than
94
+ assigned directly to a matched h1-h6 selector. Nothing in this file matches
95
+ an actual heading element anymore -- see typography-context.css (vars/), which
96
+ is the single, global, :where()-scoped rule that actually reads these vars
97
+ and applies them to real elements. That split is what lets a component
98
+ nested arbitrarily deep (through <article>, <Tile>, whatever) still get the
99
+ nearest ancestor's heading styling: these are ordinary inherited custom
100
+ properties, so DOM proximity decides which component's values apply, not
101
+ selector specificity or stylesheet order. See AGENTS-INTERNAL.md. */
102
+ /* All six heading levels' context vars in one call -- what every
103
+ "typography container" component (Container, Card, Tile, TextLayout, ...)
104
+ includes on itself once, alongside the shared --_heading-* vars below. */
105
+ /* Shared prose-measure group: p, blockquote, dl, ul, ol. li/dt/dd don't get
106
+ their own vars -- they're never wider than their ul/ol/dl parent's own
107
+ max-width box, so they're already bounded for free by ordinary block
108
+ layout, not by anything selector-based reaching them individually. */
109
+ /* p specifically also gets the fuller typography-props-bare treatment
110
+ (paragraph-prefixed), which -- same as in the original selector-based
111
+ version -- is declared/consumed AFTER the shared prose vars above, so it
112
+ wins for whichever properties both groups set. */
113
+ /* The first paragraph in a typography context (p:first-of-type, or a p right
114
+ after a heading) gets its own, slightly different treatment -- larger lede
115
+ text, no top margin. Structural matching (:first-of-type, `h1 + p`) still
116
+ has to happen where the real element is, in the global consumer rule; only
117
+ the VALUES move to context vars here. */
118
+ /* Logical, not top/bottom/left/right: margin-block/margin-inline already take
93
119
  one value (both sides equal) or two (start end), so there is no separate
94
120
  "which side" prop to remember and no 4-value shorthand order to get wrong.
95
121
  Defaults to 0 on purpose -- unlike padding-props above, a margin that
@@ -21,7 +21,33 @@ const el = $derived(elementProps(restProps, "row-container", ROW_CONTAINER_VARS)
21
21
  {@render children?.()}
22
22
  </section>
23
23
 
24
- <style>/* Logical, not top/bottom/left/right: margin-block/margin-inline already take
24
+ <style>/* Per-level heading (h1-h6) box/typography properties, resolved to PRIVATE,
25
+ inheriting custom properties (--_h1-color, --_h2-font-size, etc) rather than
26
+ assigned directly to a matched h1-h6 selector. Nothing in this file matches
27
+ an actual heading element anymore -- see typography-context.css (vars/), which
28
+ is the single, global, :where()-scoped rule that actually reads these vars
29
+ and applies them to real elements. That split is what lets a component
30
+ nested arbitrarily deep (through <article>, <Tile>, whatever) still get the
31
+ nearest ancestor's heading styling: these are ordinary inherited custom
32
+ properties, so DOM proximity decides which component's values apply, not
33
+ selector specificity or stylesheet order. See AGENTS-INTERNAL.md. */
34
+ /* All six heading levels' context vars in one call -- what every
35
+ "typography container" component (Container, Card, Tile, TextLayout, ...)
36
+ includes on itself once, alongside the shared --_heading-* vars below. */
37
+ /* Shared prose-measure group: p, blockquote, dl, ul, ol. li/dt/dd don't get
38
+ their own vars -- they're never wider than their ul/ol/dl parent's own
39
+ max-width box, so they're already bounded for free by ordinary block
40
+ layout, not by anything selector-based reaching them individually. */
41
+ /* p specifically also gets the fuller typography-props-bare treatment
42
+ (paragraph-prefixed), which -- same as in the original selector-based
43
+ version -- is declared/consumed AFTER the shared prose vars above, so it
44
+ wins for whichever properties both groups set. */
45
+ /* The first paragraph in a typography context (p:first-of-type, or a p right
46
+ after a heading) gets its own, slightly different treatment -- larger lede
47
+ text, no top margin. Structural matching (:first-of-type, `h1 + p`) still
48
+ has to happen where the real element is, in the global consumer rule; only
49
+ the VALUES move to context vars here. */
50
+ /* Logical, not top/bottom/left/right: margin-block/margin-inline already take
25
51
  one value (both sides equal) or two (start end), so there is no separate
26
52
  "which side" prop to remember and no 4-value shorthand order to get wrong.
27
53
  Defaults to 0 on purpose -- unlike padding-props above, a margin that
@@ -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, overlay = false, open = $bindable(undefined), 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. */
@@ -28,6 +28,8 @@ const expandedHamburger = $derived(open ?? false);
28
28
  class:right
29
29
  class:left
30
30
  class:overlay
31
+ class:sticky
32
+ class:button-sheet-toggle={sheetButton === "button"}
31
33
  class:expandedHamburger
32
34
  class:expandedBar
33
35
  {...el}
@@ -57,6 +59,32 @@ const expandedHamburger = $derived(open ?? false);
57
59
  </aside>
58
60
 
59
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. */
60
88
  /* Logical, not top/bottom/left/right: margin-block/margin-inline already take
61
89
  one value (both sides equal) or two (start end), so there is no separate
62
90
  "which side" prop to remember and no 4-value shorthand order to get wrong.
@@ -85,6 +113,9 @@ const expandedHamburger = $derived(open ?? false);
85
113
  */
86
114
  /* Convenience groupings */
87
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;
88
119
  --link-bg: var(--sidebar-link-bg, var(--surface-link-bg, inherit));
89
120
  --link-fg: var(--sidebar-link-fg, var(--surface-link-fg, inherit));
90
121
  --_bg-mix-color: var(--sidebar-bg-mix-color, var(--surface-bg-mix-color, var(--bg-mix-color, transparent)));
@@ -383,6 +414,26 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
383
414
  var(--sidebar-icon-height, 1rem),
384
415
  var(--icon-size, 32px)
385
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
+ );
386
437
  position: relative;
387
438
  overflow: visible;
388
439
  background: transparent;
@@ -408,7 +459,9 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
408
459
  top: 0;
409
460
  left: 0;
410
461
  width: var(--sidebar-width);
411
- z-index: 2;
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);
412
465
  transform: translateX(-100%);
413
466
  opacity: 0;
414
467
  background: transparent;
@@ -536,16 +589,16 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
536
589
  aside.overlay > button {
537
590
  transition: left var(--sidebar-transition);
538
591
  transform: translateX(0);
539
- z-index: 3;
592
+ z-index: calc(var(--sidebar-overlay-z-index, 3) + 1);
540
593
  position: absolute;
541
594
  opacity: 1;
542
595
  pointer-events: all;
543
596
  display: block;
544
597
  top: var(--padding);
545
598
  left: 0;
546
- border-radius: var(--circle-button-radius, var(--mini-button-radius, var(--button-radius, var(--radius, 50%))));
547
- border-top-left-radius: 0;
548
- border-bottom-left-radius: 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);
549
602
  border: var(--circle-button-border, var(--mini-button-border));
550
603
  width: var(--_sidebar-expander-width);
551
604
  height: var(--_sidebar-expander-height);
@@ -674,9 +727,9 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
674
727
  Either the button pops out of the panel or it sits inside it; this
675
728
  sits inside it, and the inset is then exactly button + one gutter. */
676
729
  left: calc(var(--sidebar-width) - var(--_sidebar-expander-width));
677
- border-radius: var(--circle-button-radius, var(--mini-button-radius, var(--button-radius, var(--radius, 50%))));
678
- border-top-right-radius: 0;
679
- border-bottom-right-radius: 0;
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);
680
733
  }
681
734
  aside.overlay {
682
735
  /* Everything above anchors the button to the left, which is right for a
@@ -690,18 +743,95 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
690
743
  aside.overlay.right > button {
691
744
  left: auto;
692
745
  right: 0;
693
- border-top-left-radius: var(--circle-button-radius, var(--mini-button-radius, var(--button-radius, var(--radius, 50%))));
694
- border-bottom-left-radius: var(--circle-button-radius, var(--mini-button-radius, var(--button-radius, var(--radius, 50%))));
695
- border-top-right-radius: 0;
696
- border-bottom-right-radius: 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);
697
749
  }
698
750
  aside.overlay.right > button.close {
699
751
  left: auto;
700
752
  right: calc(var(--sidebar-width) - var(--_sidebar-expander-width));
701
- border-top-right-radius: var(--circle-button-radius, var(--mini-button-radius, var(--button-radius, var(--radius, 50%))));
702
- border-bottom-right-radius: var(--circle-button-radius, var(--mini-button-radius, var(--button-radius, var(--radius, 50%))));
703
- border-top-left-radius: 0;
704
- border-bottom-left-radius: 0;
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));
705
835
  }
706
836
  .edge-bar {
707
837
  --link-bg: var(--grab-bar-link-bg, var(--sidebar-link-bg, var(--surface-link-bg, inherit)));
@@ -863,6 +993,26 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
863
993
  var(--sidebar-icon-height, 1rem),
864
994
  var(--icon-size, 32px)
865
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
+ );
866
1016
  position: relative;
867
1017
  overflow: visible;
868
1018
  background: transparent;
@@ -888,7 +1038,9 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
888
1038
  top: 0;
889
1039
  left: 0;
890
1040
  width: var(--sidebar-width);
891
- z-index: 2;
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);
892
1044
  transform: translateX(-100%);
893
1045
  opacity: 0;
894
1046
  background: transparent;
@@ -1016,16 +1168,16 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
1016
1168
  aside > button {
1017
1169
  transition: left var(--sidebar-transition);
1018
1170
  transform: translateX(0);
1019
- z-index: 3;
1171
+ z-index: calc(var(--sidebar-overlay-z-index, 3) + 1);
1020
1172
  position: absolute;
1021
1173
  opacity: 1;
1022
1174
  pointer-events: all;
1023
1175
  display: block;
1024
1176
  top: var(--padding);
1025
1177
  left: 0;
1026
- border-radius: var(--circle-button-radius, var(--mini-button-radius, var(--button-radius, var(--radius, 50%))));
1027
- border-top-left-radius: 0;
1028
- border-bottom-left-radius: 0;
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);
1029
1181
  border: var(--circle-button-border, var(--mini-button-border));
1030
1182
  width: var(--_sidebar-expander-width);
1031
1183
  height: var(--_sidebar-expander-height);
@@ -1154,9 +1306,9 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
1154
1306
  Either the button pops out of the panel or it sits inside it; this
1155
1307
  sits inside it, and the inset is then exactly button + one gutter. */
1156
1308
  left: calc(var(--sidebar-width) - var(--_sidebar-expander-width));
1157
- border-radius: var(--circle-button-radius, var(--mini-button-radius, var(--button-radius, var(--radius, 50%))));
1158
- border-top-right-radius: 0;
1159
- border-bottom-right-radius: 0;
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);
1160
1312
  }
1161
1313
  aside {
1162
1314
  /* Everything above anchors the button to the left, which is right for a
@@ -1170,20 +1322,101 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
1170
1322
  aside.right > button {
1171
1323
  left: auto;
1172
1324
  right: 0;
1173
- border-top-left-radius: var(--circle-button-radius, var(--mini-button-radius, var(--button-radius, var(--radius, 50%))));
1174
- border-bottom-left-radius: var(--circle-button-radius, var(--mini-button-radius, var(--button-radius, var(--radius, 50%))));
1175
- border-top-right-radius: 0;
1176
- border-bottom-right-radius: 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);
1177
1328
  }
1178
1329
  aside.right > button.close {
1179
1330
  left: auto;
1180
1331
  right: calc(var(--sidebar-width) - var(--_sidebar-expander-width));
1181
- border-top-right-radius: var(--circle-button-radius, var(--mini-button-radius, var(--button-radius, var(--radius, 50%))));
1182
- border-bottom-right-radius: var(--circle-button-radius, var(--mini-button-radius, var(--button-radius, var(--radius, 50%))));
1183
- border-top-left-radius: 0;
1184
- border-bottom-left-radius: 0;
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));
1185
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);
1186
1418
  }
1419
+
1187
1420
  button::after {
1188
1421
  color: var(--sidebar-icon-fg, var(--fg, currentColor));
1189
1422
  background-position: center;
@@ -18,6 +18,40 @@ declare const Sidebar: import("svelte").Component<ContainProps<HTMLAttributes<HT
18
18
  * top of the content is worse.
19
19
  */
20
20
  overlay?: boolean;
21
+ /**
22
+ * Pin the sheet to the viewport while the page scrolls past it, instead
23
+ * of stretching it to the height of whatever it sits beside.
24
+ *
25
+ * Without it, an open sheet is as tall as its row -- right beside a
26
+ * short card, wrong beside a fifty-row table, where the top of the
27
+ * panel scrolls away and anything at its foot ends up off the bottom of
28
+ * the page. With it, the sheet (and its button) stick at
29
+ * `--sidebar-sheet-top` and stop growing at `--sidebar-sheet-max-height`,
30
+ * scrolling their own content past that.
31
+ *
32
+ * Applies wherever the sheet does: any `overlay` sidebar, and any
33
+ * sidebar in the compact layout. The wide rail is unaffected. Like any
34
+ * `position: sticky`, it needs every ancestor up to the scrolling one to
35
+ * leave `overflow` alone.
36
+ */
37
+ sticky?: boolean;
38
+ /**
39
+ * The shape of the sheet's toggle button (the sheet is what any
40
+ * `overlay` sidebar, and any sidebar in the compact layout, uses).
41
+ *
42
+ * - `"tab"` (default): the corners on the side it attaches to are
43
+ * square, so it reads as fastened to that edge -- the page edge while
44
+ * shut, the panel's edge while open. Right against a page edge.
45
+ * - `"button"`: rounded all round like any other button, for a sheet
46
+ * that does not sit against an edge -- beside a table, inside page
47
+ * padding -- where a half-flat tab looks broken.
48
+ *
49
+ * Per-state control, if you need it, is by variable:
50
+ * `--sidebar-sheet-expand-edge-radius` (the shut button) and
51
+ * `--sidebar-sheet-collapse-edge-radius` (the open one), both falling
52
+ * back to `--sidebar-sheet-edge-radius`.
53
+ */
54
+ sheetButton?: "tab" | "button";
21
55
  /**
22
56
  * Whether the sidebar is showing its panel. Bindable, so a caller can
23
57
  * drive the sidebar from a button of their own:
@@ -18,7 +18,33 @@ const el = $derived(elementProps(restProps, "sidebar-container", [
18
18
  {@render children?.()}
19
19
  </div>
20
20
 
21
- <style>/* Logical, not top/bottom/left/right: margin-block/margin-inline already take
21
+ <style>/* Per-level heading (h1-h6) box/typography properties, resolved to PRIVATE,
22
+ inheriting custom properties (--_h1-color, --_h2-font-size, etc) rather than
23
+ assigned directly to a matched h1-h6 selector. Nothing in this file matches
24
+ an actual heading element anymore -- see typography-context.css (vars/), which
25
+ is the single, global, :where()-scoped rule that actually reads these vars
26
+ and applies them to real elements. That split is what lets a component
27
+ nested arbitrarily deep (through <article>, <Tile>, whatever) still get the
28
+ nearest ancestor's heading styling: these are ordinary inherited custom
29
+ properties, so DOM proximity decides which component's values apply, not
30
+ selector specificity or stylesheet order. See AGENTS-INTERNAL.md. */
31
+ /* All six heading levels' context vars in one call -- what every
32
+ "typography container" component (Container, Card, Tile, TextLayout, ...)
33
+ includes on itself once, alongside the shared --_heading-* vars below. */
34
+ /* Shared prose-measure group: p, blockquote, dl, ul, ol. li/dt/dd don't get
35
+ their own vars -- they're never wider than their ul/ol/dl parent's own
36
+ max-width box, so they're already bounded for free by ordinary block
37
+ layout, not by anything selector-based reaching them individually. */
38
+ /* p specifically also gets the fuller typography-props-bare treatment
39
+ (paragraph-prefixed), which -- same as in the original selector-based
40
+ version -- is declared/consumed AFTER the shared prose vars above, so it
41
+ wins for whichever properties both groups set. */
42
+ /* The first paragraph in a typography context (p:first-of-type, or a p right
43
+ after a heading) gets its own, slightly different treatment -- larger lede
44
+ text, no top margin. Structural matching (:first-of-type, `h1 + p`) still
45
+ has to happen where the real element is, in the global consumer rule; only
46
+ the VALUES move to context vars here. */
47
+ /* Logical, not top/bottom/left/right: margin-block/margin-inline already take
22
48
  one value (both sides equal) or two (start end), so there is no separate
23
49
  "which side" prop to remember and no 4-value shorthand order to get wrong.
24
50
  Defaults to 0 on purpose -- unlike padding-props above, a margin that