@jungherz-de/glasskit 1.31.0 → 1.33.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/CHANGELOG.md +141 -1
- package/README.md +161 -22
- package/SKILL.md +575 -22
- package/glasskit-droplet.js +70 -32
- package/glasskit-styles.js +2 -2
- package/glasskit.css +934 -35
- package/glasskit.min.css +1 -1
- package/glasskit.min.css.map +1 -1
- package/package.json +2 -2
- package/theme-override.css +119 -0
package/glasskit.css
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* ============================================================
|
|
2
2
|
GLASSKIT COMPONENT LIBRARY
|
|
3
|
-
Jungherz GmbH – v1.
|
|
3
|
+
Jungherz GmbH – v1.33.0
|
|
4
4
|
|
|
5
5
|
Features:
|
|
6
6
|
- Scoped Reset (box-sizing fix)
|
|
@@ -799,12 +799,15 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
|
|
|
799
799
|
stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
|
|
800
800
|
}
|
|
801
801
|
|
|
802
|
-
/* Primary
|
|
802
|
+
/* Primary. --gl-btn-primary-bg (since 1.32.0) replaces the whole
|
|
803
|
+
background, as the toggle and the radio have theirs: a product that keeps
|
|
804
|
+
white text starts the gradient darker without touching the brand colour,
|
|
805
|
+
which also draws focus rings, borders and the accent as text. */
|
|
803
806
|
.glass-btn--primary {
|
|
804
|
-
background: linear-gradient(135deg,
|
|
807
|
+
background: var(--gl-btn-primary-bg, linear-gradient(135deg,
|
|
805
808
|
var(--gl-color-primary) 0%, var(--gl-color-primary-mid) 50%,
|
|
806
809
|
var(--gl-color-primary-dark) 100%
|
|
807
|
-
);
|
|
810
|
+
));
|
|
808
811
|
color: var(--gl-color-on-primary, #ffffff);
|
|
809
812
|
border: 1px solid var(--gl-border-warm);
|
|
810
813
|
box-shadow: var(--gl-shadow-btn-primary), var(--gl-inset-strong);
|
|
@@ -924,6 +927,21 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
|
|
|
924
927
|
above its field (until 1.24.0). */
|
|
925
928
|
.glass-label:empty { display: none; }
|
|
926
929
|
|
|
930
|
+
/* A place at the end of the label row (since 1.33.0): "Forgot password?"
|
|
931
|
+
on the line of the label "Password". The __label-end element comes
|
|
932
|
+
after the field in the markup, so the keyboard reaches it after the
|
|
933
|
+
field, which reads best in a sign-in form; --label-end draws it on the
|
|
934
|
+
label's line, at its end. */
|
|
935
|
+
.glass-input-group.glass-input-group--label-end {
|
|
936
|
+
display: grid; grid-template-columns: minmax(0, 1fr) auto;
|
|
937
|
+
align-items: center; column-gap: var(--gl-space-sm);
|
|
938
|
+
}
|
|
939
|
+
.glass-input-group--label-end > * { grid-column: 1 / -1; }
|
|
940
|
+
.glass-input-group--label-end > .glass-label { grid-column: 1; grid-row: 1; }
|
|
941
|
+
.glass-input-group--label-end > .glass-input-group__label-end { grid-column: 2; grid-row: 1; justify-self: end; }
|
|
942
|
+
.glass-input-group__label-end { font-size: var(--gl-font-size-sm); }
|
|
943
|
+
.glass-input-group__label-end[hidden] { display: none; }
|
|
944
|
+
|
|
927
945
|
.glass-input {
|
|
928
946
|
width: 100%; height: var(--gl-control-height, 52px);
|
|
929
947
|
padding: 0 var(--gl-control-padding-x, var(--gl-space-md));
|
|
@@ -1336,7 +1354,9 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
|
|
|
1336
1354
|
.glass-radio__dot {
|
|
1337
1355
|
width: var(--gl-radio-dot-size, 10px); height: var(--gl-radio-dot-size, 10px);
|
|
1338
1356
|
border-radius: var(--gl-radius-pill);
|
|
1339
|
-
|
|
1357
|
+
/* Unset, the ink on the primary (since 1.32.0), so the dot turns dark
|
|
1358
|
+
with the checkbox tick where a project switches the ink. */
|
|
1359
|
+
background: var(--gl-radio-dot-color, var(--gl-color-on-primary, white));
|
|
1340
1360
|
opacity: 0; transform: scale(0);
|
|
1341
1361
|
transition: var(--gl-transition-fast);
|
|
1342
1362
|
}
|
|
@@ -1726,7 +1746,12 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
|
|
|
1726
1746
|
padding: var(--gl-space-2xs) var(--gl-space-sm);
|
|
1727
1747
|
background: none; border: none; cursor: pointer;
|
|
1728
1748
|
border-radius: var(--gl-radius-sm);
|
|
1729
|
-
|
|
1749
|
+
/* Colours and the press move; the box does not (since 1.33.0): a bar that
|
|
1750
|
+
changes its layout — upright, floating, a rail — took 250ms to settle
|
|
1751
|
+
with `all`, and an item measured then was not yet its size. */
|
|
1752
|
+
transition-property: color, background-color, box-shadow, opacity, transform;
|
|
1753
|
+
transition-duration: 0.25s; transition-timing-function: ease;
|
|
1754
|
+
outline: none;
|
|
1730
1755
|
-webkit-tap-highlight-color: transparent;
|
|
1731
1756
|
min-width: 60px;
|
|
1732
1757
|
}
|
|
@@ -1738,26 +1763,28 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
|
|
|
1738
1763
|
inactive colour is --gl-icon-muted and the hovered one --gl-icon-default;
|
|
1739
1764
|
--gl-tab-bar-color and --gl-tab-bar-hover-color set them, for icon and
|
|
1740
1765
|
label alike. */
|
|
1766
|
+
/* --gl-tab-bar-icon-size (24px, since 1.33.0) sizes the box and the SVG:
|
|
1767
|
+
a compact top bar takes the 18px of the segmented control's icons. */
|
|
1741
1768
|
.glass-tab-bar__icon {
|
|
1742
|
-
width: 24px; height: 24px;
|
|
1769
|
+
width: var(--gl-tab-bar-icon-size, 24px); height: var(--gl-tab-bar-icon-size, 24px);
|
|
1743
1770
|
display: flex; align-items: center; justify-content: center;
|
|
1744
1771
|
position: relative;
|
|
1745
1772
|
color: var(--gl-tab-bar-color, var(--gl-icon-muted));
|
|
1746
|
-
font-size: 24px;
|
|
1747
|
-
transition:
|
|
1773
|
+
font-size: var(--gl-tab-bar-icon-size, 24px);
|
|
1774
|
+
transition: color 0.25s ease;
|
|
1748
1775
|
}
|
|
1749
1776
|
|
|
1750
1777
|
.glass-tab-bar__icon svg,
|
|
1751
1778
|
.glass-tab-bar__icon ::slotted(svg) {
|
|
1752
|
-
width: 24px; height: 24px; fill: none;
|
|
1779
|
+
width: var(--gl-tab-bar-icon-size, 24px); height: var(--gl-tab-bar-icon-size, 24px); fill: none;
|
|
1753
1780
|
stroke: currentColor; stroke-width: 1.8;
|
|
1754
1781
|
stroke-linecap: round; stroke-linejoin: round;
|
|
1755
|
-
transition:
|
|
1782
|
+
transition: color 0.25s ease;
|
|
1756
1783
|
}
|
|
1757
1784
|
|
|
1758
1785
|
.glass-tab-bar__label {
|
|
1759
1786
|
font-size: var(--gl-tab-bar-label-size, 10px); font-weight: var(--gl-font-weight-medium);
|
|
1760
|
-
color: var(--gl-tab-bar-color, var(--gl-icon-muted)); transition:
|
|
1787
|
+
color: var(--gl-tab-bar-color, var(--gl-icon-muted)); transition: color 0.25s ease;
|
|
1761
1788
|
letter-spacing: 0.2px;
|
|
1762
1789
|
}
|
|
1763
1790
|
|
|
@@ -1771,7 +1798,12 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
|
|
|
1771
1798
|
.glass-tab-bar__item.is-active .glass-tab-bar__icon ::slotted(*) {
|
|
1772
1799
|
filter: var(--gl-tab-bar-active-glow, drop-shadow(0 0 6px color-mix(in srgb, var(--gl-color-primary) 35%, transparent)));
|
|
1773
1800
|
}
|
|
1774
|
-
|
|
1801
|
+
/* --gl-tab-bar-active-weight (since 1.33.0) sets the active label's weight,
|
|
1802
|
+
unset the medium of every label. */
|
|
1803
|
+
.glass-tab-bar__item.is-active .glass-tab-bar__label {
|
|
1804
|
+
color: var(--gl-tab-bar-active-color, var(--gl-color-primary-text));
|
|
1805
|
+
font-weight: var(--gl-tab-bar-active-weight, var(--gl-font-weight-medium));
|
|
1806
|
+
}
|
|
1775
1807
|
|
|
1776
1808
|
/* The active item's own surface; unset, it has none. A capsule like the
|
|
1777
1809
|
selected tab of iOS 26 takes --gl-tab-bar-active-bg and -shadow; on the
|
|
@@ -1841,9 +1873,12 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
|
|
|
1841
1873
|
gap: var(--gl-space-2xs);
|
|
1842
1874
|
}
|
|
1843
1875
|
|
|
1876
|
+
/* --gl-tab-bar-item-radius (since 1.33.0) rounds the floating items and the
|
|
1877
|
+
drop, unset a capsule: rounded squares in a rail, the 12px of the
|
|
1878
|
+
segmented control in a compact top bar. */
|
|
1844
1879
|
.glass-tab-bar--floating .glass-tab-bar__item {
|
|
1845
1880
|
position: relative;
|
|
1846
|
-
border-radius: var(--gl-radius-full);
|
|
1881
|
+
border-radius: var(--gl-tab-bar-item-radius, var(--gl-radius-full));
|
|
1847
1882
|
padding: var(--gl-space-2xs) var(--gl-space-sm);
|
|
1848
1883
|
min-width: auto;
|
|
1849
1884
|
align-items: center;
|
|
@@ -1984,7 +2019,16 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
|
|
|
1984
2019
|
on the item below it. Without the script a row keeps its usual look:
|
|
1985
2020
|
the tab bar its spotlight, the segmented row its raised option. GlassKit
|
|
1986
2021
|
Elements' <glk-tab-bar droplet> and <glk-segmented droplet> do all of
|
|
1987
|
-
this; for plain markup the docs have the script.
|
|
2022
|
+
this; for plain markup the docs have the script.
|
|
2023
|
+
|
|
2024
|
+
The motion is tokens (since 1.32.0), unset today's values, read by the
|
|
2025
|
+
stylesheet and the script alike: --gl-droplet-duration and -easing for
|
|
2026
|
+
the leading edge, -trail-duration, -trail-delay and -trail-easing for the
|
|
2027
|
+
trailing one, -lift (0.86; 1 squashes nothing), -lift-duration (280ms),
|
|
2028
|
+
-settle-duration and -settle-easing for the spring back, -lens-duration
|
|
2029
|
+
and -lens-easing for the item below the drop. In a column — a vertical
|
|
2030
|
+
tab bar, a sidebar — the script sets data-droplet-axis="y" and the
|
|
2031
|
+
directions "down" and "up", and the drop runs along y. */
|
|
1988
2032
|
.glass-tab-bar--floating.glass-tab-bar--droplet,
|
|
1989
2033
|
.glass-segmented.glass-segmented--droplet { position: relative; }
|
|
1990
2034
|
.glass-tab-bar.glass-tab-bar--droplet[data-droplet],
|
|
@@ -2012,13 +2056,15 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
|
|
|
2012
2056
|
-webkit-backdrop-filter: blur(var(--gl-blur-light)) saturate(1.6);
|
|
2013
2057
|
pointer-events: none;
|
|
2014
2058
|
transition:
|
|
2015
|
-
transform 0.55s cubic-bezier(0.34, 1.8, 0.5, 1),
|
|
2059
|
+
transform var(--gl-droplet-settle-duration, 0.55s) var(--gl-droplet-settle-easing, cubic-bezier(0.34, 1.8, 0.5, 1)),
|
|
2016
2060
|
box-shadow 0.3s ease;
|
|
2017
2061
|
}
|
|
2062
|
+
/* A column hands sideways swipes to the page and keeps the vertical drag. */
|
|
2063
|
+
:is(.glass-tab-bar.glass-tab-bar--droplet, .glass-segmented.glass-segmented--droplet)[data-droplet][data-droplet-axis="y"] { touch-action: pan-x; }
|
|
2018
2064
|
/* The tab bar's drop is a capsule, in the active item's surface where a
|
|
2019
2065
|
project set one (--gl-tab-bar-active-bg, -shadow). */
|
|
2020
2066
|
.glass-tab-bar.glass-tab-bar--droplet[data-droplet]::before {
|
|
2021
|
-
border-radius: var(--gl-radius-full);
|
|
2067
|
+
border-radius: var(--gl-tab-bar-item-radius, var(--gl-radius-full));
|
|
2022
2068
|
background:
|
|
2023
2069
|
var(--gl-gloss-droplet, none),
|
|
2024
2070
|
var(--gl-tab-bar-active-bg, linear-gradient(to bottom, var(--gl-surface-5) 0%, var(--gl-surface-3) 100%));
|
|
@@ -2042,18 +2088,35 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
|
|
|
2042
2088
|
after it and overshoots, so the drop stretches and draws together. */
|
|
2043
2089
|
:is(.glass-tab-bar.glass-tab-bar--droplet, .glass-segmented.glass-segmented--droplet)[data-droplet][data-droplet-dir="right"]::before {
|
|
2044
2090
|
transition:
|
|
2045
|
-
right 0.3s cubic-bezier(0.3, 1.2, 0.5, 1),
|
|
2046
|
-
left 0.5s cubic-bezier(0.5, 1.5, 0.45, 0.92) 0.04s,
|
|
2047
|
-
top 0.3s ease, bottom 0.3s ease,
|
|
2048
|
-
transform 0.55s cubic-bezier(0.34, 1.8, 0.5, 1),
|
|
2091
|
+
right var(--gl-droplet-duration, 0.3s) var(--gl-droplet-easing, cubic-bezier(0.3, 1.2, 0.5, 1)),
|
|
2092
|
+
left var(--gl-droplet-trail-duration, 0.5s) var(--gl-droplet-trail-easing, cubic-bezier(0.5, 1.5, 0.45, 0.92)) var(--gl-droplet-trail-delay, 0.04s),
|
|
2093
|
+
top var(--gl-droplet-duration, 0.3s) ease, bottom var(--gl-droplet-duration, 0.3s) ease,
|
|
2094
|
+
transform var(--gl-droplet-settle-duration, 0.55s) var(--gl-droplet-settle-easing, cubic-bezier(0.34, 1.8, 0.5, 1)),
|
|
2049
2095
|
box-shadow 0.3s ease;
|
|
2050
2096
|
}
|
|
2051
2097
|
:is(.glass-tab-bar.glass-tab-bar--droplet, .glass-segmented.glass-segmented--droplet)[data-droplet][data-droplet-dir="left"]::before {
|
|
2052
2098
|
transition:
|
|
2053
|
-
left 0.3s cubic-bezier(0.3, 1.2, 0.5, 1),
|
|
2054
|
-
right 0.5s cubic-bezier(0.5, 1.5, 0.45, 0.92) 0.04s,
|
|
2055
|
-
top 0.3s ease, bottom 0.3s ease,
|
|
2056
|
-
transform 0.55s cubic-bezier(0.34, 1.8, 0.5, 1),
|
|
2099
|
+
left var(--gl-droplet-duration, 0.3s) var(--gl-droplet-easing, cubic-bezier(0.3, 1.2, 0.5, 1)),
|
|
2100
|
+
right var(--gl-droplet-trail-duration, 0.5s) var(--gl-droplet-trail-easing, cubic-bezier(0.5, 1.5, 0.45, 0.92)) var(--gl-droplet-trail-delay, 0.04s),
|
|
2101
|
+
top var(--gl-droplet-duration, 0.3s) ease, bottom var(--gl-droplet-duration, 0.3s) ease,
|
|
2102
|
+
transform var(--gl-droplet-settle-duration, 0.55s) var(--gl-droplet-settle-easing, cubic-bezier(0.34, 1.8, 0.5, 1)),
|
|
2103
|
+
box-shadow 0.3s ease;
|
|
2104
|
+
}
|
|
2105
|
+
/* In a column the same journey runs along y (since 1.32.0). */
|
|
2106
|
+
:is(.glass-tab-bar.glass-tab-bar--droplet, .glass-segmented.glass-segmented--droplet)[data-droplet][data-droplet-dir="down"]::before {
|
|
2107
|
+
transition:
|
|
2108
|
+
bottom var(--gl-droplet-duration, 0.3s) var(--gl-droplet-easing, cubic-bezier(0.3, 1.2, 0.5, 1)),
|
|
2109
|
+
top var(--gl-droplet-trail-duration, 0.5s) var(--gl-droplet-trail-easing, cubic-bezier(0.5, 1.5, 0.45, 0.92)) var(--gl-droplet-trail-delay, 0.04s),
|
|
2110
|
+
left var(--gl-droplet-duration, 0.3s) ease, right var(--gl-droplet-duration, 0.3s) ease,
|
|
2111
|
+
transform var(--gl-droplet-settle-duration, 0.55s) var(--gl-droplet-settle-easing, cubic-bezier(0.34, 1.8, 0.5, 1)),
|
|
2112
|
+
box-shadow 0.3s ease;
|
|
2113
|
+
}
|
|
2114
|
+
:is(.glass-tab-bar.glass-tab-bar--droplet, .glass-segmented.glass-segmented--droplet)[data-droplet][data-droplet-dir="up"]::before {
|
|
2115
|
+
transition:
|
|
2116
|
+
top var(--gl-droplet-duration, 0.3s) var(--gl-droplet-easing, cubic-bezier(0.3, 1.2, 0.5, 1)),
|
|
2117
|
+
bottom var(--gl-droplet-trail-duration, 0.5s) var(--gl-droplet-trail-easing, cubic-bezier(0.5, 1.5, 0.45, 0.92)) var(--gl-droplet-trail-delay, 0.04s),
|
|
2118
|
+
left var(--gl-droplet-duration, 0.3s) ease, right var(--gl-droplet-duration, 0.3s) ease,
|
|
2119
|
+
transform var(--gl-droplet-settle-duration, 0.55s) var(--gl-droplet-settle-easing, cubic-bezier(0.34, 1.8, 0.5, 1)),
|
|
2057
2120
|
box-shadow 0.3s ease;
|
|
2058
2121
|
}
|
|
2059
2122
|
/* While it travels the drop runs flatter and lifts off the row; it comes
|
|
@@ -2068,7 +2131,7 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
|
|
|
2068
2131
|
var(--gl-inset-strong);
|
|
2069
2132
|
}
|
|
2070
2133
|
:is(.glass-tab-bar.glass-tab-bar--droplet, .glass-segmented.glass-segmented--droplet)[data-droplet][data-droplet-lift]::before {
|
|
2071
|
-
transform: scale(1, 0.86);
|
|
2134
|
+
transform: scale(1, var(--gl-droplet-lift, 0.86));
|
|
2072
2135
|
}
|
|
2073
2136
|
:is(.glass-tab-bar.glass-tab-bar--droplet, .glass-segmented.glass-segmented--droplet)[data-droplet][data-droplet-drag]::before {
|
|
2074
2137
|
transform: scale(1.1, 1.22);
|
|
@@ -2076,10 +2139,22 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
|
|
|
2076
2139
|
left 0.1s ease-out, right 0.1s ease-out,
|
|
2077
2140
|
transform 0.25s ease-out, box-shadow 0.25s ease;
|
|
2078
2141
|
}
|
|
2142
|
+
/* Along y the drop runs narrower instead, and grows less across its long
|
|
2143
|
+
side when held: a sidebar's item is wide. */
|
|
2144
|
+
:is(.glass-tab-bar.glass-tab-bar--droplet, .glass-segmented.glass-segmented--droplet)[data-droplet][data-droplet-axis="y"][data-droplet-lift]::before {
|
|
2145
|
+
transform: scale(var(--gl-droplet-lift, 0.86), 1);
|
|
2146
|
+
}
|
|
2147
|
+
:is(.glass-tab-bar.glass-tab-bar--droplet, .glass-segmented.glass-segmented--droplet)[data-droplet][data-droplet-axis="y"][data-droplet-drag]::before {
|
|
2148
|
+
transform: scale(1.04, 1.18);
|
|
2149
|
+
transition:
|
|
2150
|
+
top 0.1s ease-out, bottom 0.1s ease-out,
|
|
2151
|
+
transform 0.25s ease-out, box-shadow 0.25s ease;
|
|
2152
|
+
}
|
|
2079
2153
|
|
|
2080
2154
|
/* The drop is the chosen item's surface, so the item draws none; on the
|
|
2081
2155
|
tab bar neither spotlight, dot nor glow. */
|
|
2082
2156
|
.glass-tab-bar--droplet[data-droplet] .glass-tab-bar__item.is-active { background: none; box-shadow: none; }
|
|
2157
|
+
.glass-tab-bar--droplet[data-droplet] .glass-tab-bar__item.is-active:focus-visible { box-shadow: var(--gl-shadow-focus); }
|
|
2083
2158
|
.glass-tab-bar--droplet[data-droplet] .glass-tab-bar__item.is-active::before { display: none; }
|
|
2084
2159
|
.glass-tab-bar--droplet[data-droplet] .glass-tab-bar__item.is-active .glass-tab-bar__icon::after { display: none; }
|
|
2085
2160
|
.glass-tab-bar--droplet[data-droplet] .glass-tab-bar__item.is-active .glass-tab-bar__icon > :where(:not(.glass-tab-bar__badge)),
|
|
@@ -2093,9 +2168,9 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
|
|
|
2093
2168
|
a lens. While a finger drags the drop, the item below it takes the
|
|
2094
2169
|
chosen colour and the chosen item lets it go. The tab icon keeps the
|
|
2095
2170
|
fade of its colour next to the lens. */
|
|
2096
|
-
.glass-tab-bar--droplet[data-droplet] .glass-tab-bar__icon { transition: var(--gl-
|
|
2171
|
+
.glass-tab-bar--droplet[data-droplet] .glass-tab-bar__icon { transition: color 0.25s ease, transform var(--gl-droplet-lens-duration, 0.4s) var(--gl-droplet-lens-easing, cubic-bezier(0.34, 1.6, 0.5, 1)); }
|
|
2097
2172
|
.glass-segmented--droplet[data-droplet] .glass-segmented__dot,
|
|
2098
|
-
.glass-segmented--droplet[data-droplet] .glass-segmented__icon { transition: transform 0.4s cubic-bezier(0.34, 1.6, 0.5, 1); }
|
|
2173
|
+
.glass-segmented--droplet[data-droplet] .glass-segmented__icon { transition: transform var(--gl-droplet-lens-duration, 0.4s) var(--gl-droplet-lens-easing, cubic-bezier(0.34, 1.6, 0.5, 1)); }
|
|
2099
2174
|
.glass-tab-bar--droplet[data-droplet] .glass-tab-bar__item.is-under-droplet .glass-tab-bar__icon { transform: scale(1.15); }
|
|
2100
2175
|
.glass-segmented--droplet[data-droplet] .glass-segmented__item.is-under-droplet .glass-segmented__dot { transform: scale(1.3); }
|
|
2101
2176
|
.glass-segmented--droplet[data-droplet] .glass-segmented__item.is-under-droplet .glass-segmented__icon { transform: scale(1.15); }
|
|
@@ -2107,13 +2182,277 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
|
|
|
2107
2182
|
@media (prefers-reduced-motion: reduce) {
|
|
2108
2183
|
:is(.glass-tab-bar.glass-tab-bar--droplet, .glass-segmented.glass-segmented--droplet)[data-droplet]::before,
|
|
2109
2184
|
:is(.glass-tab-bar.glass-tab-bar--droplet, .glass-segmented.glass-segmented--droplet)[data-droplet][data-droplet-dir]::before,
|
|
2110
|
-
:is(.glass-tab-bar.glass-tab-bar--droplet, .glass-segmented.glass-segmented--droplet)[data-droplet][data-droplet-drag]::before
|
|
2111
|
-
.glass-tab-bar--droplet[data-droplet]
|
|
2185
|
+
:is(.glass-tab-bar.glass-tab-bar--droplet, .glass-segmented.glass-segmented--droplet)[data-droplet][data-droplet-drag]::before,
|
|
2186
|
+
:is(.glass-tab-bar.glass-tab-bar--droplet, .glass-segmented.glass-segmented--droplet)[data-droplet][data-droplet-axis="y"][data-droplet-drag]::before { transition: none; }
|
|
2187
|
+
.glass-tab-bar--droplet[data-droplet] .glass-tab-bar__icon { transition: color 0.25s ease, transform 0s; }
|
|
2112
2188
|
.glass-segmented--droplet[data-droplet] .glass-segmented__dot,
|
|
2113
2189
|
.glass-segmented--droplet[data-droplet] .glass-segmented__icon { transition: none; }
|
|
2114
2190
|
}
|
|
2115
2191
|
|
|
2116
2192
|
|
|
2193
|
+
/* ============================================================
|
|
2194
|
+
18d. TAB BAR – SIDEBAR, RAIL, TOP BAR, SIDE DOCK
|
|
2195
|
+
============================================================ */
|
|
2196
|
+
/* One navigation in more places than the bottom edge (since 1.32.0). The
|
|
2197
|
+
items stay the same — icon, label, badge, the active state, the drop —
|
|
2198
|
+
and only the bar's layout changes:
|
|
2199
|
+
|
|
2200
|
+
--vertical a column for a sidebar: placed by the page's layout (a grid
|
|
2201
|
+
column) and as high as its container, one item per row with
|
|
2202
|
+
the icon before the label at body size and the badge at the
|
|
2203
|
+
row's end. With --start or --end it is fixed to that edge of
|
|
2204
|
+
the viewport instead, full height and clear of the edge's
|
|
2205
|
+
safe area — the side a phone's thumb reaches when the system
|
|
2206
|
+
moves its own controls there. Start and end follow the
|
|
2207
|
+
writing direction.
|
|
2208
|
+
--rail with --vertical: icons alone, --gl-tab-bar-rail-width wide
|
|
2209
|
+
(72px). The label stays the item's name for assistive
|
|
2210
|
+
technology, hidden from view — show it in a tooltip — and the
|
|
2211
|
+
badge goes back to the icon's corner.
|
|
2212
|
+
--top a horizontal bar at the top: in the flow (put it in a sticky
|
|
2213
|
+
header), its border at the bottom, no room for the home
|
|
2214
|
+
indicator, and from 640px the icon beside the label.
|
|
2215
|
+
|
|
2216
|
+
The floating bar stands upright in a dock with --vertical and --start or
|
|
2217
|
+
--end: its items stacked as on the bottom edge, the accessory below them,
|
|
2218
|
+
above with --accessory-left. In a column the drop runs along y; the
|
|
2219
|
+
droplet script reads the flex direction. */
|
|
2220
|
+
|
|
2221
|
+
/* Every bar: a ring for the keyboard, which the items had none of. An
|
|
2222
|
+
item may be a link, to open a view in a new tab. */
|
|
2223
|
+
.glass-tab-bar__item:focus-visible { box-shadow: var(--gl-shadow-focus); }
|
|
2224
|
+
a.glass-tab-bar__item { text-decoration: none; color: inherit; }
|
|
2225
|
+
|
|
2226
|
+
/* ---- Sidebar ---- */
|
|
2227
|
+
.glass-tab-bar.glass-tab-bar--vertical {
|
|
2228
|
+
position: relative; inset: auto;
|
|
2229
|
+
height: 100%; width: auto;
|
|
2230
|
+
flex-direction: column; justify-content: flex-start; align-items: stretch;
|
|
2231
|
+
gap: var(--gl-space-2xs);
|
|
2232
|
+
padding: var(--gl-space-sm);
|
|
2233
|
+
border-top: none;
|
|
2234
|
+
border-inline-end: 1px solid var(--gl-border-subtle);
|
|
2235
|
+
overflow-y: auto; overscroll-behavior: contain;
|
|
2236
|
+
box-shadow: var(--gl-inset-subtle);
|
|
2237
|
+
}
|
|
2238
|
+
.glass-tab-bar--vertical .glass-tab-bar__item {
|
|
2239
|
+
position: relative;
|
|
2240
|
+
flex-direction: row; justify-content: flex-start;
|
|
2241
|
+
gap: var(--gl-space-sm);
|
|
2242
|
+
width: 100%; min-width: 0; min-height: 44px;
|
|
2243
|
+
padding: var(--gl-space-xs) var(--gl-space-md);
|
|
2244
|
+
text-align: start; text-decoration: none;
|
|
2245
|
+
}
|
|
2246
|
+
.glass-tab-bar--vertical .glass-tab-bar__label {
|
|
2247
|
+
flex: 1 1 auto; min-width: 0;
|
|
2248
|
+
font-size: var(--gl-font-size-base);
|
|
2249
|
+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
2250
|
+
}
|
|
2251
|
+
.glass-tab-bar--vertical .glass-tab-bar__item:not(.is-active):hover { background: var(--gl-surface-1); }
|
|
2252
|
+
/* The active row stands on a surface of its own, unset --gl-surface-3,
|
|
2253
|
+
where the bottom bar has its dot. */
|
|
2254
|
+
.glass-tab-bar--vertical .glass-tab-bar__item.is-active { background: var(--gl-tab-bar-active-bg, var(--gl-surface-3)); }
|
|
2255
|
+
.glass-tab-bar--vertical .glass-tab-bar__item.is-active .glass-tab-bar__icon::after { display: none; }
|
|
2256
|
+
/* The badge at the row's end: positioned on the item, not the icon. */
|
|
2257
|
+
.glass-tab-bar--vertical:not(.glass-tab-bar--rail, .glass-tab-bar--floating) .glass-tab-bar__icon { position: static; }
|
|
2258
|
+
.glass-tab-bar--vertical:not(.glass-tab-bar--rail, .glass-tab-bar--floating) .glass-tab-bar__badge {
|
|
2259
|
+
top: 50%; inset-inline-end: var(--gl-space-md);
|
|
2260
|
+
transform: translateY(-50%);
|
|
2261
|
+
}
|
|
2262
|
+
/* A long label ends before the badge. */
|
|
2263
|
+
.glass-tab-bar--vertical:not(.glass-tab-bar--rail, .glass-tab-bar--floating) .glass-tab-bar__item:has(.glass-tab-bar__badge:not([hidden])) .glass-tab-bar__label {
|
|
2264
|
+
padding-inline-end: var(--gl-tab-bar-badge-room, 32px);
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2267
|
+
/* Fixed to an edge, full height, clear of that edge's safe area. */
|
|
2268
|
+
.glass-tab-bar.glass-tab-bar--vertical:is(.glass-tab-bar--start, .glass-tab-bar--end) {
|
|
2269
|
+
position: fixed; top: 0; bottom: 0; height: auto;
|
|
2270
|
+
padding-top: max(var(--gl-space-sm), env(safe-area-inset-top, 0px));
|
|
2271
|
+
padding-bottom: max(var(--gl-space-sm), env(safe-area-inset-bottom, 0px));
|
|
2272
|
+
z-index: var(--gl-tab-bar-z-index, 900);
|
|
2273
|
+
}
|
|
2274
|
+
.glass-tab-bar.glass-tab-bar--vertical.glass-tab-bar--start { inset-inline-start: 0; inset-inline-end: auto; }
|
|
2275
|
+
.glass-tab-bar.glass-tab-bar--vertical.glass-tab-bar--end {
|
|
2276
|
+
inset-inline-end: 0; inset-inline-start: auto;
|
|
2277
|
+
border-inline-end: none; border-inline-start: 1px solid var(--gl-border-subtle);
|
|
2278
|
+
}
|
|
2279
|
+
.glass-tab-bar.glass-tab-bar--vertical:is(.glass-tab-bar--start:dir(ltr), .glass-tab-bar--end:dir(rtl)) {
|
|
2280
|
+
padding-left: max(var(--gl-space-sm), env(safe-area-inset-left, 0px));
|
|
2281
|
+
}
|
|
2282
|
+
.glass-tab-bar.glass-tab-bar--vertical:is(.glass-tab-bar--end:dir(ltr), .glass-tab-bar--start:dir(rtl)) {
|
|
2283
|
+
padding-right: max(var(--gl-space-sm), env(safe-area-inset-right, 0px));
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2286
|
+
/* ---- Rail ---- */
|
|
2287
|
+
.glass-tab-bar.glass-tab-bar--vertical.glass-tab-bar--rail {
|
|
2288
|
+
width: var(--gl-tab-bar-rail-width, 72px);
|
|
2289
|
+
align-items: center;
|
|
2290
|
+
padding-inline: var(--gl-space-xs);
|
|
2291
|
+
}
|
|
2292
|
+
.glass-tab-bar--rail .glass-tab-bar__item {
|
|
2293
|
+
justify-content: center;
|
|
2294
|
+
width: auto; min-width: 48px; min-height: 48px;
|
|
2295
|
+
padding: var(--gl-space-sm);
|
|
2296
|
+
}
|
|
2297
|
+
.glass-tab-bar--rail .glass-tab-bar__label {
|
|
2298
|
+
position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
|
|
2299
|
+
overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
|
|
2300
|
+
}
|
|
2301
|
+
|
|
2302
|
+
/* ---- Floating, upright ---- */
|
|
2303
|
+
.glass-tab-bar.glass-tab-bar--floating.glass-tab-bar--vertical {
|
|
2304
|
+
flex-direction: column; align-items: center;
|
|
2305
|
+
width: auto; height: auto; max-height: 100%;
|
|
2306
|
+
padding: 10px 6px;
|
|
2307
|
+
border: 1px solid var(--gl-border-subtle);
|
|
2308
|
+
border-radius: var(--gl-tab-bar-floating-radius);
|
|
2309
|
+
}
|
|
2310
|
+
.glass-tab-bar--floating.glass-tab-bar--vertical .glass-tab-bar__item {
|
|
2311
|
+
flex-direction: column; gap: var(--gl-space-2xs);
|
|
2312
|
+
width: auto; min-height: 0;
|
|
2313
|
+
padding: var(--gl-space-2xs) var(--gl-space-sm);
|
|
2314
|
+
}
|
|
2315
|
+
.glass-tab-bar--floating.glass-tab-bar--vertical .glass-tab-bar__label {
|
|
2316
|
+
flex: none; font-size: var(--gl-tab-bar-label-size, 10px);
|
|
2317
|
+
}
|
|
2318
|
+
.glass-tab-bar--floating.glass-tab-bar--vertical .glass-tab-bar__item.is-active { background: var(--gl-tab-bar-active-bg, none); }
|
|
2319
|
+
|
|
2320
|
+
/* ---- Rail with labels (since 1.33.0) ---- */
|
|
2321
|
+
/* --labels with --rail: the label under the icon, as on the floating bar —
|
|
2322
|
+
a desktop rail that names its items. The items fill the rail's width
|
|
2323
|
+
(--gl-tab-bar-rail-width) and are at least --gl-tab-bar-rail-item-height
|
|
2324
|
+
(62px) high; --gl-tab-bar-item-radius rounds them and the drop, unset
|
|
2325
|
+
16px, the corners of a button. */
|
|
2326
|
+
/* A floating rail is a pane, not a pill, docked or in the flow: as high as
|
|
2327
|
+
its container, with the card's corners (--gl-tab-bar-rail-radius) and
|
|
2328
|
+
room around its items. */
|
|
2329
|
+
.glass-tab-bar.glass-tab-bar--floating.glass-tab-bar--vertical.glass-tab-bar--rail {
|
|
2330
|
+
width: var(--gl-tab-bar-rail-width, 72px); height: 100%;
|
|
2331
|
+
padding: var(--gl-space-sm) var(--gl-space-xs);
|
|
2332
|
+
border-radius: var(--gl-tab-bar-rail-radius, var(--gl-radius-card));
|
|
2333
|
+
}
|
|
2334
|
+
.glass-tab-bar.glass-tab-bar--vertical.glass-tab-bar--rail.glass-tab-bar--labels { align-items: stretch; }
|
|
2335
|
+
.glass-tab-bar--rail.glass-tab-bar--labels .glass-tab-bar__item {
|
|
2336
|
+
flex-direction: column; justify-content: center;
|
|
2337
|
+
gap: var(--gl-space-2xs);
|
|
2338
|
+
width: 100%; min-height: var(--gl-tab-bar-rail-item-height, 62px);
|
|
2339
|
+
padding: var(--gl-space-xs) var(--gl-space-2xs);
|
|
2340
|
+
border-radius: var(--gl-tab-bar-item-radius, var(--gl-radius-btn));
|
|
2341
|
+
}
|
|
2342
|
+
.glass-tab-bar--rail.glass-tab-bar--labels .glass-tab-bar__label {
|
|
2343
|
+
position: static; width: auto; height: auto; margin: 0; padding: 0; clip-path: none;
|
|
2344
|
+
flex: none; max-width: 100%;
|
|
2345
|
+
font-size: var(--gl-tab-bar-label-size, 10px); text-align: center;
|
|
2346
|
+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
2347
|
+
}
|
|
2348
|
+
.glass-tab-bar.glass-tab-bar--rail.glass-tab-bar--labels.glass-tab-bar--droplet[data-droplet]::before {
|
|
2349
|
+
border-radius: var(--gl-tab-bar-item-radius, var(--gl-radius-btn));
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
/* ---- Floating rail at an edge (since 1.33.0) ---- */
|
|
2353
|
+
/* --floating with --vertical and --start or --end: a rail of its own
|
|
2354
|
+
glass, as high as the viewport less --gl-tab-bar-floating-inset (16px)
|
|
2355
|
+
at both ends and that much from its edge, clear of the safe areas, with
|
|
2356
|
+
the corners of a card (--gl-tab-bar-rail-radius). */
|
|
2357
|
+
.glass-tab-bar.glass-tab-bar--floating.glass-tab-bar--vertical:is(.glass-tab-bar--start, .glass-tab-bar--end) {
|
|
2358
|
+
top: calc(var(--gl-tab-bar-floating-inset, 16px) + env(safe-area-inset-top, 0px));
|
|
2359
|
+
bottom: calc(var(--gl-tab-bar-floating-inset, 16px) + env(safe-area-inset-bottom, 0px));
|
|
2360
|
+
height: auto; max-height: none;
|
|
2361
|
+
padding: var(--gl-space-sm) var(--gl-space-xs);
|
|
2362
|
+
border: 1px solid var(--gl-border-subtle);
|
|
2363
|
+
border-radius: var(--gl-tab-bar-rail-radius, var(--gl-radius-card));
|
|
2364
|
+
overflow-y: auto;
|
|
2365
|
+
}
|
|
2366
|
+
.glass-tab-bar.glass-tab-bar--floating.glass-tab-bar--vertical.glass-tab-bar--start {
|
|
2367
|
+
inset-inline-start: calc(var(--gl-tab-bar-floating-inset, 16px) + env(safe-area-inset-left, 0px));
|
|
2368
|
+
}
|
|
2369
|
+
.glass-tab-bar.glass-tab-bar--floating.glass-tab-bar--vertical.glass-tab-bar--end {
|
|
2370
|
+
inset-inline-end: calc(var(--gl-tab-bar-floating-inset, 16px) + env(safe-area-inset-right, 0px));
|
|
2371
|
+
}
|
|
2372
|
+
.glass-tab-bar.glass-tab-bar--floating.glass-tab-bar--vertical.glass-tab-bar--start:dir(rtl) {
|
|
2373
|
+
inset-inline-start: calc(var(--gl-tab-bar-floating-inset, 16px) + env(safe-area-inset-right, 0px));
|
|
2374
|
+
}
|
|
2375
|
+
.glass-tab-bar.glass-tab-bar--floating.glass-tab-bar--vertical.glass-tab-bar--end:dir(rtl) {
|
|
2376
|
+
inset-inline-end: calc(var(--gl-tab-bar-floating-inset, 16px) + env(safe-area-inset-left, 0px));
|
|
2377
|
+
}
|
|
2378
|
+
|
|
2379
|
+
/* ---- A place before and after the items (since 1.33.0) ---- */
|
|
2380
|
+
/* __leading holds a mark or the accessory, __trailing a foot such as the
|
|
2381
|
+
account: above and below the items in a column, the trailing part at
|
|
2382
|
+
the far end; before and after them in a row. */
|
|
2383
|
+
.glass-tab-bar__leading,
|
|
2384
|
+
.glass-tab-bar__trailing {
|
|
2385
|
+
display: flex; align-items: center; justify-content: center;
|
|
2386
|
+
gap: var(--gl-space-sm);
|
|
2387
|
+
flex: none;
|
|
2388
|
+
}
|
|
2389
|
+
.glass-tab-bar__leading[hidden],
|
|
2390
|
+
.glass-tab-bar__trailing[hidden] { display: none; }
|
|
2391
|
+
.glass-tab-bar--vertical .glass-tab-bar__leading,
|
|
2392
|
+
.glass-tab-bar--vertical .glass-tab-bar__trailing {
|
|
2393
|
+
flex-direction: column;
|
|
2394
|
+
width: 100%;
|
|
2395
|
+
padding-block: var(--gl-space-2xs);
|
|
2396
|
+
}
|
|
2397
|
+
.glass-tab-bar--vertical .glass-tab-bar__leading { margin-block-end: var(--gl-space-sm); }
|
|
2398
|
+
.glass-tab-bar--vertical .glass-tab-bar__trailing { margin-block-start: auto; padding-block-start: var(--gl-space-sm); }
|
|
2399
|
+
|
|
2400
|
+
/* The accessory with its name under it (since 1.33.0), for a rail's
|
|
2401
|
+
leading place; the name is the button's too. */
|
|
2402
|
+
.glass-tab-bar__accessory-item {
|
|
2403
|
+
display: flex; flex-direction: column; align-items: center;
|
|
2404
|
+
gap: var(--gl-space-2xs);
|
|
2405
|
+
}
|
|
2406
|
+
.glass-tab-bar__accessory-label {
|
|
2407
|
+
font-size: var(--gl-tab-bar-label-size, 10px); font-weight: var(--gl-font-weight-medium);
|
|
2408
|
+
letter-spacing: 0.2px; text-align: center;
|
|
2409
|
+
color: var(--gl-tab-bar-color, var(--gl-icon-muted));
|
|
2410
|
+
cursor: pointer;
|
|
2411
|
+
}
|
|
2412
|
+
|
|
2413
|
+
.glass-tab-bar-dock.glass-tab-bar-dock--vertical {
|
|
2414
|
+
flex-direction: column;
|
|
2415
|
+
top: 50%; bottom: auto;
|
|
2416
|
+
transform: translateY(-50%);
|
|
2417
|
+
max-width: none; max-height: calc(100dvh - 2 * var(--gl-space-md));
|
|
2418
|
+
/* The edge by the writing direction, for browsers without :dir(); the
|
|
2419
|
+
rules below add the safe area of the physical side. */
|
|
2420
|
+
left: auto; right: auto;
|
|
2421
|
+
inset-inline-end: calc(var(--gl-tab-bar-floating-bottom) + env(safe-area-inset-right, 0px));
|
|
2422
|
+
}
|
|
2423
|
+
.glass-tab-bar-dock.glass-tab-bar-dock--vertical.glass-tab-bar-dock--start {
|
|
2424
|
+
inset-inline-end: auto;
|
|
2425
|
+
inset-inline-start: calc(var(--gl-tab-bar-floating-bottom) + env(safe-area-inset-left, 0px));
|
|
2426
|
+
}
|
|
2427
|
+
.glass-tab-bar-dock.glass-tab-bar-dock--vertical.glass-tab-bar-dock--accessory-left { flex-direction: column-reverse; }
|
|
2428
|
+
/* Unset or --end, at the end edge; --start at the other. */
|
|
2429
|
+
.glass-tab-bar-dock.glass-tab-bar-dock--vertical:is(:not(.glass-tab-bar-dock--start):dir(ltr), .glass-tab-bar-dock--start:dir(rtl)) {
|
|
2430
|
+
left: auto; right: calc(var(--gl-tab-bar-floating-bottom) + env(safe-area-inset-right, 0px));
|
|
2431
|
+
}
|
|
2432
|
+
.glass-tab-bar-dock.glass-tab-bar-dock--vertical:is(.glass-tab-bar-dock--start:dir(ltr), :not(.glass-tab-bar-dock--start):dir(rtl)) {
|
|
2433
|
+
right: auto; left: calc(var(--gl-tab-bar-floating-bottom) + env(safe-area-inset-left, 0px));
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2436
|
+
/* ---- Top bar ---- */
|
|
2437
|
+
.glass-tab-bar.glass-tab-bar--top {
|
|
2438
|
+
position: relative; inset: auto;
|
|
2439
|
+
height: auto;
|
|
2440
|
+
align-items: center;
|
|
2441
|
+
padding-bottom: var(--gl-space-xs);
|
|
2442
|
+
border-top: none; border-bottom: 1px solid var(--gl-border-subtle);
|
|
2443
|
+
box-shadow: 0 4px 20px rgba(0,0,0,0.08), var(--gl-inset-subtle);
|
|
2444
|
+
}
|
|
2445
|
+
@media (min-width: 640px) {
|
|
2446
|
+
.glass-tab-bar--top .glass-tab-bar__item {
|
|
2447
|
+
flex-direction: row; gap: var(--gl-space-xs);
|
|
2448
|
+
padding-inline: var(--gl-space-md);
|
|
2449
|
+
}
|
|
2450
|
+
.glass-tab-bar--top .glass-tab-bar__label { font-size: var(--gl-font-size-sm); }
|
|
2451
|
+
/* Clear of the badge at the icon's corner. */
|
|
2452
|
+
.glass-tab-bar--top .glass-tab-bar__item:has(.glass-tab-bar__badge:not([hidden])) .glass-tab-bar__label { margin-inline-start: 8px; }
|
|
2453
|
+
}
|
|
2454
|
+
|
|
2455
|
+
|
|
2117
2456
|
/* ============================================================
|
|
2118
2457
|
19. ACCORDION
|
|
2119
2458
|
============================================================ */
|
|
@@ -2310,6 +2649,23 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
|
|
|
2310
2649
|
inset 0 0 0 100px color-mix(in srgb, var(--gl-badge-accent) 22%, transparent);
|
|
2311
2650
|
}
|
|
2312
2651
|
|
|
2652
|
+
/* A chip to choose with (since 1.33.0): --gl-badge-md-height (36px) high,
|
|
2653
|
+
in the small text of a form. Interactive, it takes taps on 44px above
|
|
2654
|
+
and below its edges, the target every control keeps. */
|
|
2655
|
+
.glass-badge--md {
|
|
2656
|
+
box-sizing: border-box;
|
|
2657
|
+
min-height: var(--gl-badge-md-height, 36px);
|
|
2658
|
+
padding: 0 var(--gl-space-md);
|
|
2659
|
+
justify-content: center;
|
|
2660
|
+
font-size: var(--gl-font-size-sm);
|
|
2661
|
+
}
|
|
2662
|
+
.glass-badge--interactive.glass-badge--md { position: relative; }
|
|
2663
|
+
.glass-badge--interactive.glass-badge--md::after {
|
|
2664
|
+
content: ''; position: absolute;
|
|
2665
|
+
/* From the padding box, so the 1px border counts as well. */
|
|
2666
|
+
inset: min(0px, calc((var(--gl-badge-md-height, 36px) - 44px) / 2 - 1px)) 0;
|
|
2667
|
+
}
|
|
2668
|
+
|
|
2313
2669
|
|
|
2314
2670
|
/* ============================================================
|
|
2315
2671
|
21. DIVIDER
|
|
@@ -2440,6 +2796,171 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
|
|
|
2440
2796
|
.glass-popover--end:dir(rtl) { transform-origin: top left; }
|
|
2441
2797
|
.glass-popover--end.is-open { transform: translateY(0) scale(1); }
|
|
2442
2798
|
|
|
2799
|
+
/* In the top layer (since 1.32.0): with the popover attribute the panel is
|
|
2800
|
+
shown with showPopover() and placed by script, fixed at the coordinates
|
|
2801
|
+
it writes. It is no longer cut off by a box with overflow: hidden around
|
|
2802
|
+
it, does not leave the screen at its edges, and closed it takes no room.
|
|
2803
|
+
GlassKit Elements' <glk-popover> does this. */
|
|
2804
|
+
/* It fades out with transitions that keep it in the top layer until they
|
|
2805
|
+
end (transition-behavior: allow-discrete), and in with an animation —
|
|
2806
|
+
written in longhands and keyframes, which the minifier keeps. */
|
|
2807
|
+
.glass-popover[popover] {
|
|
2808
|
+
position: fixed; inset: auto; margin: 0;
|
|
2809
|
+
transform: translateY(-4px) scale(0.96);
|
|
2810
|
+
transition-property: opacity, transform, overlay, display;
|
|
2811
|
+
transition-duration: .18s;
|
|
2812
|
+
transition-timing-function: ease;
|
|
2813
|
+
transition-behavior: allow-discrete;
|
|
2814
|
+
}
|
|
2815
|
+
.glass-popover[popover]:popover-open {
|
|
2816
|
+
opacity: 1; visibility: visible; pointer-events: auto;
|
|
2817
|
+
transform: none;
|
|
2818
|
+
animation: glass-popover-in .22s cubic-bezier(.2,.8,.2,1);
|
|
2819
|
+
}
|
|
2820
|
+
@keyframes glass-popover-in { from { opacity: 0; transform: translateY(-4px) scale(0.96); } }
|
|
2821
|
+
.glass-popover[popover][data-side="top"] { transform-origin: bottom center; }
|
|
2822
|
+
|
|
2823
|
+
|
|
2824
|
+
/* ============================================================
|
|
2825
|
+
24b. TOOLTIP
|
|
2826
|
+
============================================================ */
|
|
2827
|
+
/* A short name or hint for a control on hover and focus (since 1.32.0):
|
|
2828
|
+
an icon-only button, the items of a rail, a row action. Small text on an
|
|
2829
|
+
opaque surface of the theme, readable over any glass, at most
|
|
2830
|
+
--gl-tooltip-max-width (240px) wide; --gl-tooltip-bg and
|
|
2831
|
+
--gl-tooltip-color recolour it. role="tooltip"; the control points to it
|
|
2832
|
+
with aria-describedby, or aria-labelledby where it is the control's only
|
|
2833
|
+
name. Opened in the top layer (popover="hint") and placed by script next
|
|
2834
|
+
to its control, flipped and shifted to stay in the viewport; the pointer
|
|
2835
|
+
can move onto it without it closing (WCAG 1.4.13). GlassKit Elements'
|
|
2836
|
+
<glk-tooltip> does all of this. */
|
|
2837
|
+
.glass-tooltip {
|
|
2838
|
+
max-width: var(--gl-tooltip-max-width, 240px);
|
|
2839
|
+
padding: 6px 12px;
|
|
2840
|
+
border: 1px solid var(--gl-border-medium);
|
|
2841
|
+
border-radius: var(--gl-radius-sm);
|
|
2842
|
+
/* The popover's glass, tinted far enough to keep the text readable over
|
|
2843
|
+
whatever lies below it. */
|
|
2844
|
+
background: var(--gl-tooltip-bg, color-mix(in srgb, var(--gl-color-bg-mid) 80%, transparent));
|
|
2845
|
+
backdrop-filter: blur(var(--gl-blur));
|
|
2846
|
+
-webkit-backdrop-filter: blur(var(--gl-blur));
|
|
2847
|
+
box-shadow: var(--gl-shadow-card), var(--gl-inset-strong);
|
|
2848
|
+
color: var(--gl-tooltip-color, var(--gl-color-text));
|
|
2849
|
+
font-family: var(--gl-font-family);
|
|
2850
|
+
font-size: var(--gl-font-size-xs); font-weight: var(--gl-font-weight-medium);
|
|
2851
|
+
line-height: 1.4;
|
|
2852
|
+
overflow-wrap: anywhere;
|
|
2853
|
+
}
|
|
2854
|
+
.glass-tooltip[popover] {
|
|
2855
|
+
inset: auto; margin: 0; overflow: visible;
|
|
2856
|
+
opacity: 0;
|
|
2857
|
+
transition-property: opacity, overlay, display;
|
|
2858
|
+
transition-duration: .12s;
|
|
2859
|
+
transition-behavior: allow-discrete;
|
|
2860
|
+
}
|
|
2861
|
+
.glass-tooltip[popover]:popover-open { opacity: 1; animation: glass-fade-in .12s ease; }
|
|
2862
|
+
@keyframes glass-fade-in { from { opacity: 0; } }
|
|
2863
|
+
|
|
2864
|
+
|
|
2865
|
+
/* ============================================================
|
|
2866
|
+
24c. MENU
|
|
2867
|
+
============================================================ */
|
|
2868
|
+
/* A list of actions behind a button (since 1.32.0): the overflow menu of a
|
|
2869
|
+
header, the actions of a table row. On the popover's surface, in the top
|
|
2870
|
+
layer and placed like the tooltip. Items are buttons with
|
|
2871
|
+
role="menuitem" — or menuitemcheckbox and menuitemradio with aria-checked
|
|
2872
|
+
(or .is-checked), which shows the check mark — with an icon before the
|
|
2873
|
+
label and a hint at the end, such as a shortcut. --danger takes the error
|
|
2874
|
+
colour for something destructive, aria-disabled="true" leaves an item
|
|
2875
|
+
announced but inert; __separator and __group-label part the list. The
|
|
2876
|
+
keyboard — arrows, Home and End, typing to jump, Escape back to the
|
|
2877
|
+
button — is GlassKit Elements' <glk-menu>. */
|
|
2878
|
+
.glass-menu {
|
|
2879
|
+
min-width: 180px; max-width: min(320px, calc(100vw - 16px));
|
|
2880
|
+
max-height: min(70vh, 480px);
|
|
2881
|
+
overflow-y: auto; overscroll-behavior: contain;
|
|
2882
|
+
padding: 6px;
|
|
2883
|
+
background: var(--gl-surface-3);
|
|
2884
|
+
backdrop-filter: blur(var(--gl-blur));
|
|
2885
|
+
-webkit-backdrop-filter: blur(var(--gl-blur));
|
|
2886
|
+
border: 1px solid var(--gl-border-medium);
|
|
2887
|
+
border-radius: var(--gl-radius-card);
|
|
2888
|
+
box-shadow: var(--gl-shadow-card), var(--gl-inset-strong);
|
|
2889
|
+
color: var(--gl-color-text);
|
|
2890
|
+
font-family: var(--gl-font-family);
|
|
2891
|
+
}
|
|
2892
|
+
.glass-menu[popover] {
|
|
2893
|
+
inset: auto; margin: 0;
|
|
2894
|
+
opacity: 0; transform: scale(0.97);
|
|
2895
|
+
transition-property: opacity, transform, overlay, display;
|
|
2896
|
+
transition-duration: .14s;
|
|
2897
|
+
transition-behavior: allow-discrete;
|
|
2898
|
+
}
|
|
2899
|
+
.glass-menu[popover]:popover-open { opacity: 1; transform: none; animation: glass-menu-in .14s ease; }
|
|
2900
|
+
@keyframes glass-menu-in { from { opacity: 0; transform: scale(0.97); } }
|
|
2901
|
+
.glass-menu__item {
|
|
2902
|
+
display: flex; align-items: center; gap: 10px;
|
|
2903
|
+
width: 100%; min-height: 40px;
|
|
2904
|
+
padding: 8px 12px;
|
|
2905
|
+
/* Concentric with the panel's corners. */
|
|
2906
|
+
border: 0; border-radius: calc(var(--gl-radius-card) - 6px);
|
|
2907
|
+
background: none;
|
|
2908
|
+
color: var(--gl-color-text);
|
|
2909
|
+
font: inherit; font-size: var(--gl-font-size-sm); text-align: start;
|
|
2910
|
+
cursor: pointer; outline: none; user-select: none;
|
|
2911
|
+
-webkit-tap-highlight-color: transparent;
|
|
2912
|
+
}
|
|
2913
|
+
.glass-menu__item:hover,
|
|
2914
|
+
.glass-menu__item:focus,
|
|
2915
|
+
.glass-menu__item.is-focused { background: var(--gl-surface-2); }
|
|
2916
|
+
.glass-menu__item:focus-visible,
|
|
2917
|
+
.glass-menu__item.is-focus-visible { box-shadow: inset 0 0 0 2px var(--gl-border-focus); }
|
|
2918
|
+
.glass-menu__item--danger { color: var(--gl-color-error-on-surface, var(--gl-color-error)); }
|
|
2919
|
+
.glass-menu__item[aria-disabled="true"],
|
|
2920
|
+
.glass-menu__item.is-disabled { opacity: 0.45; cursor: not-allowed; }
|
|
2921
|
+
.glass-menu__icon {
|
|
2922
|
+
flex: none; width: 18px; height: 18px;
|
|
2923
|
+
display: flex; align-items: center; justify-content: center;
|
|
2924
|
+
color: var(--gl-icon-default);
|
|
2925
|
+
}
|
|
2926
|
+
.glass-menu__item--danger .glass-menu__icon { color: inherit; }
|
|
2927
|
+
.glass-menu__icon:empty, .glass-menu__icon[hidden] { display: none; }
|
|
2928
|
+
.glass-menu__icon svg,
|
|
2929
|
+
.glass-menu__icon ::slotted(svg) {
|
|
2930
|
+
width: 18px; height: 18px; fill: none;
|
|
2931
|
+
stroke: currentColor; stroke-width: 1.8;
|
|
2932
|
+
stroke-linecap: round; stroke-linejoin: round;
|
|
2933
|
+
}
|
|
2934
|
+
.glass-menu__check {
|
|
2935
|
+
flex: none; width: 16px; height: 16px;
|
|
2936
|
+
color: var(--gl-color-primary-text);
|
|
2937
|
+
visibility: hidden;
|
|
2938
|
+
}
|
|
2939
|
+
.glass-menu__check svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
|
|
2940
|
+
.glass-menu__item:is([aria-checked="true"], .is-checked) .glass-menu__check { visibility: visible; }
|
|
2941
|
+
.glass-menu__label {
|
|
2942
|
+
flex: 1 1 auto; min-width: 0;
|
|
2943
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
2944
|
+
}
|
|
2945
|
+
.glass-menu__trailing {
|
|
2946
|
+
flex: none; margin-inline-start: auto; padding-inline-start: var(--gl-space-sm);
|
|
2947
|
+
color: var(--gl-color-text-muted); font-size: var(--gl-font-size-xs);
|
|
2948
|
+
}
|
|
2949
|
+
.glass-menu__separator {
|
|
2950
|
+
height: 1px; margin: var(--gl-space-2xs) var(--gl-space-xs);
|
|
2951
|
+
border: 0; background: var(--gl-border-subtle);
|
|
2952
|
+
}
|
|
2953
|
+
.glass-menu__group-label {
|
|
2954
|
+
padding: 6px 12px 2px;
|
|
2955
|
+
color: var(--gl-color-text-muted);
|
|
2956
|
+
font-size: var(--gl-font-size-xs); font-weight: var(--gl-font-weight-semibold);
|
|
2957
|
+
}
|
|
2958
|
+
@media (prefers-reduced-motion: reduce) {
|
|
2959
|
+
.glass-popover[popover], .glass-popover[popover]:popover-open,
|
|
2960
|
+
.glass-tooltip[popover], .glass-tooltip[popover]:popover-open,
|
|
2961
|
+
.glass-menu[popover], .glass-menu[popover]:popover-open { transition: none; animation: none; }
|
|
2962
|
+
}
|
|
2963
|
+
|
|
2443
2964
|
|
|
2444
2965
|
/* ============================================================
|
|
2445
2966
|
25. LIST
|
|
@@ -2558,6 +3079,9 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
|
|
|
2558
3079
|
background: var(--gl-surface-2);
|
|
2559
3080
|
outline: none;
|
|
2560
3081
|
}
|
|
3082
|
+
/* A ring for the keyboard (since 1.32.0), inside the row: the list clips
|
|
3083
|
+
what lies outside it. The tint alone was too faint to find the focus. */
|
|
3084
|
+
.glass-list__item--interactive:focus-visible { box-shadow: inset 0 0 0 2px var(--gl-border-focus); }
|
|
2561
3085
|
.glass-list__item--interactive:active {
|
|
2562
3086
|
background: var(--gl-surface-3);
|
|
2563
3087
|
}
|
|
@@ -2689,6 +3213,249 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
|
|
|
2689
3213
|
}
|
|
2690
3214
|
|
|
2691
3215
|
|
|
3216
|
+
/* ============================================================
|
|
3217
|
+
25b. TREE
|
|
3218
|
+
============================================================ */
|
|
3219
|
+
/* Levels that open and close (since 1.32.0): the structure of an app, menus
|
|
3220
|
+
with submenus. A row per item — a twisty that turns when the item opens,
|
|
3221
|
+
an icon, the label, something at the end (a badge, a status, an action) —
|
|
3222
|
+
indented by --gl-tree-indent (20px) per level, with the item's children
|
|
3223
|
+
in a __group below it. role="tree" on the container, "treeitem" on each
|
|
3224
|
+
item with aria-level, aria-expanded on the ones with children and
|
|
3225
|
+
aria-selected; one tab stop and the arrow keys of the WAI-ARIA tree view,
|
|
3226
|
+
which GlassKit Elements' <glk-tree> brings. No surface of its own: put it
|
|
3227
|
+
in a card or a sidebar. */
|
|
3228
|
+
.glass-tree {
|
|
3229
|
+
display: flex; flex-direction: column; gap: 1px;
|
|
3230
|
+
margin: 0; padding: 0; list-style: none;
|
|
3231
|
+
font-family: var(--gl-font-family);
|
|
3232
|
+
font-size: var(--gl-font-size-sm);
|
|
3233
|
+
color: var(--gl-color-text);
|
|
3234
|
+
}
|
|
3235
|
+
.glass-tree__row {
|
|
3236
|
+
position: relative;
|
|
3237
|
+
display: flex; align-items: center; gap: var(--gl-space-xs);
|
|
3238
|
+
min-height: var(--gl-tree-row-height, 36px);
|
|
3239
|
+
padding-block: 4px;
|
|
3240
|
+
padding-inline: calc(var(--gl-space-xs) + (var(--gl-tree-level, 1) - 1) * var(--gl-tree-indent, 20px)) var(--gl-space-sm);
|
|
3241
|
+
border-radius: var(--gl-radius-sm);
|
|
3242
|
+
cursor: pointer; user-select: none;
|
|
3243
|
+
transition: background .15s ease;
|
|
3244
|
+
}
|
|
3245
|
+
.glass-tree__row:hover { background: var(--gl-surface-1); }
|
|
3246
|
+
/* The chosen row stands on raised glass, as the active row of a sidebar
|
|
3247
|
+
does, its icon in the accent. */
|
|
3248
|
+
.glass-tree__row.is-selected {
|
|
3249
|
+
background: var(--gl-tree-selected-bg, var(--gl-surface-4));
|
|
3250
|
+
box-shadow: var(--gl-inset-strong), var(--gl-shadow-btn);
|
|
3251
|
+
color: var(--gl-color-text-heading);
|
|
3252
|
+
font-weight: var(--gl-font-weight-semibold);
|
|
3253
|
+
}
|
|
3254
|
+
.glass-tree__row.is-selected .glass-tree__icon { color: var(--gl-color-primary-text); }
|
|
3255
|
+
.glass-tree__row.is-focus-visible,
|
|
3256
|
+
.glass-tree__row:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--gl-border-focus); }
|
|
3257
|
+
/* In plain markup the treeitem takes the focus, its row shows it. */
|
|
3258
|
+
.glass-tree [role="treeitem"]:focus-visible { outline: none; }
|
|
3259
|
+
.glass-tree [role="treeitem"]:focus-visible > .glass-tree__row { box-shadow: inset 0 0 0 2px var(--gl-border-focus); }
|
|
3260
|
+
.glass-tree__row.is-disabled { opacity: 0.45; cursor: not-allowed; }
|
|
3261
|
+
.glass-tree__twisty {
|
|
3262
|
+
flex: none; width: 20px; height: 20px;
|
|
3263
|
+
display: flex; align-items: center; justify-content: center;
|
|
3264
|
+
color: var(--gl-icon-default);
|
|
3265
|
+
border-radius: var(--gl-radius-xs);
|
|
3266
|
+
transition: transform .15s ease;
|
|
3267
|
+
}
|
|
3268
|
+
.glass-tree__twisty svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
|
|
3269
|
+
.glass-tree__twisty:dir(rtl) { transform: scaleX(-1); }
|
|
3270
|
+
.glass-tree__row.is-expanded .glass-tree__twisty { transform: rotate(90deg); }
|
|
3271
|
+
.glass-tree__row.is-leaf .glass-tree__twisty { visibility: hidden; }
|
|
3272
|
+
/* Children on their way: the twisty turns into a small spinner. */
|
|
3273
|
+
.glass-tree__row.is-loading .glass-tree__twisty svg { display: none; }
|
|
3274
|
+
.glass-tree__row.is-loading .glass-tree__twisty::after {
|
|
3275
|
+
content: ''; width: 12px; height: 12px;
|
|
3276
|
+
border: 2px solid var(--gl-border-medium); border-top-color: var(--gl-color-primary);
|
|
3277
|
+
border-radius: 50%;
|
|
3278
|
+
animation: glass-tree-spin .8s linear infinite;
|
|
3279
|
+
}
|
|
3280
|
+
@keyframes glass-tree-spin { to { transform: rotate(360deg); } }
|
|
3281
|
+
.glass-tree__icon {
|
|
3282
|
+
flex: none; width: 18px; height: 18px;
|
|
3283
|
+
display: flex; align-items: center; justify-content: center;
|
|
3284
|
+
color: var(--gl-icon-default);
|
|
3285
|
+
}
|
|
3286
|
+
.glass-tree__icon:empty, .glass-tree__icon[hidden] { display: none; }
|
|
3287
|
+
.glass-tree__icon svg,
|
|
3288
|
+
.glass-tree__icon ::slotted(svg) {
|
|
3289
|
+
width: 18px; height: 18px; fill: none;
|
|
3290
|
+
stroke: currentColor; stroke-width: 1.8;
|
|
3291
|
+
stroke-linecap: round; stroke-linejoin: round;
|
|
3292
|
+
}
|
|
3293
|
+
.glass-tree__label {
|
|
3294
|
+
flex: 1 1 auto; min-width: 0;
|
|
3295
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
3296
|
+
}
|
|
3297
|
+
.glass-tree__trailing {
|
|
3298
|
+
flex: none; display: flex; align-items: center; gap: var(--gl-space-2xs);
|
|
3299
|
+
margin-inline-start: auto;
|
|
3300
|
+
color: var(--gl-color-text-muted); font-size: var(--gl-font-size-xs);
|
|
3301
|
+
}
|
|
3302
|
+
.glass-tree__trailing[hidden] { display: none; }
|
|
3303
|
+
.glass-tree__group { display: flex; flex-direction: column; gap: 1px; margin: 0; padding: 0; list-style: none; }
|
|
3304
|
+
.glass-tree__group[hidden] { display: none; }
|
|
3305
|
+
/* Children that could not load: the twisty turns into a mark in the error
|
|
3306
|
+
colour, and opening the item again tries once more. */
|
|
3307
|
+
.glass-tree__row.is-error .glass-tree__twisty { color: var(--gl-color-error-on-surface, var(--gl-color-error)); }
|
|
3308
|
+
.glass-tree__row.is-error .glass-tree__twisty svg { display: none; }
|
|
3309
|
+
.glass-tree__row.is-error .glass-tree__twisty::after {
|
|
3310
|
+
content: '!';
|
|
3311
|
+
display: flex; align-items: center; justify-content: center;
|
|
3312
|
+
width: 14px; height: 14px; box-sizing: border-box;
|
|
3313
|
+
border: 1.5px solid currentColor; border-radius: 50%;
|
|
3314
|
+
font-size: 10px; font-weight: var(--gl-font-weight-semibold); line-height: 1;
|
|
3315
|
+
}
|
|
3316
|
+
|
|
3317
|
+
/* A short flash on an item that changed elsewhere — on another device, by
|
|
3318
|
+
an undo — so the eye finds it among the others (since 1.32.0), on a tree
|
|
3319
|
+
row or a list item; --gl-flash-color tints it. */
|
|
3320
|
+
.glass-tree__row.is-flash,
|
|
3321
|
+
.glass-list__item.is-flash { animation: glass-flash 1.2s ease-out; }
|
|
3322
|
+
@keyframes glass-flash { from { background-color: var(--gl-flash-color, color-mix(in srgb, var(--gl-color-primary) 30%, transparent)); } }
|
|
3323
|
+
@media (prefers-reduced-motion: reduce) {
|
|
3324
|
+
.glass-tree__twisty, .glass-tree__row { transition: none; }
|
|
3325
|
+
.glass-tree__row.is-loading .glass-tree__twisty::after { animation-duration: 2.4s; }
|
|
3326
|
+
.glass-tree__row.is-flash, .glass-list__item.is-flash { animation: none; }
|
|
3327
|
+
}
|
|
3328
|
+
|
|
3329
|
+
|
|
3330
|
+
/* ============================================================
|
|
3331
|
+
25c. BREADCRUMBS
|
|
3332
|
+
============================================================ */
|
|
3333
|
+
/* The path through nested levels (since 1.32.0): My app › Main menu ›
|
|
3334
|
+
Services › Online booking. A <nav aria-label="Breadcrumb"> around an
|
|
3335
|
+
<ol>; each crumb a link (or a button), the current level plain text with
|
|
3336
|
+
aria-current="page". The separators are drawn by CSS, hidden from
|
|
3337
|
+
assistive technology and mirrored right to left. A crumb longer than
|
|
3338
|
+
--gl-breadcrumbs-crumb-width (200px) is cut with an ellipsis. Where the
|
|
3339
|
+
path does not fit, __overflow stands for the middle levels — a button
|
|
3340
|
+
that opens them as a menu; GlassKit Elements' <glk-breadcrumbs> folds
|
|
3341
|
+
them by itself and makes the crumbs drop targets for a sortable list. */
|
|
3342
|
+
.glass-breadcrumbs { min-width: 0; font-family: var(--gl-font-family); }
|
|
3343
|
+
.glass-breadcrumbs__list {
|
|
3344
|
+
display: flex; align-items: center; flex-wrap: nowrap;
|
|
3345
|
+
min-width: 0; margin: 0; padding: 0; list-style: none;
|
|
3346
|
+
font-size: var(--gl-font-size-sm);
|
|
3347
|
+
}
|
|
3348
|
+
.glass-breadcrumbs__item { display: flex; align-items: center; min-width: 0; }
|
|
3349
|
+
.glass-breadcrumbs__item[hidden] { display: none; }
|
|
3350
|
+
.glass-breadcrumbs__item + .glass-breadcrumbs__item::before {
|
|
3351
|
+
content: ''; flex: none;
|
|
3352
|
+
width: 6px; height: 6px; margin-inline: 4px 8px;
|
|
3353
|
+
border-top: 1.5px solid var(--gl-icon-muted);
|
|
3354
|
+
border-right: 1.5px solid var(--gl-icon-muted);
|
|
3355
|
+
transform: rotate(45deg);
|
|
3356
|
+
}
|
|
3357
|
+
.glass-breadcrumbs__item + .glass-breadcrumbs__item:dir(rtl)::before { transform: rotate(-135deg); }
|
|
3358
|
+
.glass-breadcrumbs__crumb,
|
|
3359
|
+
.glass-breadcrumbs__current {
|
|
3360
|
+
display: block; min-width: 0;
|
|
3361
|
+
max-width: var(--gl-breadcrumbs-crumb-width, 200px);
|
|
3362
|
+
padding: 4px 8px;
|
|
3363
|
+
border-radius: var(--gl-radius-full);
|
|
3364
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
3365
|
+
}
|
|
3366
|
+
.glass-breadcrumbs__crumb {
|
|
3367
|
+
border: 0; background: none;
|
|
3368
|
+
color: var(--gl-color-text-muted);
|
|
3369
|
+
font: inherit; text-decoration: none;
|
|
3370
|
+
cursor: pointer;
|
|
3371
|
+
transition: background .15s ease, color .15s ease;
|
|
3372
|
+
}
|
|
3373
|
+
.glass-breadcrumbs__crumb:hover { color: var(--gl-color-text); background: var(--gl-surface-1); }
|
|
3374
|
+
.glass-breadcrumbs__crumb:focus-visible { outline: none; box-shadow: var(--gl-shadow-focus); }
|
|
3375
|
+
.glass-breadcrumbs__current { color: var(--gl-color-text-heading); font-weight: var(--gl-font-weight-semibold); }
|
|
3376
|
+
.glass-breadcrumbs__overflow { min-width: 32px; text-align: center; letter-spacing: 1px; }
|
|
3377
|
+
/* The "…" crumb keeps its width where the path still does not fit. */
|
|
3378
|
+
.glass-breadcrumbs__item--overflow { flex: none; }
|
|
3379
|
+
/* A crumb as the target of a dragged item: it stands for its level. */
|
|
3380
|
+
.glass-breadcrumbs__crumb.is-drop-target,
|
|
3381
|
+
.glass-breadcrumbs__current.is-drop-target {
|
|
3382
|
+
color: var(--gl-color-text);
|
|
3383
|
+
background: color-mix(in srgb, var(--gl-color-primary) 18%, transparent);
|
|
3384
|
+
box-shadow: inset 0 0 0 1.5px var(--gl-border-focus);
|
|
3385
|
+
}
|
|
3386
|
+
.glass-breadcrumbs__crumb.is-drop-refused { box-shadow: inset 0 0 0 1.5px var(--gl-color-error-border); background: var(--gl-color-error-surface); }
|
|
3387
|
+
|
|
3388
|
+
|
|
3389
|
+
/* ============================================================
|
|
3390
|
+
25d. SORTING
|
|
3391
|
+
============================================================ */
|
|
3392
|
+
/* The states of a drag that reorders a list or a tree (since 1.32.0). The
|
|
3393
|
+
dragged item stays where it was, faded (.is-sort-source); a raised copy,
|
|
3394
|
+
__ghost, follows the pointer in the top layer. A line shows where it
|
|
3395
|
+
lands before or after an item — .is-sort-before / .is-sort-after on the
|
|
3396
|
+
target, indented to the level it lands on (--gl-tree-level) — and an
|
|
3397
|
+
item that takes it inside lights up (.is-sort-inside); one that refuses
|
|
3398
|
+
it shows the error colour (.is-sort-refused). The motion of the items
|
|
3399
|
+
that make room and of the drop gliding into place is GlassKit
|
|
3400
|
+
Elements'; --gl-sort-duration (180ms) and --gl-sort-easing (ease-out)
|
|
3401
|
+
time it. None of it moves under prefers-reduced-motion. */
|
|
3402
|
+
.is-sort-source { opacity: 0.4; }
|
|
3403
|
+
.is-sort-before, .is-sort-after { position: relative; }
|
|
3404
|
+
.is-sort-before::before,
|
|
3405
|
+
.is-sort-after::before {
|
|
3406
|
+
content: ''; position: absolute; z-index: 2;
|
|
3407
|
+
inset-inline-start: calc(var(--gl-space-xs) + (var(--gl-tree-level, 1) - 1) * var(--gl-tree-indent, 20px));
|
|
3408
|
+
inset-inline-end: var(--gl-space-xs);
|
|
3409
|
+
height: 2px; border-radius: 1px;
|
|
3410
|
+
background: var(--gl-color-primary);
|
|
3411
|
+
box-shadow: 0 0 6px color-mix(in srgb, var(--gl-color-primary) 50%, transparent);
|
|
3412
|
+
pointer-events: none;
|
|
3413
|
+
}
|
|
3414
|
+
.is-sort-before::before { top: -1px; }
|
|
3415
|
+
.is-sort-after::before { bottom: -1px; }
|
|
3416
|
+
.is-sort-inside {
|
|
3417
|
+
background: color-mix(in srgb, var(--gl-color-primary) 16%, transparent);
|
|
3418
|
+
box-shadow: inset 0 0 0 1.5px var(--gl-border-focus);
|
|
3419
|
+
}
|
|
3420
|
+
.is-sort-refused {
|
|
3421
|
+
background: var(--gl-color-error-surface);
|
|
3422
|
+
box-shadow: inset 0 0 0 1.5px var(--gl-color-error-border);
|
|
3423
|
+
}
|
|
3424
|
+
.is-sort-refused::before { background: var(--gl-color-error); box-shadow: none; }
|
|
3425
|
+
.glass-sort__ghost {
|
|
3426
|
+
display: flex; align-items: center; gap: var(--gl-space-xs);
|
|
3427
|
+
max-width: min(360px, calc(100vw - 16px));
|
|
3428
|
+
padding: 10px 14px;
|
|
3429
|
+
border: 1px solid var(--gl-border-medium);
|
|
3430
|
+
border-radius: var(--gl-radius-sm);
|
|
3431
|
+
background: var(--gl-surface-4);
|
|
3432
|
+
backdrop-filter: blur(var(--gl-blur));
|
|
3433
|
+
-webkit-backdrop-filter: blur(var(--gl-blur));
|
|
3434
|
+
box-shadow: 0 12px 32px rgba(0,0,0,0.28), 0 2px 8px rgba(0,0,0,0.14), var(--gl-inset-strong);
|
|
3435
|
+
color: var(--gl-color-text);
|
|
3436
|
+
font-family: var(--gl-font-family); font-size: var(--gl-font-size-sm);
|
|
3437
|
+
font-weight: var(--gl-font-weight-medium);
|
|
3438
|
+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
3439
|
+
pointer-events: none;
|
|
3440
|
+
}
|
|
3441
|
+
.glass-sort__ghost[popover] { inset: auto; margin: 0; overflow: hidden; }
|
|
3442
|
+
/* Closed, out of the page: its own display would keep it on screen. */
|
|
3443
|
+
.glass-sort__ghost[popover]:not(:popover-open) { display: none; }
|
|
3444
|
+
/* A handle: with one, only the handle starts a drag, so a tap on the item
|
|
3445
|
+
still chooses it; a finger on it drags at once, without the long press. */
|
|
3446
|
+
.glass-sort__handle {
|
|
3447
|
+
flex: none; display: flex; align-items: center; justify-content: center;
|
|
3448
|
+
color: var(--gl-icon-muted);
|
|
3449
|
+
cursor: grab; touch-action: none;
|
|
3450
|
+
}
|
|
3451
|
+
.glass-sort__handle[hidden] { display: none; }
|
|
3452
|
+
.glass-sort__handle svg,
|
|
3453
|
+
.glass-sort__handle ::slotted(svg) {
|
|
3454
|
+
width: 18px; height: 18px; fill: none;
|
|
3455
|
+
stroke: currentColor; stroke-width: 1.8; stroke-linecap: round;
|
|
3456
|
+
}
|
|
3457
|
+
|
|
3458
|
+
|
|
2692
3459
|
/* ============================================================
|
|
2693
3460
|
26. SKELETON
|
|
2694
3461
|
============================================================ */
|
|
@@ -2728,12 +3495,12 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
|
|
|
2728
3495
|
font-size: var(--gl-font-size-sm); color: var(--gl-color-text);
|
|
2729
3496
|
}
|
|
2730
3497
|
.glass-table th {
|
|
2731
|
-
text-align: start; padding: 8px 10px; white-space: nowrap;
|
|
3498
|
+
text-align: start; padding: var(--gl-table-header-padding, 8px 10px); white-space: nowrap;
|
|
2732
3499
|
font-weight: var(--gl-font-weight-medium); color: var(--gl-color-text-muted);
|
|
2733
3500
|
border-bottom: 1px solid var(--gl-border-medium);
|
|
2734
3501
|
}
|
|
2735
3502
|
.glass-table td {
|
|
2736
|
-
padding: 9px 10px; vertical-align: top;
|
|
3503
|
+
padding: var(--gl-table-cell-padding, 9px 10px); vertical-align: top;
|
|
2737
3504
|
border-bottom: 1px solid var(--gl-border-subtle);
|
|
2738
3505
|
}
|
|
2739
3506
|
.glass-table tbody tr:last-child td { border-bottom: 0; }
|
|
@@ -2741,6 +3508,131 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
|
|
|
2741
3508
|
.glass-table__num { text-align: end; font-variant-numeric: tabular-nums; }
|
|
2742
3509
|
.glass-table__muted { color: var(--gl-color-text-muted); }
|
|
2743
3510
|
|
|
3511
|
+
/* More since 1.32.0, mostly brought by GlassKit Elements' <glk-table>
|
|
3512
|
+
around the table, which keeps it a native table in the page:
|
|
3513
|
+
|
|
3514
|
+
__sort a button filling a header cell that sorts by its column;
|
|
3515
|
+
the th carries aria-sort and the arrow shows it.
|
|
3516
|
+
--sticky the header cells stick to the top of a wrap that scrolls
|
|
3517
|
+
both ways (.glass-table-wrap--sticky, as high as
|
|
3518
|
+
--gl-table-max-height, 60vh), on --gl-table-header-bg.
|
|
3519
|
+
__actions a cell for row actions — a menu, icon buttons — at the
|
|
3520
|
+
row's end, kept there while the table scrolls sideways
|
|
3521
|
+
(on a surface of its own once it does: --scrolls).
|
|
3522
|
+
--list each row a card with the column's name before each value
|
|
3523
|
+
(data-label on the cells), the actions at its top end —
|
|
3524
|
+
for narrow places.
|
|
3525
|
+
|
|
3526
|
+
--gl-table-cell-padding and --gl-table-header-padding set the cells'
|
|
3527
|
+
room. */
|
|
3528
|
+
.glass-table__sort {
|
|
3529
|
+
display: inline-flex; align-items: center; gap: 4px;
|
|
3530
|
+
margin: -4px -6px; padding: 4px 6px;
|
|
3531
|
+
border: 0; border-radius: var(--gl-radius-xs);
|
|
3532
|
+
background: none; color: inherit;
|
|
3533
|
+
font: inherit; font-weight: inherit; white-space: nowrap;
|
|
3534
|
+
cursor: pointer;
|
|
3535
|
+
transition: background .15s ease, color .15s ease;
|
|
3536
|
+
}
|
|
3537
|
+
.glass-table__sort:hover { color: var(--gl-color-text); background: var(--gl-surface-1); }
|
|
3538
|
+
.glass-table__sort:focus-visible { outline: none; box-shadow: var(--gl-shadow-focus); }
|
|
3539
|
+
.glass-table__sort-icon {
|
|
3540
|
+
flex: none; width: 12px; height: 12px;
|
|
3541
|
+
fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round;
|
|
3542
|
+
opacity: 0.35;
|
|
3543
|
+
transition: transform .15s ease, opacity .15s ease;
|
|
3544
|
+
}
|
|
3545
|
+
.glass-table th:is([aria-sort="ascending"], [aria-sort="descending"]) .glass-table__sort { color: var(--gl-color-text); }
|
|
3546
|
+
.glass-table th:is([aria-sort="ascending"], [aria-sort="descending"]) .glass-table__sort-icon { opacity: 1; }
|
|
3547
|
+
.glass-table th[aria-sort="descending"] .glass-table__sort-icon { transform: rotate(180deg); }
|
|
3548
|
+
|
|
3549
|
+
.glass-table-wrap--sticky { overflow: auto; max-height: var(--gl-table-max-height, 60vh); overscroll-behavior: contain; }
|
|
3550
|
+
.glass-table--sticky thead th {
|
|
3551
|
+
position: sticky; top: 0; z-index: 2;
|
|
3552
|
+
/* The line goes with the cell: a collapsed border stays with the table. */
|
|
3553
|
+
border-bottom: 0;
|
|
3554
|
+
box-shadow: inset 0 -1px 0 var(--gl-border-medium);
|
|
3555
|
+
background: var(--gl-table-header-bg, color-mix(in srgb, var(--gl-color-bg-mid) 94%, transparent));
|
|
3556
|
+
backdrop-filter: blur(var(--gl-blur-light));
|
|
3557
|
+
-webkit-backdrop-filter: blur(var(--gl-blur-light));
|
|
3558
|
+
}
|
|
3559
|
+
|
|
3560
|
+
.glass-table__actions {
|
|
3561
|
+
position: sticky; inset-inline-end: 0; z-index: 1;
|
|
3562
|
+
width: 1%; text-align: end; white-space: nowrap;
|
|
3563
|
+
}
|
|
3564
|
+
.glass-table--sticky thead th.glass-table__actions { z-index: 3; }
|
|
3565
|
+
.glass-table--scrolls .glass-table__actions {
|
|
3566
|
+
background: var(--gl-table-header-bg, color-mix(in srgb, var(--gl-color-bg-mid) 94%, transparent));
|
|
3567
|
+
box-shadow: -8px 0 8px -8px rgba(0,0,0,0.3);
|
|
3568
|
+
}
|
|
3569
|
+
.glass-table--scrolls .glass-table__actions:dir(rtl) { box-shadow: 8px 0 8px -8px rgba(0,0,0,0.3); }
|
|
3570
|
+
|
|
3571
|
+
/* The edges of a wrap that scrolls sideways shade where more columns wait
|
|
3572
|
+
(data-more-start, data-more-end on the frame, set by <glk-table>). */
|
|
3573
|
+
.glass-table-frame { position: relative; min-width: 0; }
|
|
3574
|
+
.glass-table-frame::before,
|
|
3575
|
+
.glass-table-frame::after {
|
|
3576
|
+
content: ''; position: absolute; top: 0; bottom: 0; z-index: 4;
|
|
3577
|
+
width: 16px; pointer-events: none;
|
|
3578
|
+
opacity: 0; transition: opacity .15s ease;
|
|
3579
|
+
}
|
|
3580
|
+
.glass-table-frame::before { inset-inline-start: 0; background: linear-gradient(to right, rgba(0,0,0,0.22), transparent); }
|
|
3581
|
+
.glass-table-frame::after { inset-inline-end: 0; background: linear-gradient(to left, rgba(0,0,0,0.22), transparent); }
|
|
3582
|
+
.glass-table-frame:dir(rtl)::before { background: linear-gradient(to left, rgba(0,0,0,0.22), transparent); }
|
|
3583
|
+
.glass-table-frame:dir(rtl)::after { background: linear-gradient(to right, rgba(0,0,0,0.22), transparent); }
|
|
3584
|
+
.glass-table-frame[data-more-start]::before,
|
|
3585
|
+
.glass-table-frame[data-more-end]::after { opacity: 1; }
|
|
3586
|
+
.glass-table__empty { padding: var(--gl-space-lg) var(--gl-space-md); }
|
|
3587
|
+
.glass-table__empty[hidden] { display: none; }
|
|
3588
|
+
|
|
3589
|
+
/* As cards. The header stays for assistive technology, hidden from view;
|
|
3590
|
+
the labels are generated content without a text of their own, so a
|
|
3591
|
+
screen reader does not hear the column twice. */
|
|
3592
|
+
.glass-table--list thead {
|
|
3593
|
+
position: absolute; width: 1px; height: 1px; margin: -1px;
|
|
3594
|
+
overflow: hidden; clip-path: inset(50%); white-space: nowrap;
|
|
3595
|
+
}
|
|
3596
|
+
.glass-table--list,
|
|
3597
|
+
.glass-table--list tbody,
|
|
3598
|
+
.glass-table--list tr,
|
|
3599
|
+
.glass-table--list td { display: block; width: 100%; box-sizing: border-box; }
|
|
3600
|
+
.glass-table--list tbody { display: flex; flex-direction: column; gap: var(--gl-space-sm); }
|
|
3601
|
+
.glass-table--list tr {
|
|
3602
|
+
position: relative;
|
|
3603
|
+
display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: var(--gl-space-sm);
|
|
3604
|
+
padding: var(--gl-space-sm) var(--gl-space-md);
|
|
3605
|
+
border: 1px solid var(--gl-border-subtle);
|
|
3606
|
+
/* Glass like the list's and the accordion's items. */
|
|
3607
|
+
border-radius: var(--gl-radius-btn);
|
|
3608
|
+
background: var(--gl-surface-1);
|
|
3609
|
+
box-shadow: var(--gl-inset-subtle);
|
|
3610
|
+
}
|
|
3611
|
+
.glass-table--list td {
|
|
3612
|
+
display: flex; justify-content: space-between; align-items: baseline; gap: var(--gl-space-md);
|
|
3613
|
+
grid-column: 1 / -1;
|
|
3614
|
+
padding: 4px 0; border: 0;
|
|
3615
|
+
text-align: end; overflow-wrap: anywhere;
|
|
3616
|
+
}
|
|
3617
|
+
/* The actions at the card's top end, beside its first two values. */
|
|
3618
|
+
.glass-table--list tr:has(> .glass-table__actions) > td:nth-child(-n+2):not(.glass-table__actions) { grid-column: 1; }
|
|
3619
|
+
.glass-table--list td::before {
|
|
3620
|
+
content: attr(data-label);
|
|
3621
|
+
content: attr(data-label) / "";
|
|
3622
|
+
flex: none; max-width: 50%;
|
|
3623
|
+
color: var(--gl-color-text-muted); font-weight: var(--gl-font-weight-medium);
|
|
3624
|
+
text-align: start;
|
|
3625
|
+
}
|
|
3626
|
+
.glass-table--list td:is(:not([data-label]), [data-label=""])::before { content: none; }
|
|
3627
|
+
.glass-table--list tbody tr:hover td { background: none; }
|
|
3628
|
+
.glass-table--list .glass-table__actions {
|
|
3629
|
+
position: static; grid-column: 2; grid-row: 1 / span 2; align-self: start;
|
|
3630
|
+
width: auto; padding: 0; background: none; box-shadow: none;
|
|
3631
|
+
}
|
|
3632
|
+
@media (prefers-reduced-motion: reduce) {
|
|
3633
|
+
.glass-table__sort, .glass-table__sort-icon, .glass-table-frame::before, .glass-table-frame::after { transition: none; }
|
|
3634
|
+
}
|
|
3635
|
+
|
|
2744
3636
|
|
|
2745
3637
|
/* ============================================================
|
|
2746
3638
|
28. PROSE
|
|
@@ -3010,8 +3902,11 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
|
|
|
3010
3902
|
color: var(--gl-color-success-on-surface);
|
|
3011
3903
|
}
|
|
3012
3904
|
.glass-steps__item--current { color: var(--gl-color-text-heading); }
|
|
3905
|
+
/* The flat accent fills that carry text — this number, the chosen day of
|
|
3906
|
+
the date strip and of the calendar — take --gl-color-primary-fill where
|
|
3907
|
+
it is set (since 1.32.0), else the primary. */
|
|
3013
3908
|
.glass-steps__item--current .glass-steps__num {
|
|
3014
|
-
background: var(--gl-color-primary); border-color: transparent;
|
|
3909
|
+
background: var(--gl-color-primary-fill, var(--gl-color-primary)); border-color: transparent;
|
|
3015
3910
|
color: var(--gl-color-on-primary);
|
|
3016
3911
|
}
|
|
3017
3912
|
.glass-steps__line { flex: 1 1 8px; min-width: 8px; height: 1px; background: var(--gl-border-subtle); }
|
|
@@ -3184,7 +4079,7 @@ dialog.glass-sheet-overlay::backdrop { background: transparent; }
|
|
|
3184
4079
|
.glass-date-strip__day:hover { background: var(--gl-surface-3); }
|
|
3185
4080
|
.glass-date-strip__day[aria-pressed="true"] {
|
|
3186
4081
|
--gl-date-strip-ink: var(--gl-color-on-primary);
|
|
3187
|
-
background: var(--gl-color-primary);
|
|
4082
|
+
background: var(--gl-color-primary-fill, var(--gl-color-primary));
|
|
3188
4083
|
color: var(--gl-color-on-primary);
|
|
3189
4084
|
border-color: transparent;
|
|
3190
4085
|
box-shadow: var(--gl-shadow-btn-primary);
|
|
@@ -3201,6 +4096,10 @@ dialog.glass-sheet-overlay::backdrop { background: transparent; }
|
|
|
3201
4096
|
font-size: var(--gl-font-size-xs); opacity: 0.75;
|
|
3202
4097
|
text-transform: uppercase; letter-spacing: 0.04em;
|
|
3203
4098
|
}
|
|
4099
|
+
/* On the chosen chip the weekday is as strong as the number (since
|
|
4100
|
+
1.32.0): faded, it stayed below 4.5:1 even on a fill that the number
|
|
4101
|
+
reaches it on. */
|
|
4102
|
+
.glass-date-strip__day[aria-pressed="true"] .glass-date-strip__wd { opacity: 1; }
|
|
3204
4103
|
.glass-date-strip__num {
|
|
3205
4104
|
font-size: var(--gl-font-size-lg); font-weight: var(--gl-font-weight-semibold);
|
|
3206
4105
|
line-height: 1.1;
|
|
@@ -3290,7 +4189,7 @@ dialog.glass-sheet-overlay::backdrop { background: transparent; }
|
|
|
3290
4189
|
.glass-calendar__day--today { border-color: var(--gl-border-warm); }
|
|
3291
4190
|
.glass-calendar__day[aria-pressed="true"] {
|
|
3292
4191
|
--gl-calendar-ink: var(--gl-color-on-primary);
|
|
3293
|
-
background: var(--gl-color-primary);
|
|
4192
|
+
background: var(--gl-color-primary-fill, var(--gl-color-primary));
|
|
3294
4193
|
color: var(--gl-color-on-primary);
|
|
3295
4194
|
}
|
|
3296
4195
|
.glass-calendar__day:focus-visible { box-shadow: var(--gl-shadow-focus); }
|