@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/glasskit.css CHANGED
@@ -1,6 +1,6 @@
1
1
  /* ============================================================
2
2
  GLASSKIT COMPONENT LIBRARY
3
- Jungherz GmbH – v1.31.0
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
- background: var(--gl-radio-dot-color, white);
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
- transition: var(--gl-transition); outline: none;
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: var(--gl-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: var(--gl-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: var(--gl-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
- .glass-tab-bar__item.is-active .glass-tab-bar__label { color: var(--gl-tab-bar-active-color, var(--gl-color-primary-text)); }
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-transition), transform 0.4s cubic-bezier(0.34, 1.6, 0.5, 1); }
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 { transition: none; }
2111
- .glass-tab-bar--droplet[data-droplet] .glass-tab-bar__icon { transition: var(--gl-transition), transform 0s; }
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); }