@jungherz-de/glasskit 1.27.0 → 1.28.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.27.0
3
+ Jungherz GmbH – v1.28.0
4
4
 
5
5
  Features:
6
6
  - Scoped Reset (box-sizing fix)
@@ -1681,10 +1681,18 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
1681
1681
  -webkit-backdrop-filter: blur(var(--gl-blur));
1682
1682
  border-top: 1px solid var(--gl-border-subtle);
1683
1683
  display: flex; justify-content: space-around;
1684
+ /* The items sit in the middle of the room the home indicator leaves.
1685
+ Where its inset leaves less than they need (an iPhone with
1686
+ viewport-fit=cover), they overflow the content box and `safe` keeps
1687
+ them at the top, as before 1.28.0. Without one — Android, the desktop —
1688
+ the inset is 0px, not the fallback, and the items sat 9px below the
1689
+ top edge with 26px empty below them. A browser without `safe` keeps
1690
+ flex-start. */
1684
1691
  align-items: flex-start;
1692
+ align-items: safe center;
1685
1693
  padding-top: var(--gl-space-xs);
1686
- padding-bottom: env(safe-area-inset-bottom, 8px);
1687
- z-index: 900;
1694
+ padding-bottom: max(var(--gl-space-xs), env(safe-area-inset-bottom, 0px));
1695
+ z-index: var(--gl-tab-bar-z-index, 900);
1688
1696
  box-shadow: 0 -4px 20px rgba(0,0,0,0.08), var(--gl-inset-subtle);
1689
1697
  }
1690
1698
 
@@ -1699,30 +1707,44 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
1699
1707
  min-width: 60px;
1700
1708
  }
1701
1709
 
1710
+ /* Every state colours the icon box, and the SVG strokes in currentColor
1711
+ (since 1.28.0): an icon font, an <img> mask or an element slotted in
1712
+ takes the colour as well, with the lens of the drop and the drag state.
1713
+ A font icon takes the box's size unless it sets its own. Unset, the
1714
+ inactive colour is --gl-icon-muted and the hovered one --gl-icon-default;
1715
+ --gl-tab-bar-color and --gl-tab-bar-hover-color set them, for icon and
1716
+ label alike. */
1702
1717
  .glass-tab-bar__icon {
1703
1718
  width: 24px; height: 24px;
1704
1719
  display: flex; align-items: center; justify-content: center;
1705
1720
  position: relative;
1721
+ color: var(--gl-tab-bar-color, var(--gl-icon-muted));
1722
+ font-size: 24px;
1723
+ transition: var(--gl-transition);
1706
1724
  }
1707
1725
 
