contain-css-svelte 1.4.2 → 1.5.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 +57 -3
- package/dist/controls/Button.svelte +32 -0
- package/dist/controls/ButtonLink.svelte +32 -0
- package/dist/controls/Checkbox.svelte +36 -0
- package/dist/controls/CircleButton.svelte +32 -0
- package/dist/controls/Input.svelte +32 -0
- package/dist/controls/RadioButton.svelte +36 -1
- package/dist/controls/Select.svelte +35 -0
- package/dist/controls/Slider.svelte +32 -0
- package/dist/controls/TabItem.svelte +36 -0
- package/dist/controls/Toggle.svelte +32 -0
- package/dist/dropdowns/DropdownMenu.svelte +53 -1
- package/dist/layout/Accordion.svelte +32 -0
- package/dist/layout/Bar.svelte +50 -0
- package/dist/layout/ColumnContainer.svelte +32 -0
- package/dist/layout/Columns.svelte +32 -0
- package/dist/layout/Container.svelte +48 -2
- package/dist/layout/DataList.svelte +33 -1
- package/dist/layout/DataList.svelte.d.ts +7 -0
- package/dist/layout/DataListItem.svelte +56 -2
- package/dist/layout/Fieldset.svelte +32 -0
- package/dist/layout/Form.svelte +32 -0
- package/dist/layout/FormItem.svelte +46 -0
- package/dist/layout/GridLayout.svelte +59 -0
- package/dist/layout/Hero.svelte +42 -0
- package/dist/layout/Inline.svelte +37 -1
- package/dist/layout/MenuList.svelte +32 -0
- package/dist/layout/Page.svelte +137 -10
- package/dist/layout/Page.svelte.d.ts +17 -0
- package/dist/layout/RowContainer.svelte +32 -0
- package/dist/layout/Sidebar.svelte +104 -56
- package/dist/layout/SidebarContainer.svelte +32 -0
- package/dist/layout/SplitPane.svelte +32 -0
- package/dist/layout/Stack.svelte +32 -0
- package/dist/layout/TabBar.svelte +127 -8
- package/dist/layout/TabBar.svelte.d.ts +16 -0
- package/dist/layout/Table.svelte +60 -9
- package/dist/layout/Tile.svelte +89 -6
- package/dist/misc/CodeInner.svelte +32 -0
- package/dist/misc/Progress.svelte +32 -0
- package/dist/misc/Tag.svelte +32 -0
- package/dist/overlays/Dialog.svelte +32 -0
- package/dist/overlays/Tooltip.svelte +32 -0
- package/dist/sass/_responsive.scss +43 -0
- package/dist/typography/TextLayout.svelte +47 -0
- package/dist/vars/colors.css +21 -5
- package/dist/vars/defaults.css +25 -6
- package/dist/vars/layout.css +4 -1
- package/dist/vars/responsive-typography.css +4 -1
- package/package.json +1 -1
|
@@ -130,6 +130,38 @@ const expandedHamburger = $derived(open ?? false);
|
|
|
130
130
|
we deliberately avoid everywhere else.
|
|
131
131
|
|
|
132
132
|
One definition, used by both, so they cannot drift again. */
|
|
133
|
+
/* The "small" size tier: a component whose container is this wide or less
|
|
134
|
+
is on a phone, or in a pane about that narrow, and sizes itself for it.
|
|
135
|
+
|
|
136
|
+
The convention, shared by every component that has a size worth changing
|
|
137
|
+
there (Card started it; Tile, GridLayout, Bar and Container follow):
|
|
138
|
+
|
|
139
|
+
--{component}-{property}-small
|
|
140
|
+
|
|
141
|
+
applies inside a small container. What wins there, in order:
|
|
142
|
+
1. the `-small` variable, if you set it;
|
|
143
|
+
2. otherwise the regular variable, if you set it -- a size you chose on
|
|
144
|
+
purpose is not overridden behind your back;
|
|
145
|
+
3. otherwise the component's own small default, which for most
|
|
146
|
+
properties is simply its regular default.
|
|
147
|
+
|
|
148
|
+
Container queries can't read a custom property, so the breakpoint itself
|
|
149
|
+
is fixed here rather than themeable. 600px matches Card and SplitPane,
|
|
150
|
+
which already switched there. (The Sidebar's rail-to-sheet switch,
|
|
151
|
+
above, is a separate, layout-chrome decision and keeps its own value.) */
|
|
152
|
+
/* Styles for the small tier: the nearest container is narrow, OR the
|
|
153
|
+
viewport is.
|
|
154
|
+
|
|
155
|
+
The container query is the real answer -- a Tile in a 400px pane on a
|
|
156
|
+
desktop is small. But a container query only matches inside an ancestor
|
|
157
|
+
that *is* a container, and a component dropped straight into a page (no
|
|
158
|
+
Page, no Container around it) has none, so on a phone it would never
|
|
159
|
+
find out. The viewport query covers that: a narrow viewport means every
|
|
160
|
+
container in it is narrow too, so the two never disagree in the
|
|
161
|
+
direction that matters.
|
|
162
|
+
|
|
163
|
+
An element never queries itself, so on a component that is its own
|
|
164
|
+
container (Tile, Bar) this asks about the space it was given. */
|
|
133
165
|
/* Warning: because we define a fallback
|
|
134
166
|
media query, the media query can override the container
|
|
135
167
|
if we stack a bunch of these in a row and aren't thoughtful about the order.
|
|
@@ -439,8 +471,14 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
|
|
|
439
471
|
var(--sidebar-icon-height, 1rem),
|
|
440
472
|
var(--icon-size, 32px)
|
|
441
473
|
);
|
|
474
|
+
/* Where the open sheet's close button sits: just past the panel's outer
|
|
475
|
+
edge, but never off the screen. */
|
|
476
|
+
--_sidebar-close-offset: min(
|
|
477
|
+
var(--sidebar-width),
|
|
478
|
+
calc(100vw - var(--_sidebar-expander-width))
|
|
479
|
+
);
|
|
442
480
|
/* The sheet button's corners. Its "edge" corners are the pair on the
|
|
443
|
-
side it attaches to -- the page edge while shut, the panel's
|
|
481
|
+
side it attaches to -- the page edge while shut, the panel's outer edge
|
|
444
482
|
while open -- and default to 0, which is what makes it read as a tab
|
|
445
483
|
fastened to that edge. `sheetButton="button"` rounds them like the
|
|
446
484
|
other two, for a sheet that does not sit against an edge. */
|
|
@@ -462,6 +500,12 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
|
|
|
462
500
|
position: relative;
|
|
463
501
|
overflow: visible;
|
|
464
502
|
background: transparent;
|
|
503
|
+
/* The aside is only a transparent strip holding the button and the
|
|
504
|
+
sheet, and a host may let content run underneath it (Page does, once
|
|
505
|
+
it has moved the button into its header). The strip itself must not
|
|
506
|
+
swallow clicks meant for that content; the button and an open sheet
|
|
507
|
+
turn pointer events back on for themselves. */
|
|
508
|
+
pointer-events: none;
|
|
465
509
|
width: calc(var(--gap) + max(var(--sidebar-icon-width, 0.65rem), var(--icon-size, 32px)));
|
|
466
510
|
flex: 0 0 auto;
|
|
467
511
|
/* Every child is out of flow, so the aside has no content to hold it
|
|
@@ -493,16 +537,13 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
|
|
|
493
537
|
pointer-events: none;
|
|
494
538
|
transition: transform var(--sidebar-transition) ease-in-out, opacity var(--sidebar-transition) ease-in-out;
|
|
495
539
|
padding: var(--padding);
|
|
496
|
-
/* The close button
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
--sidebar-sheet-content-inset to 0 and set your own padding if you
|
|
504
|
-
would rather have the width back. */
|
|
505
|
-
padding-inline-end: var(--sidebar-sheet-content-inset, calc(var(--padding) + var(--_sidebar-expander-width)));
|
|
540
|
+
/* The close button hangs off the panel's outer edge (below), so the
|
|
541
|
+
panel keeps its whole width. It used to sit inside that edge, which
|
|
542
|
+
meant reserving a button-wide strip down the full height of the
|
|
543
|
+
sheet -- a stripe beside every nav item, there only to clear one
|
|
544
|
+
button at the top. --sidebar-sheet-content-inset brings a reserve
|
|
545
|
+
back if you want one. */
|
|
546
|
+
padding-inline-end: var(--sidebar-sheet-content-inset, var(--padding));
|
|
506
547
|
}
|
|
507
548
|
aside.overlay.left > .content {
|
|
508
549
|
border-right: var(--border-width) var(--border-style) var(--border-color);
|
|
@@ -518,7 +559,7 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
|
|
|
518
559
|
transform: translateX(100%);
|
|
519
560
|
border-left: var(--border-width) var(--border-style) var(--border-color);
|
|
520
561
|
padding-inline-end: var(--padding);
|
|
521
|
-
padding-inline-start: var(--sidebar-sheet-content-inset,
|
|
562
|
+
padding-inline-start: var(--sidebar-sheet-content-inset, var(--padding));
|
|
522
563
|
}
|
|
523
564
|
aside.overlay.expandedHamburger > .content {
|
|
524
565
|
transform: translateX(0);
|
|
@@ -619,7 +660,9 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
|
|
|
619
660
|
opacity: 1;
|
|
620
661
|
pointer-events: all;
|
|
621
662
|
display: block;
|
|
622
|
-
|
|
663
|
+
/* A host can line the button up with something of its own -- Page
|
|
664
|
+
centres it in its header row. */
|
|
665
|
+
top: var(--sidebar-sheet-button-top, var(--padding));
|
|
623
666
|
left: 0;
|
|
624
667
|
border-radius: var(--_sidebar-sheet-button-radius);
|
|
625
668
|
border-top-left-radius: var(--_sidebar-sheet-expand-edge-radius);
|
|
@@ -744,17 +787,14 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
|
|
|
744
787
|
filter: var(--sidebar-mobile-icon-filter, none);
|
|
745
788
|
}
|
|
746
789
|
aside.overlay > button.close {
|
|
747
|
-
/*
|
|
748
|
-
|
|
749
|
-
edge
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
Either the button pops out of the panel or it sits inside it; this
|
|
753
|
-
sits inside it, and the inset is then exactly button + one gutter. */
|
|
754
|
-
left: calc(var(--sidebar-width) - var(--_sidebar-expander-width));
|
|
790
|
+
/* Just outside the open panel, fastened to its outer edge like a tab --
|
|
791
|
+
the same tab the open button was, now on the panel instead of the
|
|
792
|
+
page edge. Clamped so a sheet nearly as wide as the screen still
|
|
793
|
+
leaves its close button on screen. */
|
|
794
|
+
left: var(--_sidebar-close-offset);
|
|
755
795
|
border-radius: var(--_sidebar-sheet-button-radius);
|
|
756
|
-
border-top-
|
|
757
|
-
border-bottom-
|
|
796
|
+
border-top-left-radius: var(--_sidebar-sheet-collapse-edge-radius);
|
|
797
|
+
border-bottom-left-radius: var(--_sidebar-sheet-collapse-edge-radius);
|
|
758
798
|
}
|
|
759
799
|
aside.overlay {
|
|
760
800
|
/* Everything above anchors the button to the left, which is right for a
|
|
@@ -774,10 +814,10 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
|
|
|
774
814
|
}
|
|
775
815
|
aside.overlay.right > button.close {
|
|
776
816
|
left: auto;
|
|
777
|
-
right:
|
|
817
|
+
right: var(--_sidebar-close-offset);
|
|
778
818
|
border-radius: var(--_sidebar-sheet-button-radius);
|
|
779
|
-
border-top-
|
|
780
|
-
border-bottom-
|
|
819
|
+
border-top-right-radius: var(--_sidebar-sheet-collapse-edge-radius);
|
|
820
|
+
border-bottom-right-radius: var(--_sidebar-sheet-collapse-edge-radius);
|
|
781
821
|
}
|
|
782
822
|
aside.overlay {
|
|
783
823
|
/* `sticky`: the sheet follows the reader down the page instead of
|
|
@@ -850,13 +890,13 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
|
|
|
850
890
|
transition: margin-left var(--sidebar-transition);
|
|
851
891
|
}
|
|
852
892
|
aside.overlay.sticky > button.close {
|
|
853
|
-
margin-left:
|
|
893
|
+
margin-left: var(--_sidebar-close-offset);
|
|
854
894
|
}
|
|
855
895
|
aside.overlay.sticky.right > button.expander {
|
|
856
896
|
margin-left: var(--gap);
|
|
857
897
|
}
|
|
858
898
|
aside.overlay.sticky.right > button.close {
|
|
859
|
-
margin-left: calc(var(--_sidebar-aside-width) - var(--
|
|
899
|
+
margin-left: calc(var(--_sidebar-aside-width) - var(--_sidebar-close-offset) - var(--_sidebar-expander-width));
|
|
860
900
|
}
|
|
861
901
|
.edge-bar {
|
|
862
902
|
--link-bg: var(--grab-bar-link-bg, var(--sidebar-link-bg, var(--surface-link-bg, inherit)));
|
|
@@ -1018,8 +1058,14 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
|
|
|
1018
1058
|
var(--sidebar-icon-height, 1rem),
|
|
1019
1059
|
var(--icon-size, 32px)
|
|
1020
1060
|
);
|
|
1061
|
+
/* Where the open sheet's close button sits: just past the panel's outer
|
|
1062
|
+
edge, but never off the screen. */
|
|
1063
|
+
--_sidebar-close-offset: min(
|
|
1064
|
+
var(--sidebar-width),
|
|
1065
|
+
calc(100vw - var(--_sidebar-expander-width))
|
|
1066
|
+
);
|
|
1021
1067
|
/* The sheet button's corners. Its "edge" corners are the pair on the
|
|
1022
|
-
side it attaches to -- the page edge while shut, the panel's
|
|
1068
|
+
side it attaches to -- the page edge while shut, the panel's outer edge
|
|
1023
1069
|
while open -- and default to 0, which is what makes it read as a tab
|
|
1024
1070
|
fastened to that edge. `sheetButton="button"` rounds them like the
|
|
1025
1071
|
other two, for a sheet that does not sit against an edge. */
|
|
@@ -1041,6 +1087,12 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
|
|
|
1041
1087
|
position: relative;
|
|
1042
1088
|
overflow: visible;
|
|
1043
1089
|
background: transparent;
|
|
1090
|
+
/* The aside is only a transparent strip holding the button and the
|
|
1091
|
+
sheet, and a host may let content run underneath it (Page does, once
|
|
1092
|
+
it has moved the button into its header). The strip itself must not
|
|
1093
|
+
swallow clicks meant for that content; the button and an open sheet
|
|
1094
|
+
turn pointer events back on for themselves. */
|
|
1095
|
+
pointer-events: none;
|
|
1044
1096
|
width: calc(var(--gap) + max(var(--sidebar-icon-width, 0.65rem), var(--icon-size, 32px)));
|
|
1045
1097
|
flex: 0 0 auto;
|
|
1046
1098
|
/* Every child is out of flow, so the aside has no content to hold it
|
|
@@ -1072,16 +1124,13 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
|
|
|
1072
1124
|
pointer-events: none;
|
|
1073
1125
|
transition: transform var(--sidebar-transition) ease-in-out, opacity var(--sidebar-transition) ease-in-out;
|
|
1074
1126
|
padding: var(--padding);
|
|
1075
|
-
/* The close button
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
--sidebar-sheet-content-inset to 0 and set your own padding if you
|
|
1083
|
-
would rather have the width back. */
|
|
1084
|
-
padding-inline-end: var(--sidebar-sheet-content-inset, calc(var(--padding) + var(--_sidebar-expander-width)));
|
|
1127
|
+
/* The close button hangs off the panel's outer edge (below), so the
|
|
1128
|
+
panel keeps its whole width. It used to sit inside that edge, which
|
|
1129
|
+
meant reserving a button-wide strip down the full height of the
|
|
1130
|
+
sheet -- a stripe beside every nav item, there only to clear one
|
|
1131
|
+
button at the top. --sidebar-sheet-content-inset brings a reserve
|
|
1132
|
+
back if you want one. */
|
|
1133
|
+
padding-inline-end: var(--sidebar-sheet-content-inset, var(--padding));
|
|
1085
1134
|
}
|
|
1086
1135
|
aside.left > .content {
|
|
1087
1136
|
border-right: var(--border-width) var(--border-style) var(--border-color);
|
|
@@ -1097,7 +1146,7 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
|
|
|
1097
1146
|
transform: translateX(100%);
|
|
1098
1147
|
border-left: var(--border-width) var(--border-style) var(--border-color);
|
|
1099
1148
|
padding-inline-end: var(--padding);
|
|
1100
|
-
padding-inline-start: var(--sidebar-sheet-content-inset,
|
|
1149
|
+
padding-inline-start: var(--sidebar-sheet-content-inset, var(--padding));
|
|
1101
1150
|
}
|
|
1102
1151
|
aside.expandedHamburger > .content {
|
|
1103
1152
|
transform: translateX(0);
|
|
@@ -1198,7 +1247,9 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
|
|
|
1198
1247
|
opacity: 1;
|
|
1199
1248
|
pointer-events: all;
|
|
1200
1249
|
display: block;
|
|
1201
|
-
|
|
1250
|
+
/* A host can line the button up with something of its own -- Page
|
|
1251
|
+
centres it in its header row. */
|
|
1252
|
+
top: var(--sidebar-sheet-button-top, var(--padding));
|
|
1202
1253
|
left: 0;
|
|
1203
1254
|
border-radius: var(--_sidebar-sheet-button-radius);
|
|
1204
1255
|
border-top-left-radius: var(--_sidebar-sheet-expand-edge-radius);
|
|
@@ -1323,17 +1374,14 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
|
|
|
1323
1374
|
filter: var(--sidebar-mobile-icon-filter, none);
|
|
1324
1375
|
}
|
|
1325
1376
|
aside > button.close {
|
|
1326
|
-
/*
|
|
1327
|
-
|
|
1328
|
-
edge
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
Either the button pops out of the panel or it sits inside it; this
|
|
1332
|
-
sits inside it, and the inset is then exactly button + one gutter. */
|
|
1333
|
-
left: calc(var(--sidebar-width) - var(--_sidebar-expander-width));
|
|
1377
|
+
/* Just outside the open panel, fastened to its outer edge like a tab --
|
|
1378
|
+
the same tab the open button was, now on the panel instead of the
|
|
1379
|
+
page edge. Clamped so a sheet nearly as wide as the screen still
|
|
1380
|
+
leaves its close button on screen. */
|
|
1381
|
+
left: var(--_sidebar-close-offset);
|
|
1334
1382
|
border-radius: var(--_sidebar-sheet-button-radius);
|
|
1335
|
-
border-top-
|
|
1336
|
-
border-bottom-
|
|
1383
|
+
border-top-left-radius: var(--_sidebar-sheet-collapse-edge-radius);
|
|
1384
|
+
border-bottom-left-radius: var(--_sidebar-sheet-collapse-edge-radius);
|
|
1337
1385
|
}
|
|
1338
1386
|
aside {
|
|
1339
1387
|
/* Everything above anchors the button to the left, which is right for a
|
|
@@ -1353,10 +1401,10 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
|
|
|
1353
1401
|
}
|
|
1354
1402
|
aside.right > button.close {
|
|
1355
1403
|
left: auto;
|
|
1356
|
-
right:
|
|
1404
|
+
right: var(--_sidebar-close-offset);
|
|
1357
1405
|
border-radius: var(--_sidebar-sheet-button-radius);
|
|
1358
|
-
border-top-
|
|
1359
|
-
border-bottom-
|
|
1406
|
+
border-top-right-radius: var(--_sidebar-sheet-collapse-edge-radius);
|
|
1407
|
+
border-bottom-right-radius: var(--_sidebar-sheet-collapse-edge-radius);
|
|
1360
1408
|
}
|
|
1361
1409
|
aside {
|
|
1362
1410
|
/* `sticky`: the sheet follows the reader down the page instead of
|
|
@@ -1429,13 +1477,13 @@ aside .content:hover::-webkit-scrollbar-thumb:hover {
|
|
|
1429
1477
|
transition: margin-left var(--sidebar-transition);
|
|
1430
1478
|
}
|
|
1431
1479
|
aside.sticky > button.close {
|
|
1432
|
-
margin-left:
|
|
1480
|
+
margin-left: var(--_sidebar-close-offset);
|
|
1433
1481
|
}
|
|
1434
1482
|
aside.sticky.right > button.expander {
|
|
1435
1483
|
margin-left: var(--gap);
|
|
1436
1484
|
}
|
|
1437
1485
|
aside.sticky.right > button.close {
|
|
1438
|
-
margin-left: calc(var(--_sidebar-aside-width) - var(--
|
|
1486
|
+
margin-left: calc(var(--_sidebar-aside-width) - var(--_sidebar-close-offset) - var(--_sidebar-expander-width));
|
|
1439
1487
|
}
|
|
1440
1488
|
}
|
|
1441
1489
|
aside.button-sheet-toggle {
|
|
@@ -89,6 +89,38 @@ const el = $derived(elementProps(restProps, "sidebar-container", [
|
|
|
89
89
|
we deliberately avoid everywhere else.
|
|
90
90
|
|
|
91
91
|
One definition, used by both, so they cannot drift again. */
|
|
92
|
+
/* The "small" size tier: a component whose container is this wide or less
|
|
93
|
+
is on a phone, or in a pane about that narrow, and sizes itself for it.
|
|
94
|
+
|
|
95
|
+
The convention, shared by every component that has a size worth changing
|
|
96
|
+
there (Card started it; Tile, GridLayout, Bar and Container follow):
|
|
97
|
+
|
|
98
|
+
--{component}-{property}-small
|
|
99
|
+
|
|
100
|
+
applies inside a small container. What wins there, in order:
|
|
101
|
+
1. the `-small` variable, if you set it;
|
|
102
|
+
2. otherwise the regular variable, if you set it -- a size you chose on
|
|
103
|
+
purpose is not overridden behind your back;
|
|
104
|
+
3. otherwise the component's own small default, which for most
|
|
105
|
+
properties is simply its regular default.
|
|
106
|
+
|
|
107
|
+
Container queries can't read a custom property, so the breakpoint itself
|
|
108
|
+
is fixed here rather than themeable. 600px matches Card and SplitPane,
|
|
109
|
+
which already switched there. (The Sidebar's rail-to-sheet switch,
|
|
110
|
+
above, is a separate, layout-chrome decision and keeps its own value.) */
|
|
111
|
+
/* Styles for the small tier: the nearest container is narrow, OR the
|
|
112
|
+
viewport is.
|
|
113
|
+
|
|
114
|
+
The container query is the real answer -- a Tile in a 400px pane on a
|
|
115
|
+
desktop is small. But a container query only matches inside an ancestor
|
|
116
|
+
that *is* a container, and a component dropped straight into a page (no
|
|
117
|
+
Page, no Container around it) has none, so on a phone it would never
|
|
118
|
+
find out. The viewport query covers that: a narrow viewport means every
|
|
119
|
+
container in it is narrow too, so the two never disagree in the
|
|
120
|
+
direction that matters.
|
|
121
|
+
|
|
122
|
+
An element never queries itself, so on a component that is its own
|
|
123
|
+
container (Tile, Bar) this asks about the space it was given. */
|
|
92
124
|
/* Warning: because we define a fallback
|
|
93
125
|
media query, the media query can override the container
|
|
94
126
|
if we stack a bunch of these in a row and aren't thoughtful about the order.
|
|
@@ -187,6 +187,38 @@ onMount(() => {
|
|
|
187
187
|
we deliberately avoid everywhere else.
|
|
188
188
|
|
|
189
189
|
One definition, used by both, so they cannot drift again. */
|
|
190
|
+
/* The "small" size tier: a component whose container is this wide or less
|
|
191
|
+
is on a phone, or in a pane about that narrow, and sizes itself for it.
|
|
192
|
+
|
|
193
|
+
The convention, shared by every component that has a size worth changing
|
|
194
|
+
there (Card started it; Tile, GridLayout, Bar and Container follow):
|
|
195
|
+
|
|
196
|
+
--{component}-{property}-small
|
|
197
|
+
|
|
198
|
+
applies inside a small container. What wins there, in order:
|
|
199
|
+
1. the `-small` variable, if you set it;
|
|
200
|
+
2. otherwise the regular variable, if you set it -- a size you chose on
|
|
201
|
+
purpose is not overridden behind your back;
|
|
202
|
+
3. otherwise the component's own small default, which for most
|
|
203
|
+
properties is simply its regular default.
|
|
204
|
+
|
|
205
|
+
Container queries can't read a custom property, so the breakpoint itself
|
|
206
|
+
is fixed here rather than themeable. 600px matches Card and SplitPane,
|
|
207
|
+
which already switched there. (The Sidebar's rail-to-sheet switch,
|
|
208
|
+
above, is a separate, layout-chrome decision and keeps its own value.) */
|
|
209
|
+
/* Styles for the small tier: the nearest container is narrow, OR the
|
|
210
|
+
viewport is.
|
|
211
|
+
|
|
212
|
+
The container query is the real answer -- a Tile in a 400px pane on a
|
|
213
|
+
desktop is small. But a container query only matches inside an ancestor
|
|
214
|
+
that *is* a container, and a component dropped straight into a page (no
|
|
215
|
+
Page, no Container around it) has none, so on a phone it would never
|
|
216
|
+
find out. The viewport query covers that: a narrow viewport means every
|
|
217
|
+
container in it is narrow too, so the two never disagree in the
|
|
218
|
+
direction that matters.
|
|
219
|
+
|
|
220
|
+
An element never queries itself, so on a component that is its own
|
|
221
|
+
container (Tile, Bar) this asks about the space it was given. */
|
|
190
222
|
/* Warning: because we define a fallback
|
|
191
223
|
media query, the media query can override the container
|
|
192
224
|
if we stack a bunch of these in a row and aren't thoughtful about the order.
|
package/dist/layout/Stack.svelte
CHANGED
|
@@ -103,6 +103,38 @@ const el = $derived(elementProps({ justify, align: resolvedAlign, gap, ...restPr
|
|
|
103
103
|
we deliberately avoid everywhere else.
|
|
104
104
|
|
|
105
105
|
One definition, used by both, so they cannot drift again. */
|
|
106
|
+
/* The "small" size tier: a component whose container is this wide or less
|
|
107
|
+
is on a phone, or in a pane about that narrow, and sizes itself for it.
|
|
108
|
+
|
|
109
|
+
The convention, shared by every component that has a size worth changing
|
|
110
|
+
there (Card started it; Tile, GridLayout, Bar and Container follow):
|
|
111
|
+
|
|
112
|
+
--{component}-{property}-small
|
|
113
|
+
|
|
114
|
+
applies inside a small container. What wins there, in order:
|
|
115
|
+
1. the `-small` variable, if you set it;
|
|
116
|
+
2. otherwise the regular variable, if you set it -- a size you chose on
|
|
117
|
+
purpose is not overridden behind your back;
|
|
118
|
+
3. otherwise the component's own small default, which for most
|
|
119
|
+
properties is simply its regular default.
|
|
120
|
+
|
|
121
|
+
Container queries can't read a custom property, so the breakpoint itself
|
|
122
|
+
is fixed here rather than themeable. 600px matches Card and SplitPane,
|
|
123
|
+
which already switched there. (The Sidebar's rail-to-sheet switch,
|
|
124
|
+
above, is a separate, layout-chrome decision and keeps its own value.) */
|
|
125
|
+
/* Styles for the small tier: the nearest container is narrow, OR the
|
|
126
|
+
viewport is.
|
|
127
|
+
|
|
128
|
+
The container query is the real answer -- a Tile in a 400px pane on a
|
|
129
|
+
desktop is small. But a container query only matches inside an ancestor
|
|
130
|
+
that *is* a container, and a component dropped straight into a page (no
|
|
131
|
+
Page, no Container around it) has none, so on a phone it would never
|
|
132
|
+
find out. The viewport query covers that: a narrow viewport means every
|
|
133
|
+
container in it is narrow too, so the two never disagree in the
|
|
134
|
+
direction that matters.
|
|
135
|
+
|
|
136
|
+
An element never queries itself, so on a component that is its own
|
|
137
|
+
container (Tile, Bar) this asks about the space it was given. */
|
|
106
138
|
/* Warning: because we define a fallback
|
|
107
139
|
media query, the media query can override the container
|
|
108
140
|
if we stack a bunch of these in a row and aren't thoughtful about the order.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import TabItem from "../controls/TabItem.svelte";
|
|
3
3
|
import { elementProps } from "../util";
|
|
4
4
|
import { COLOR_VARS, TYPOGRAPHY_VARS, } from "../styleProps";
|
|
5
|
-
let { sticky = false, active = $bindable(null), items = [], onchange, children, class: className, ...restProps } = $props();
|
|
5
|
+
let { sticky = false, wrap = false, active = $bindable(null), items = [], onchange, children, actions, class: className, ...restProps } = $props();
|
|
6
6
|
/* The shorthands this component's own CSS backs, one group per mixin
|
|
7
7
|
it includes. The Props type is derived from this same array, so what
|
|
8
8
|
the component accepts and what it emits cannot drift apart. */
|
|
@@ -14,7 +14,7 @@ const el = $derived(elementProps(restProps, "tab-bar", TAB_BAR_VARS));
|
|
|
14
14
|
let lastActive = $state(active);
|
|
15
15
|
</script>
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
{#snippet strip()}
|
|
18
18
|
<Bar
|
|
19
19
|
padding="0"
|
|
20
20
|
--button-height="var(--tab-bar-height, 3em)"
|
|
@@ -23,11 +23,15 @@ let lastActive = $state(active);
|
|
|
23
23
|
--bar-fg="var(--tab-bar-fg, var(--bar-fg))"
|
|
24
24
|
--bar-bg="var(--tab-bar-bg, var(--bar-bg))"
|
|
25
25
|
--bar-align="var(--tab-bar-align, flex-end)"
|
|
26
|
-
--bar-overflow="hidden"
|
|
26
|
+
--bar-overflow="var(--tab-bar-overflow, auto hidden)"
|
|
27
|
+
--bar-wrap="var(--tab-bar-wrap, nowrap)"
|
|
27
28
|
--bar-border-left="var(--tab-bar-border-left, none)"
|
|
28
29
|
--bar-border-right="var(--tab-bar-border-right, none)"
|
|
29
30
|
--bar-border-top="var(--tab-bar-border-top, none)"
|
|
30
|
-
--bar-border-bottom=
|
|
31
|
+
--bar-border-bottom={actions
|
|
32
|
+
? "none"
|
|
33
|
+
: "var(--tab-bar-border-bottom, var(--border-width) var(--border-style) var(--border-color))"}
|
|
34
|
+
--bar-margin-block={actions ? "0" : undefined}
|
|
31
35
|
>
|
|
32
36
|
{#each items as item}
|
|
33
37
|
{@const value = typeof item === "string" ? item : item.value}
|
|
@@ -44,6 +48,23 @@ let lastActive = $state(active);
|
|
|
44
48
|
{/each}
|
|
45
49
|
{@render children?.()}
|
|
46
50
|
</Bar>
|
|
51
|
+
{/snippet}
|
|
52
|
+
|
|
53
|
+
<div
|
|
54
|
+
class={["tabs", className]}
|
|
55
|
+
class:sticky
|
|
56
|
+
class:wrap-tabs={wrap}
|
|
57
|
+
class:has-actions={!!actions}
|
|
58
|
+
{...el}
|
|
59
|
+
>
|
|
60
|
+
{#if actions}
|
|
61
|
+
<!-- The strip and the actions share the row; the underline moves to
|
|
62
|
+
the whole row so it runs under both. -->
|
|
63
|
+
<div class="tab-strip">{@render strip()}</div>
|
|
64
|
+
<div class="tab-actions">{@render actions()}</div>
|
|
65
|
+
{:else}
|
|
66
|
+
{@render strip()}
|
|
67
|
+
{/if}
|
|
47
68
|
</div>
|
|
48
69
|
|
|
49
70
|
<style>/* Per-level heading (h1-h6) box/typography properties, resolved to PRIVATE,
|
|
@@ -117,6 +138,38 @@ let lastActive = $state(active);
|
|
|
117
138
|
we deliberately avoid everywhere else.
|
|
118
139
|
|
|
119
140
|
One definition, used by both, so they cannot drift again. */
|
|
141
|
+
/* The "small" size tier: a component whose container is this wide or less
|
|
142
|
+
is on a phone, or in a pane about that narrow, and sizes itself for it.
|
|
143
|
+
|
|
144
|
+
The convention, shared by every component that has a size worth changing
|
|
145
|
+
there (Card started it; Tile, GridLayout, Bar and Container follow):
|
|
146
|
+
|
|
147
|
+
--{component}-{property}-small
|
|
148
|
+
|
|
149
|
+
applies inside a small container. What wins there, in order:
|
|
150
|
+
1. the `-small` variable, if you set it;
|
|
151
|
+
2. otherwise the regular variable, if you set it -- a size you chose on
|
|
152
|
+
purpose is not overridden behind your back;
|
|
153
|
+
3. otherwise the component's own small default, which for most
|
|
154
|
+
properties is simply its regular default.
|
|
155
|
+
|
|
156
|
+
Container queries can't read a custom property, so the breakpoint itself
|
|
157
|
+
is fixed here rather than themeable. 600px matches Card and SplitPane,
|
|
158
|
+
which already switched there. (The Sidebar's rail-to-sheet switch,
|
|
159
|
+
above, is a separate, layout-chrome decision and keeps its own value.) */
|
|
160
|
+
/* Styles for the small tier: the nearest container is narrow, OR the
|
|
161
|
+
viewport is.
|
|
162
|
+
|
|
163
|
+
The container query is the real answer -- a Tile in a 400px pane on a
|
|
164
|
+
desktop is small. But a container query only matches inside an ancestor
|
|
165
|
+
that *is* a container, and a component dropped straight into a page (no
|
|
166
|
+
Page, no Container around it) has none, so on a phone it would never
|
|
167
|
+
find out. The viewport query covers that: a narrow viewport means every
|
|
168
|
+
container in it is narrow too, so the two never disagree in the
|
|
169
|
+
direction that matters.
|
|
170
|
+
|
|
171
|
+
An element never queries itself, so on a component that is its own
|
|
172
|
+
container (Tile, Bar) this asks about the space it was given. */
|
|
120
173
|
/* Warning: because we define a fallback
|
|
121
174
|
media query, the media query can override the container
|
|
122
175
|
if we stack a bunch of these in a row and aren't thoughtful about the order.
|
|
@@ -125,7 +178,7 @@ let lastActive = $state(active);
|
|
|
125
178
|
*/
|
|
126
179
|
/* Convenience groupings */
|
|
127
180
|
div > :global(.bar),
|
|
128
|
-
div > :global(
|
|
181
|
+
div > :global(svelte-css-wrapper > .bar) {
|
|
129
182
|
--link-bg: var(--tab-bar-link-bg, var(--bar-link-bg, var(--surface-link-bg, inherit)));
|
|
130
183
|
--link-fg: var(--tab-bar-link-fg, var(--bar-link-fg, var(--surface-link-fg, inherit)));
|
|
131
184
|
--_bg-mix-color: var(--tab-bar-bg-mix-color, var(--bar-bg-mix-color, var(--surface-bg-mix-color, var(--bg-mix-color, transparent))));
|
|
@@ -224,12 +277,78 @@ div > :global(div > .bar) {
|
|
|
224
277
|
align-items: var(--tab-bar-align, var(--align, flex-end));
|
|
225
278
|
border-bottom: var(--tab-bar-border-bottom, var(--border-width) var(--border-style) var(--border-color));
|
|
226
279
|
border-left: var(--tab-bar-border-left, none);
|
|
227
|
-
border-right: var(--tab-bar-border-
|
|
228
|
-
border-top: var(--tab-bar-border-
|
|
280
|
+
border-right: var(--tab-bar-border-right, none);
|
|
281
|
+
border-top: var(--tab-bar-border-top, none);
|
|
229
282
|
gap: var(--tab-bar-gap, var(--space-md));
|
|
230
|
-
|
|
283
|
+
/* Tabs stay one strip. When they don't all fit -- four tabs on a phone --
|
|
284
|
+
the strip scrolls sideways rather than dropping the last tab onto a
|
|
285
|
+
second row, which reads as a broken tab set (the second row floats
|
|
286
|
+
under the active tab's baseline). `wrap` (or --tab-bar-wrap: wrap)
|
|
287
|
+
brings wrapping back. */
|
|
288
|
+
overflow-x: auto;
|
|
289
|
+
overflow-y: hidden;
|
|
290
|
+
scrollbar-width: thin;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/* The scroll also goes in through --bar-overflow on the Bar itself (x
|
|
294
|
+
scrolls, y clipped), so it holds even where a consumer's own CSS beats
|
|
295
|
+
the rule above. */
|
|
296
|
+
.wrap-tabs {
|
|
297
|
+
--tab-bar-wrap: wrap;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.has-actions {
|
|
301
|
+
display: flex;
|
|
302
|
+
align-items: flex-end;
|
|
303
|
+
gap: var(--tab-bar-gap, var(--gap, var(--space-md, 8px)));
|
|
304
|
+
border-bottom: var(--tab-bar-border-bottom, var(--border-width) var(--border-style) var(--border-color));
|
|
305
|
+
margin-block: var(--tab-bar-margin-block, 0 1em);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/* The strip takes the room and gives way first: min-width 0 lets it
|
|
309
|
+
shrink below its tabs' width, at which point it scrolls (above). */
|
|
310
|
+
.tab-strip {
|
|
311
|
+
flex: 1 1 auto;
|
|
312
|
+
min-width: 0;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/* The row carries the underline; the strip's own (from the rule above)
|
|
316
|
+
would double it and stop short of the actions. */
|
|
317
|
+
.tab-strip > :global(svelte-css-wrapper > .bar),
|
|
318
|
+
.tab-strip > :global(.bar) {
|
|
319
|
+
border-bottom: none;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.tab-actions {
|
|
323
|
+
flex: 0 1 auto;
|
|
324
|
+
display: flex;
|
|
325
|
+
flex-wrap: wrap;
|
|
326
|
+
justify-content: flex-end;
|
|
327
|
+
align-items: center;
|
|
328
|
+
gap: var(--tab-bar-actions-gap, var(--gap, var(--space-md, 8px)));
|
|
329
|
+
padding-block-end: var(--tab-bar-actions-padding, var(--padding, var(--space-sm, 4px)));
|
|
231
330
|
}
|
|
232
331
|
|
|
332
|
+
/* Small tier: actions get their own row, above the tabs, so the tabs stay
|
|
333
|
+
attached to the panel they switch. */
|
|
334
|
+
@media (max-width: 600px) {
|
|
335
|
+
.has-actions {
|
|
336
|
+
flex-direction: column-reverse;
|
|
337
|
+
align-items: stretch;
|
|
338
|
+
}
|
|
339
|
+
.tab-actions {
|
|
340
|
+
justify-content: flex-start;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
@container (max-width: 600px) {
|
|
344
|
+
.has-actions {
|
|
345
|
+
flex-direction: column-reverse;
|
|
346
|
+
align-items: stretch;
|
|
347
|
+
}
|
|
348
|
+
.tab-actions {
|
|
349
|
+
justify-content: flex-start;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
233
352
|
.sticky {
|
|
234
353
|
position: sticky;
|
|
235
354
|
top: calc(-1 * var(--surface-padding, var(--block-padding, var(--padding, 8px))));
|
|
@@ -3,6 +3,14 @@ import type { ContainProps } from "../types";
|
|
|
3
3
|
import { type StyleProps } from "../styleProps";
|
|
4
4
|
declare const TabBar: import("svelte").Component<ContainProps<HTMLAttributes<HTMLDivElement>, {
|
|
5
5
|
sticky?: boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Let tabs that don't fit wrap onto another row. By default the tab
|
|
8
|
+
* strip stays one row and scrolls sideways when it runs out of room,
|
|
9
|
+
* which keeps the active tab attached to its panel; wrapping suits a
|
|
10
|
+
* long set of short, filter-like tabs where seeing them all at once
|
|
11
|
+
* matters more. Same as setting --tab-bar-wrap: wrap.
|
|
12
|
+
*/
|
|
13
|
+
wrap?: boolean;
|
|
6
14
|
active?: string | {
|
|
7
15
|
label: string;
|
|
8
16
|
value: string;
|
|
@@ -16,6 +24,14 @@ declare const TabBar: import("svelte").Component<ContainProps<HTMLAttributes<HTM
|
|
|
16
24
|
value: string;
|
|
17
25
|
} | null) => void;
|
|
18
26
|
children?: import("svelte").Snippet;
|
|
27
|
+
/**
|
|
28
|
+
* Controls that belong to the tab row but aren't tabs -- a Refresh
|
|
29
|
+
* button, a "New..." action. They sit at the end of the row when there
|
|
30
|
+
* is room, and on their own row above the tabs in the small tier (see
|
|
31
|
+
* the -small variables), so they never squeeze the tabs or stack into
|
|
32
|
+
* a tall column beside them on a phone.
|
|
33
|
+
*/
|
|
34
|
+
actions?: import("svelte").Snippet;
|
|
19
35
|
}, StyleProps<readonly ["bg", "fg", "fontFamily", "fontSize", "fontWeight", "lineHeight", "letterSpacing", "textAlign", "textTransform", "textDecoration", "fontVariant"]>>, {}, "active">;
|
|
20
36
|
type TabBar = ReturnType<typeof TabBar>;
|
|
21
37
|
export default TabBar;
|