1708
- .glass-tab-bar__icon svg {
1726
+ .glass-tab-bar__icon svg,
1727
+ .glass-tab-bar__icon ::slotted(svg) {
1709
1728
  width: 24px; height: 24px; fill: none;
1710
- stroke: var(--gl-icon-muted); stroke-width: 1.8;
1729
+ stroke: currentColor; stroke-width: 1.8;
1711
1730
  stroke-linecap: round; stroke-linejoin: round;
1712
1731
  transition: var(--gl-transition);
1713
1732
  }
1714
1733
 
1715
1734
  .glass-tab-bar__label {
1716
- font-size: 10px; font-weight: var(--gl-font-weight-medium);
1717
- color: var(--gl-icon-muted); transition: var(--gl-transition);
1735
+ font-size: var(--gl-tab-bar-label-size, 10px); font-weight: var(--gl-font-weight-medium);
1736
+ color: var(--gl-tab-bar-color, var(--gl-icon-muted)); transition: var(--gl-transition);
1718
1737
  letter-spacing: 0.2px;
1719
1738
  }
1720
1739
 
1721
1740
  /* --gl-tab-bar-active-color, where set, colours the active icon and label
1722
1741
  alone: the spotlight lightens the active item more than any other surface.
1723
- Glow, dot and spotlight stay in the primary. */
1724
- .glass-tab-bar__item.is-active .glass-tab-bar__icon svg {
1725
- stroke: var(--gl-tab-bar-active-color, var(--gl-color-primary-text));
1742
+ Glow, dot and spotlight stay in the primary. The glow lies on the icon
1743
+ itself, not on the badge beside it; :where() keeps the rule as specific
1744
+ as it was when it named the svg. */
1745
+ .glass-tab-bar__item.is-active .glass-tab-bar__icon { color: var(--gl-tab-bar-active-color, var(--gl-color-primary-text)); }
1746
+ .glass-tab-bar__item.is-active .glass-tab-bar__icon > :where(:not(.glass-tab-bar__badge)),
1747
+ .glass-tab-bar__item.is-active .glass-tab-bar__icon ::slotted(*) {
1726
1748
  filter: var(--gl-tab-bar-active-glow, drop-shadow(0 0 6px color-mix(in srgb, var(--gl-color-primary) 35%, transparent)));
1727
1749
  }
1728
1750
  .glass-tab-bar__item.is-active .glass-tab-bar__label { color: var(--gl-tab-bar-active-color, var(--gl-color-primary-text)); }
@@ -1746,8 +1768,8 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
1746
1768
  box-shadow: 0 0 6px color-mix(in srgb, var(--gl-color-primary) 50%, transparent);
1747
1769
  }
1748
1770
 
1749
- .glass-tab-bar__item:not(.is-active):hover .glass-tab-bar__icon svg { stroke: var(--gl-icon-default); }
1750
- .glass-tab-bar__item:not(.is-active):hover .glass-tab-bar__label { color: var(--gl-icon-default); }
1771
+ .glass-tab-bar__item:not(.is-active):hover .glass-tab-bar__icon,
1772
+ .glass-tab-bar__item:not(.is-active):hover .glass-tab-bar__label { color: var(--gl-tab-bar-hover-color, var(--gl-icon-default)); }
1751
1773
 
1752
1774
  .glass-tab-bar__badge {
1753
1775
  position: absolute; top: -4px; inset-inline-end: -8px;
@@ -1771,7 +1793,7 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
1771
1793
  transform: translateX(-50%);
1772
1794
  display: flex; align-items: center;
1773
1795
  gap: var(--gl-tab-bar-floating-gap);
1774
- z-index: 900;
1796
+ z-index: var(--gl-tab-bar-z-index, 900);
1775
1797
  max-width: calc(100vw - 2 * var(--gl-space-md));
1776
1798
  }
1777
1799
 
@@ -1843,22 +1865,26 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
1843
1865
  outline: none; padding: 0;
1844
1866
  -webkit-tap-highlight-color: transparent;
1845
1867
  flex-shrink: 0;
1868
+ /* The icon colour sits on the button, the SVG strokes in currentColor
1869
+ (since 1.28.0), so a font icon follows hover and the colour variants. */
1870
+ color: var(--gl-icon-default);
1846
1871
  }
1847
1872
 
1848
1873
  .glass-tab-bar__accessory svg,
1849
1874
  .glass-tab-bar__accessory ::slotted(svg) {
1850
1875
  width: 22px; height: 22px; fill: none;
1851
- stroke: var(--gl-icon-default); stroke-width: 1.8;
1876
+ stroke: currentColor; stroke-width: 1.8;
1852
1877
  stroke-linecap: round; stroke-linejoin: round;
1853
1878
  transition: var(--gl-transition);
1854
1879
  }
1855
1880
 
1856
- /* :where() keeps the hover rules at their specificity while leaving a
1857
- disabled accessory out of them — a reset could not restore the icon
1881
+ /* :where() keeps the hover rule at its specificity while leaving a
1882
+ disabled accessory out of it — a reset could not restore the icon
1858
1883
  colour, which each tone variant sets on its own. */
1859
- .glass-tab-bar__accessory:where(:not(:disabled, [aria-disabled="true"])):hover { transform: translateY(-1px); }
1860
- .glass-tab-bar__accessory:where(:not(:disabled, [aria-disabled="true"])):hover svg,
1861
- .glass-tab-bar__accessory:where(:not(:disabled, [aria-disabled="true"])):hover ::slotted(svg) { stroke: var(--gl-icon-strong); }
1884
+ .glass-tab-bar__accessory:where(:not(:disabled, [aria-disabled="true"])):hover {
1885
+ transform: translateY(-1px);
1886
+ color: var(--gl-icon-strong);
1887
+ }
1862
1888
  .glass-tab-bar__accessory:disabled,
1863
1889
  .glass-tab-bar__accessory[aria-disabled="true"] { opacity: 0.45; cursor: not-allowed; }
1864
1890
 
@@ -1869,7 +1895,10 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
1869
1895
  var(--gl-inset-subtle);
1870
1896
  }
1871
1897
 
1872
- /* Accent variants – use existing color tokens */
1898
+ /* Accent variants – use existing color tokens. Each keeps its ink on
1899
+ hover; until 1.28.0 only the accent one did, and the success and error
1900
+ capsules turned their icon --gl-icon-strong, dark grey in the light
1901
+ theme. */
1873
1902
  .glass-tab-bar__accessory--accent {
1874
1903
  background: linear-gradient(135deg,
1875
1904
  var(--gl-color-primary) 0%,
@@ -1880,10 +1909,8 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
1880
1909
  0 8px 32px color-mix(in srgb, var(--gl-color-primary) 35%, transparent),
1881
1910
  var(--gl-inset-medium);
1882
1911
  }
1883
- .glass-tab-bar__accessory--accent svg,
1884
- .glass-tab-bar__accessory--accent ::slotted(svg) { stroke: var(--gl-color-on-primary, #ffffff); }
1885
- .glass-tab-bar__accessory--accent:hover svg,
1886
- .glass-tab-bar__accessory--accent:hover ::slotted(svg) { stroke: var(--gl-color-on-primary, #ffffff); }
1912
+ .glass-tab-bar__accessory--accent,
1913
+ .glass-tab-bar__accessory--accent:hover { color: var(--gl-color-on-primary, #ffffff); }
1887
1914
 
1888
1915
  .glass-tab-bar__accessory--success {
1889
1916
  background: linear-gradient(135deg,
@@ -1895,8 +1922,8 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
1895
1922
  0 8px 32px color-mix(in srgb, var(--gl-color-success) 35%, transparent),
1896
1923
  var(--gl-inset-medium);
1897
1924
  }
1898
- .glass-tab-bar__accessory--success svg,
1899
- .glass-tab-bar__accessory--success ::slotted(svg) { stroke: var(--gl-color-on-success, #ffffff); }
1925
+ .glass-tab-bar__accessory--success,
1926
+ .glass-tab-bar__accessory--success:hover { color: var(--gl-color-on-success, #ffffff); }
1900
1927
 
1901
1928
  .glass-tab-bar__accessory--error {
1902
1929
  background: linear-gradient(135deg,
@@ -1908,8 +1935,8 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
1908
1935
  0 8px 32px color-mix(in srgb, var(--gl-color-error) 35%, transparent),
1909
1936
  var(--gl-inset-medium);
1910
1937
  }
1911
- .glass-tab-bar__accessory--error svg,
1912
- .glass-tab-bar__accessory--error ::slotted(svg) { stroke: var(--gl-color-on-error, #ffffff); }
1938
+ .glass-tab-bar__accessory--error,
1939
+ .glass-tab-bar__accessory--error:hover { color: var(--gl-color-on-error, #ffffff); }
1913
1940
 
1914
1941
 
1915
1942
  /* ============================================================
@@ -1976,6 +2003,17 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
1976
2003
  inset 0 0 0 1px var(--gl-border-subtle),
1977
2004
  var(--gl-inset-strong);
1978
2005
  }
2006
+ /* The segmented drop likewise, in the chosen option's surface where a
2007
+ project set one (--gl-segmented-active-bg, -shadow; since 1.28.0). */
2008
+ .glass-segmented.glass-segmented--droplet[data-droplet]::before {
2009
+ background:
2010
+ var(--gl-gloss-droplet, none),
2011
+ var(--gl-segmented-active-bg, linear-gradient(to bottom, var(--gl-surface-5) 0%, var(--gl-surface-3) 100%));
2012
+ box-shadow:
2013
+ var(--gl-segmented-active-shadow, 0 4px 14px rgba(0,0,0,0.18), 0 1px 3px rgba(0,0,0,0.12)),
2014
+ inset 0 0 0 1px var(--gl-border-subtle),
2015
+ var(--gl-inset-strong);
2016
+ }
1979
2017
  /* On the way: the leading edge runs ahead, the trailing one is pulled
1980
2018
  after it and overshoots, so the drop stretches and draws together. */
1981
2019
  :is(.glass-tab-bar.glass-tab-bar--droplet, .glass-segmented.glass-segmented--droplet)[data-droplet][data-droplet-dir="right"]::before {
@@ -2020,32 +2058,35 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
2020
2058
  .glass-tab-bar--droplet[data-droplet] .glass-tab-bar__item.is-active { background: none; box-shadow: none; }
2021
2059
  .glass-tab-bar--droplet[data-droplet] .glass-tab-bar__item.is-active::before { display: none; }
2022
2060
  .glass-tab-bar--droplet[data-droplet] .glass-tab-bar__item.is-active .glass-tab-bar__icon::after { display: none; }
2023
- .glass-tab-bar--droplet[data-droplet] .glass-tab-bar__item.is-active .glass-tab-bar__icon svg { filter: var(--gl-tab-bar-active-glow, none); }
2061
+ .glass-tab-bar--droplet[data-droplet] .glass-tab-bar__item.is-active .glass-tab-bar__icon > :where(:not(.glass-tab-bar__badge)),
2062
+ .glass-tab-bar--droplet[data-droplet] .glass-tab-bar__item.is-active .glass-tab-bar__icon ::slotted(*) { filter: var(--gl-tab-bar-active-glow, none); }
2024
2063
  .glass-segmented--droplet[data-droplet] .glass-segmented__item[aria-pressed="true"] { background: transparent; box-shadow: none; }
2025
2064
  .glass-segmented--droplet[data-droplet] .glass-segmented__item[aria-pressed="true"]:focus-visible { box-shadow: var(--gl-shadow-focus); }
2026
2065
  /* A disabled choice dims its drop as it dims itself. */
2027
2066
  :is(.glass-tab-bar.glass-tab-bar--droplet, .glass-segmented.glass-segmented--droplet)[data-droplet]:has(:is(.glass-tab-bar__item.is-active, .glass-segmented__item[aria-pressed="true"]):disabled)::before { opacity: 0.45; }
2028
2067
 
2029
- /* Under the drop a tab icon or a tone dot grows as under a lens. While a
2030
- finger drags the drop, the item below it takes the chosen colour and
2031
- the chosen item lets it go. */
2032
- .glass-tab-bar--droplet[data-droplet] .glass-tab-bar__icon,
2033
- .glass-segmented--droplet[data-droplet] .glass-segmented__dot { transition: transform 0.4s cubic-bezier(0.34, 1.6, 0.5, 1); }
2068
+ /* Under the drop a tab icon, a segmented icon or a tone dot grows as under
2069
+ a lens. While a finger drags the drop, the item below it takes the
2070
+ chosen colour and the chosen item lets it go. The tab icon keeps the
2071
+ fade of its colour next to the lens. */
2072
+ .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); }
2073
+ .glass-segmented--droplet[data-droplet] .glass-segmented__dot,
2074
+ .glass-segmented--droplet[data-droplet] .glass-segmented__icon { transition: transform 0.4s cubic-bezier(0.34, 1.6, 0.5, 1); }
2034
2075
  .glass-tab-bar--droplet[data-droplet] .glass-tab-bar__item.is-under-droplet .glass-tab-bar__icon { transform: scale(1.15); }
2035
2076
  .glass-segmented--droplet[data-droplet] .glass-segmented__item.is-under-droplet .glass-segmented__dot { transform: scale(1.3); }
2036
- .glass-tab-bar--droplet[data-droplet] .glass-tab-bar__item.is-under-droplet .glass-tab-bar__icon svg { stroke: var(--gl-tab-bar-active-color, var(--gl-color-primary-text)); }
2037
- .glass-tab-bar--droplet[data-droplet] .glass-tab-bar__item.is-under-droplet .glass-tab-bar__label { color: var(--gl-tab-bar-active-color, var(--gl-color-primary-text)); }
2038
- .glass-segmented--droplet[data-droplet] .glass-segmented__item.is-under-droplet { color: var(--gl-color-text-heading); }
2039
- .glass-tab-bar--droplet[data-droplet-drag] .glass-tab-bar__item.is-active:not(.is-under-droplet) .glass-tab-bar__icon svg { stroke: var(--gl-icon-muted); }
2040
- .glass-tab-bar--droplet[data-droplet-drag] .glass-tab-bar__item.is-active:not(.is-under-droplet) .glass-tab-bar__label { color: var(--gl-icon-muted); }
2077
+ .glass-segmented--droplet[data-droplet] .glass-segmented__item.is-under-droplet .glass-segmented__icon { transform: scale(1.15); }
2078
+ .glass-tab-bar--droplet[data-droplet] .glass-tab-bar__item.is-under-droplet :is(.glass-tab-bar__icon, .glass-tab-bar__label) { color: var(--gl-tab-bar-active-color, var(--gl-color-primary-text)); }
2079
+ .glass-segmented--droplet[data-droplet] .glass-segmented__item.is-under-droplet { color: var(--gl-segmented-active-color, var(--gl-color-text-heading)); }
2080
+ .glass-tab-bar--droplet[data-droplet-drag] .glass-tab-bar__item.is-active:not(.is-under-droplet) :is(.glass-tab-bar__icon, .glass-tab-bar__label) { color: var(--gl-tab-bar-color, var(--gl-icon-muted)); }
2041
2081
  .glass-segmented--droplet[data-droplet-drag] .glass-segmented__item[aria-pressed="true"]:not(.is-under-droplet) { color: var(--gl-color-text-muted); }
2042
2082
 
2043
2083
  @media (prefers-reduced-motion: reduce) {
2044
2084
  :is(.glass-tab-bar.glass-tab-bar--droplet, .glass-segmented.glass-segmented--droplet)[data-droplet]::before,
2045
2085
  :is(.glass-tab-bar.glass-tab-bar--droplet, .glass-segmented.glass-segmented--droplet)[data-droplet][data-droplet-dir]::before,
2046
2086
  :is(.glass-tab-bar.glass-tab-bar--droplet, .glass-segmented.glass-segmented--droplet)[data-droplet][data-droplet-drag]::before { transition: none; }
2047
- .glass-tab-bar--droplet[data-droplet] .glass-tab-bar__icon,
2048
- .glass-segmented--droplet[data-droplet] .glass-segmented__dot { transition: none; }
2087
+ .glass-tab-bar--droplet[data-droplet] .glass-tab-bar__icon { transition: var(--gl-transition), transform 0s; }
2088
+ .glass-segmented--droplet[data-droplet] .glass-segmented__dot,
2089
+ .glass-segmented--droplet[data-droplet] .glass-segmented__icon { transition: none; }
2049
2090
  }
2050
2091
 
2051
2092
 
@@ -2757,13 +2798,18 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
2757
2798
  outline: none; -webkit-tap-highlight-color: transparent;
2758
2799
  }
2759
2800
  .glass-segmented__item:hover { color: var(--gl-color-text); }
2801
+ /* The chosen option's surface, shadow and text colour can be set
2802
+ (since 1.28.0), like the tab bar's active item: on a dark brand the
2803
+ light surface left a white label below 4.5:1. Unset, they keep their
2804
+ values; the drop of --droplet takes the same surface (18c). The shadow
2805
+ sits in shadow lists: switch it off with a zero shadow, not none. */
2760
2806
  .glass-segmented__item[aria-pressed="true"] {
2761
- background: var(--gl-surface-5);
2762
- color: var(--gl-color-text-heading);
2763
- box-shadow: var(--gl-shadow-btn);
2807
+ background: var(--gl-segmented-active-bg, var(--gl-surface-5));
2808
+ color: var(--gl-segmented-active-color, var(--gl-color-text-heading));
2809
+ box-shadow: var(--gl-segmented-active-shadow, var(--gl-shadow-btn));
2764
2810
  }
2765
2811
  .glass-segmented__item:focus-visible { box-shadow: var(--gl-shadow-focus); }
2766
- .glass-segmented__item[aria-pressed="true"]:focus-visible { box-shadow: var(--gl-shadow-focus), var(--gl-shadow-btn); }
2812
+ .glass-segmented__item[aria-pressed="true"]:focus-visible { box-shadow: var(--gl-shadow-focus), var(--gl-segmented-active-shadow, var(--gl-shadow-btn)); }
2767
2813
  .glass-segmented__item:disabled { opacity: 0.45; cursor: not-allowed; }
2768
2814
 
2769
2815
  .glass-segmented__dot {
@@ -2774,6 +2820,27 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
2774
2820
  .glass-segmented__item--warning { --gl-segmented-tone: var(--gl-color-warning); }
2775
2821
  .glass-segmented__item--error { --gl-segmented-tone: var(--gl-color-error); }
2776
2822
 
2823
+ /* An icon before the label (since 1.28.0): an SVG strokes in the text
2824
+ colour; any other element — a font icon, an <img>, a mask — takes the
2825
+ colour, and a font icon the size, from the box. Under the drop it grows
2826
+ like the dot. Hidden by script while empty: the display below beats the
2827
+ hidden attribute's own rule. --icons makes a row of icons alone, square,
2828
+ each option's name in its aria-label (and title, for the pointer). */
2829
+ .glass-segmented__icon {
2830
+ flex: none;
2831
+ display: inline-flex; align-items: center; justify-content: center;
2832
+ width: 18px; height: 18px;
2833
+ font-size: 18px; line-height: 1;
2834
+ }
2835
+ .glass-segmented__icon[hidden] { display: none; }
2836
+ .glass-segmented__icon svg,
2837
+ .glass-segmented__icon ::slotted(svg) {
2838
+ width: 18px; height: 18px; fill: none;
2839
+ stroke: currentColor; stroke-width: 1.8;
2840
+ stroke-linecap: round; stroke-linejoin: round;
2841
+ }
2842
+ .glass-segmented--icons .glass-segmented__item { padding: 8px; }
2843
+
2777
2844
 
2778
2845
  /* ============================================================
2779
2846
  30. STEPS
@@ -2819,9 +2886,19 @@ dialog.glass-modal-overlay::backdrop { background: transparent; }
2819
2886
  color: var(--gl-color-on-primary);
2820
2887
  }
2821
2888
  .glass-steps__line { flex: 1 1 8px; min-width: 8px; height: 1px; background: var(--gl-border-subtle); }
2889
+ /* Narrow, the current label comes first (since 1.28.0). The circles keep
2890
+ their size and the lines take only the room that is left, down to 2px
2891
+ at 4px from their neighbours: the gap belongs to the list, which is the
2892
+ container itself and out of its own query's reach, so the lines take a
2893
+ negative margin. Only then does the label shorten. Until 1.28.0 every
2894
+ part gave way at once — seven steps cut the current label in a 350px
2895
+ column and pushed the circles past its edge. */
2822
2896
  @container (max-width: 360px) {
2823
2897
  .glass-steps__label { display: none; }
2824
2898
  .glass-steps__item--current .glass-steps__label { display: inline; }
2899
+ .glass-steps__item { flex-shrink: 0; }
2900
+ .glass-steps__item--current { flex-shrink: 1; }
2901
+ .glass-steps__line { flex-basis: 0; min-width: 2px; margin-inline: -2px; }
2825
2902
  }
2826
2903
 
2827
2904