@jungherz-de/glasskit 1.19.1 → 1.21.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,11 +1,12 @@
1
1
  /* ============================================================
2
2
  GLASSKIT COMPONENT LIBRARY
3
- Jungherz GmbH – v1.19.1
3
+ Jungherz GmbH – v1.21.0
4
4
 
5
5
  Features:
6
6
  - Scoped Reset (box-sizing fix)
7
7
  - Dark Mode (default)
8
8
  - Light Mode via [data-theme="light"]
9
+ - Compact density via [data-density="compact"]
9
10
  - 34 components (incl. floating Tab-Bar variant + Accessory)
10
11
  ============================================================ */
11
12
 
@@ -68,6 +69,8 @@
68
69
  --gl-color-text-muted: rgba(255, 255, 255, 0.60);
69
70
  --gl-color-text-on-light: #1a3a4a;
70
71
  --gl-color-text-heading: #ffffff;
72
+ /* The select's chevron, drawn in the muted text colour of each theme. */
73
+ --gl-select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='rgba(255,255,255,0.6)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
71
74
  --gl-color-success: #34c759;
72
75
  --gl-color-error: #ff3b30;
73
76
  --gl-color-warning: #ffcc00;
@@ -198,6 +201,29 @@
198
201
  --gl-space-3xl: 40px;
199
202
  --gl-space-4xl: 56px;
200
203
 
204
+ /* ---- Density: control sizes ----
205
+ The sizes GlassKit has always drawn its controls at, as tokens; the
206
+ rules read them with that value as fallback, like --gl-modal-max-width.
207
+ [data-density="compact"] (1c below) swaps in a denser set. The toggle's
208
+ thumb inset and travel and the first-line alignment of toggle, checkbox
209
+ and radio are derived from these, so any value set here stays aligned.
210
+ Two more are left unset on purpose, like --gl-toast-top:
211
+ --gl-control-padding-x (inner left and right of input, select and
212
+ textarea) and --gl-list-item-padding-y. Unset, the rules fall back to
213
+ --gl-space-md as before, so a spacing override still reaches them
214
+ wherever it is declared. */
215
+ --gl-control-height: 52px; /* input, select */
216
+ --gl-btn-height: 56px;
217
+ --gl-btn-height-sm: 44px;
218
+ --gl-btn-height-lg: 64px;
219
+ --gl-textarea-min-height: 120px;
220
+ --gl-list-item-min-height: 56px;
221
+ --gl-toggle-width: 52px;
222
+ --gl-toggle-height: 30px;
223
+ --gl-toggle-thumb-size: 22px;
224
+ --gl-check-size: 24px; /* checkbox, radio */
225
+ --gl-modal-action-height: 52px;
226
+
201
227
  /* ---- Typography ---- */
202
228
  --gl-font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display',
203
229
  'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
@@ -219,6 +245,8 @@
219
245
 
220
246
  /* ---- Tab-Bar ---- */
221
247
  --gl-tab-bar-height: 82px;
248
+ /* Widest a .glass-modal grows; dialogs with a form or a table can take more. */
249
+ --gl-modal-max-width: 340px;
222
250
 
223
251
  /* ---- Tab-Bar Floating ---- */
224
252
  --gl-tab-bar-floating-bottom: 16px;
@@ -261,6 +289,7 @@
261
289
  --gl-color-text-muted: rgba(26, 42, 54, 0.55);
262
290
  --gl-color-text-on-light: #1a3a4a;
263
291
  --gl-color-text-heading: #0f1f2a;
292
+ --gl-select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='rgba(26,42,54,0.55)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
264
293
  --gl-color-success: #28a745;
265
294
  --gl-color-error: #dc3545;
266
295
  --gl-color-warning: #e6a800;
@@ -347,6 +376,37 @@
347
376
  }
348
377
 
349
378
 
379
+ /* ============================================================
380
+ 1c. DESIGN TOKENS – COMPACT DENSITY
381
+ ============================================================ */
382
+ /* Opt-in, next to the theme: <html data-density="compact">. Fields and
383
+ buttons at 40px instead of 52 and 56, smaller toggles and boxes, denser
384
+ list rows — for admin screens, desktop layouts and narrow forms. Only
385
+ the density tokens change; colours, type and spacing stay as they are.
386
+
387
+ A bare attribute selector, like [data-theme="light"]: it matches <html>,
388
+ and a theme wrapper that carries the attribute in a shadow root that
389
+ adopts the full sheet. build-styles-js.mjs moves it into tokensCss with
390
+ the theme blocks. It stays after them: the dark one declares the
391
+ defaults with the same specificity, and where both match, the later
392
+ block wins. */
393
+ [data-density="compact"] {
394
+ --gl-control-height: 40px;
395
+ --gl-control-padding-x: var(--gl-space-sm);
396
+ --gl-btn-height: 40px;
397
+ --gl-btn-height-sm: 32px;
398
+ --gl-btn-height-lg: 48px;
399
+ --gl-textarea-min-height: 88px;
400
+ --gl-list-item-min-height: 48px;
401
+ --gl-list-item-padding-y: 10px;
402
+ --gl-toggle-width: 44px;
403
+ --gl-toggle-height: 26px;
404
+ --gl-toggle-thumb-size: 18px;
405
+ --gl-check-size: 20px;
406
+ --gl-modal-action-height: 44px;
407
+ }
408
+
409
+
350
410
  /* ============================================================
351
411
  2. HINTERGRUND
352
412
  ============================================================ */
@@ -519,7 +579,7 @@
519
579
  ============================================================ */
520
580
  .glass-btn {
521
581
  display: flex; align-items: center; justify-content: center;
522
- gap: 10px; width: 100%; height: 56px;
582
+ gap: 10px; width: 100%; height: var(--gl-btn-height, 56px);
523
583
  border: none; border-radius: var(--gl-radius-btn);
524
584
  font-family: var(--gl-font-family);
525
585
  font-size: var(--gl-font-size-btn);
@@ -598,8 +658,8 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
598
658
  .glass-btn--tertiary:active { transform: translateY(1px); }
599
659
 
600
660
  /* Sizes */
601
- .glass-btn--sm { height: 44px; font-size: var(--gl-font-size-sm); border-radius: var(--gl-radius-sm); }
602
- .glass-btn--lg { height: 64px; font-size: var(--gl-font-size-lg); }
661
+ .glass-btn--sm { height: var(--gl-btn-height-sm, 44px); font-size: var(--gl-font-size-sm); border-radius: var(--gl-radius-sm); }
662
+ .glass-btn--lg { height: var(--gl-btn-height-lg, 64px); font-size: var(--gl-font-size-lg); }
603
663
  .glass-btn--auto { width: auto; padding: 0 var(--gl-space-xl); }
604
664
 
605
665
 
@@ -643,12 +703,12 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
643
703
  font-size: var(--gl-font-size-sm);
644
704
  font-weight: var(--gl-font-weight-medium);
645
705
  color: var(--gl-icon-strong);
646
- padding-left: var(--gl-space-2xs);
706
+ padding-inline-start: var(--gl-space-2xs);
647
707
  }
648
708
 
649
709
  .glass-input {
650
- width: 100%; height: 52px;
651
- padding: 0 var(--gl-space-md);
710
+ width: 100%; height: var(--gl-control-height, 52px);
711
+ padding: 0 var(--gl-control-padding-x, var(--gl-space-md));
652
712
  background: var(--gl-surface-2);
653
713
  backdrop-filter: blur(var(--gl-blur-light));
654
714
  -webkit-backdrop-filter: blur(var(--gl-blur-light));
@@ -697,14 +757,14 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
697
757
  .glass-hint {
698
758
  font-size: var(--gl-font-size-xs);
699
759
  color: var(--gl-color-text-muted);
700
- padding-left: var(--gl-space-2xs);
760
+ padding-inline-start: var(--gl-space-2xs);
701
761
  }
702
762
  .glass-hint--error { color: var(--gl-color-error); }
703
763
 
704
764
  /* Textarea */
705
765
  .glass-textarea {
706
- width: 100%; min-height: 120px;
707
- padding: var(--gl-space-md);
766
+ width: 100%; min-height: var(--gl-textarea-min-height, 120px);
767
+ padding: var(--gl-space-md) var(--gl-control-padding-x, var(--gl-space-md));
708
768
  background: var(--gl-surface-2);
709
769
  backdrop-filter: blur(var(--gl-blur-light));
710
770
  -webkit-backdrop-filter: blur(var(--gl-blur-light));
@@ -730,8 +790,8 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
730
790
  9. SELECT
731
791
  ============================================================ */
732
792
  .glass-select {
733
- width: 100%; height: 52px;
734
- padding: 0 var(--gl-space-3xl) 0 var(--gl-space-md);
793
+ width: 100%; height: var(--gl-control-height, 52px);
794
+ padding: 0 var(--gl-space-3xl) 0 var(--gl-control-padding-x, var(--gl-space-md));
735
795
  background: var(--gl-surface-2);
736
796
  backdrop-filter: blur(var(--gl-blur-light));
737
797
  -webkit-backdrop-filter: blur(var(--gl-blur-light));
@@ -745,10 +805,16 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
745
805
  box-shadow: var(--gl-inset-subtle);
746
806
  cursor: pointer;
747
807
  appearance: none; -webkit-appearance: none;
748
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='rgba(255,255,255,0.6)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
808
+ background-image: var(--gl-select-chevron, url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='rgba(255,255,255,0.6)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E"));
749
809
  background-repeat: no-repeat;
750
810
  background-position: right 16px center;
751
811
  }
812
+ /* Right to left: the chevron and the room kept for it move to the left —
813
+ together, under :dir(), so a browser without it keeps both on the right. */
814
+ .glass-select:dir(rtl) {
815
+ padding: 0 var(--gl-control-padding-x, var(--gl-space-md)) 0 var(--gl-space-3xl);
816
+ background-position: left 16px center;
817
+ }
752
818
  .glass-select:focus {
753
819
  border-color: var(--gl-border-focus);
754
820
  box-shadow: var(--gl-shadow-focus), var(--gl-inset-medium);
@@ -764,7 +830,7 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
764
830
  }
765
831
 
766
832
  .glass-search__icon {
767
- position: absolute; left: var(--gl-space-md);
833
+ position: absolute; inset-inline-start: var(--gl-space-md);
768
834
  width: 18px; height: 18px;
769
835
  stroke: var(--gl-icon-muted) !important;
770
836
  stroke-width: 2 !important;
@@ -776,7 +842,7 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
776
842
  }
777
843
 
778
844
  .glass-search .glass-input {
779
- padding-left: 44px;
845
+ padding-inline-start: 44px;
780
846
  border-radius: var(--gl-radius-full);
781
847
  }
782
848
 
@@ -784,23 +850,30 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
784
850
  /* ============================================================
785
851
  11. TOGGLE SWITCH
786
852
  ============================================================ */
787
- /* First-line alignment — see the note on .glass-checkbox below. Track is 30px. */
853
+ /* First-line alignment — see the note on .glass-checkbox below. The track
854
+ is --gl-toggle-height high (30px by default). */
788
855
  .glass-toggle {
789
856
  display: inline-flex; align-items: flex-start;
790
857
  gap: var(--gl-space-sm); cursor: pointer;
791
858
  -webkit-tap-highlight-color: transparent;
792
859
  }
793
860
 
794
- /* Visually hidden but keeps the control's area: zero-size inputs make the
795
- browser suppress the native validation bubble on required fields
796
- (silent submit block). pointer-events: none — the label handles clicks. */
861
+ /* Visually hidden, but it keeps the control's area and lies on top of it.
862
+ Zero-size inputs make the browser suppress the native validation bubble
863
+ on required fields (silent submit block). On top, a pointer — or a test
864
+ tool that clicks the switch by its role — reaches the input itself; the
865
+ track drew over it before and took the click. It covers the track only:
866
+ the label text, which may hold a link, stays the label's. margin-top is
867
+ the track's first-line offset, from the label's font; a browser without
868
+ the lh unit drops it and keeps margin: 0, as the track does. */
797
869
  .glass-toggle__input {
798
- position: absolute; opacity: 0; margin: 0;
799
- width: 52px; height: 30px; pointer-events: none;
870
+ position: absolute; z-index: 1; opacity: 0; cursor: inherit;
871
+ width: var(--gl-toggle-width, 52px); height: var(--gl-toggle-height, 30px); margin: 0; font: inherit;
872
+ margin-top: max(0px, calc((1lh - var(--gl-toggle-height, 30px)) / 2));
800
873
  }
801
874
 
802
875
  .glass-toggle__track {
803
- position: relative; width: 52px; height: 30px;
876
+ position: relative; width: var(--gl-toggle-width, 52px); height: var(--gl-toggle-height, 30px);
804
877
  background: var(--gl-surface-2);
805
878
  backdrop-filter: blur(var(--gl-blur-soft));
806
879
  -webkit-backdrop-filter: blur(var(--gl-blur-soft));
@@ -809,12 +882,19 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
809
882
  transition: var(--gl-transition);
810
883
  box-shadow: var(--gl-inset-subtle);
811
884
  flex-shrink: 0;
812
- margin-top: max(0px, calc((1lh - 30px) / 2));
885
+ margin-top: max(0px, calc((1lh - var(--gl-toggle-height, 30px)) / 2));
813
886
  }
814
887
 
888
+ /* Inset and travel follow the track. Top and left (right, right to left)
889
+ are (height − thumb) / 2 − 1px inside the 1.5px border: 3px at 52 × 30,
890
+ where the thumb has always sat (taking off the whole border would move
891
+ it by half a pixel). It travels width − height, so the gap at the end
892
+ mirrors the one below. */
815
893
  .glass-toggle__thumb {
816
- position: absolute; top: 3px; left: 3px;
817
- width: 22px; height: 22px;
894
+ position: absolute;
895
+ top: calc((var(--gl-toggle-height, 30px) - var(--gl-toggle-thumb-size, 22px)) / 2 - 1px);
896
+ left: calc((var(--gl-toggle-height, 30px) - var(--gl-toggle-thumb-size, 22px)) / 2 - 1px);
897
+ width: var(--gl-toggle-thumb-size, 22px); height: var(--gl-toggle-thumb-size, 22px);
818
898
  border-radius: var(--gl-radius-pill);
819
899
  background: var(--gl-toggle-thumb-bg);
820
900
  box-shadow: 0 2px 6px rgba(0,0,0,0.2), var(--gl-inset-milk);
@@ -829,7 +909,15 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
829
909
  }
830
910
 
831
911
  .glass-toggle__input:checked + .glass-toggle__track .glass-toggle__thumb {
832
- transform: translateX(22px);
912
+ transform: translateX(calc(var(--gl-toggle-width, 52px) - var(--gl-toggle-height, 30px)));
913
+ }
914
+ /* Right to left the thumb starts on the right and travels left — both
915
+ under :dir(), so a browser without it keeps the left-to-right switch. */
916
+ .glass-toggle__thumb:dir(rtl) {
917
+ left: auto; right: calc((var(--gl-toggle-height, 30px) - var(--gl-toggle-thumb-size, 22px)) / 2 - 1px);
918
+ }
919
+ .glass-toggle__input:checked + .glass-toggle__track .glass-toggle__thumb:dir(rtl) {
920
+ transform: translateX(calc(var(--gl-toggle-height, 30px) - var(--gl-toggle-width, 52px)));
833
921
  }
834
922
 
835
923
  .glass-toggle__input:focus-visible + .glass-toggle__track { box-shadow: var(--gl-shadow-focus); }
@@ -838,8 +926,12 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
838
926
  font-size: var(--gl-font-size-base);
839
927
  color: var(--gl-color-text);
840
928
  user-select: none;
841
- margin-top: max(0px, calc((30px - 1lh) / 2));
929
+ margin-top: max(0px, calc((var(--gl-toggle-height, 30px) - 1lh) / 2));
842
930
  }
931
+ /* A control without a label keeps no gap for one. */
932
+ .glass-toggle__label:empty,
933
+ .glass-checkbox__label:empty,
934
+ .glass-radio__label:empty { display: none; }
843
935
 
844
936
 
845
937
  /* ============================================================
@@ -847,24 +939,26 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
847
939
  ============================================================ */
848
940
  /* Control and label are aligned on the *first line*, not on the middle of the
849
941
  text block — a consent label runs over several lines and a centred box then
850
- points at nothing. The two margins below centre the 24px box on that first
851
- line; whichever is the shorter of the two gets the offset, so exactly one is
852
- ever non-zero. For a single-line label this is pixel-identical to the
853
- `align-items: center` of pre-1.11.0. Repeated on .glass-radio (24px) and
854
- .glass-toggle (30px) — keep the three in step. */
942
+ points at nothing. The two margins below centre the box (--gl-check-size,
943
+ 24px by default) on that first line; whichever is the shorter of the two
944
+ gets the offset, so exactly one is ever non-zero. For a single-line label
945
+ this is pixel-identical to the `align-items: center` of pre-1.11.0.
946
+ Repeated on .glass-radio (--gl-check-size) and .glass-toggle
947
+ (--gl-toggle-height) — keep the three in step. */
855
948
  .glass-checkbox {
856
949
  display: inline-flex; align-items: flex-start;
857
950
  gap: var(--gl-space-sm); cursor: pointer;
858
951
  -webkit-tap-highlight-color: transparent;
859
952
  }
860
- /* See .glass-toggle__input: keep area so native validation bubbles anchor */
953
+ /* On top of the box, covering it only — see .glass-toggle__input. */
861
954
  .glass-checkbox__input {
862
- position: absolute; opacity: 0; margin: 0;
863
- width: 24px; height: 24px; pointer-events: none;
955
+ position: absolute; z-index: 1; opacity: 0; cursor: inherit;
956
+ width: var(--gl-check-size, 24px); height: var(--gl-check-size, 24px); margin: 0; font: inherit;
957
+ margin-top: max(0px, calc((1lh - var(--gl-check-size, 24px)) / 2));
864
958
  }
865
959
 
866
960
  .glass-checkbox__box {
867
- width: 24px; height: 24px;
961
+ width: var(--gl-check-size, 24px); height: var(--gl-check-size, 24px);
868
962
  border-radius: var(--gl-radius-xs);
869
963
  background: var(--gl-surface-2);
870
964
  backdrop-filter: blur(var(--gl-blur-soft));
@@ -874,7 +968,7 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
874
968
  transition: var(--gl-transition);
875
969
  box-shadow: var(--gl-inset-subtle);
876
970
  flex-shrink: 0;
877
- margin-top: max(0px, calc((1lh - 24px) / 2));
971
+ margin-top: max(0px, calc((1lh - var(--gl-check-size, 24px)) / 2));
878
972
  }
879
973
 
880
974
  .glass-checkbox__box svg {
@@ -897,7 +991,7 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
897
991
  .glass-checkbox__label {
898
992
  font-size: var(--gl-font-size-base);
899
993
  color: var(--gl-color-text); user-select: none;
900
- margin-top: max(0px, calc((24px - 1lh) / 2));
994
+ margin-top: max(0px, calc((var(--gl-check-size, 24px) - 1lh) / 2));
901
995
  }
902
996
 
903
997
 
@@ -910,14 +1004,15 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
910
1004
  gap: var(--gl-space-sm); cursor: pointer;
911
1005
  -webkit-tap-highlight-color: transparent;
912
1006
  }
913
- /* See .glass-toggle__input: keep area so native validation bubbles anchor */
1007
+ /* On top of the circle, covering it only — see .glass-toggle__input. */
914
1008
  .glass-radio__input {
915
- position: absolute; opacity: 0; margin: 0;
916
- width: 24px; height: 24px; pointer-events: none;
1009
+ position: absolute; z-index: 1; opacity: 0; cursor: inherit;
1010
+ width: var(--gl-check-size, 24px); height: var(--gl-check-size, 24px); margin: 0; font: inherit;
1011
+ margin-top: max(0px, calc((1lh - var(--gl-check-size, 24px)) / 2));
917
1012
  }
918
1013
 
919
1014
  .glass-radio__circle {
920
- width: 24px; height: 24px;
1015
+ width: var(--gl-check-size, 24px); height: var(--gl-check-size, 24px);
921
1016
  border-radius: var(--gl-radius-pill);
922
1017
  background: var(--gl-surface-2);
923
1018
  backdrop-filter: blur(var(--gl-blur-soft));
@@ -926,7 +1021,7 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
926
1021
  transition: var(--gl-transition);
927
1022
  box-shadow: var(--gl-inset-subtle);
928
1023
  flex-shrink: 0;
929
- margin-top: max(0px, calc((1lh - 24px) / 2));
1024
+ margin-top: max(0px, calc((1lh - var(--gl-check-size, 24px)) / 2));
930
1025
  }
931
1026
 
932
1027
  .glass-radio__dot {
@@ -949,7 +1044,7 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
949
1044
  .glass-radio__label {
950
1045
  font-size: var(--gl-font-size-base);
951
1046
  color: var(--gl-color-text); user-select: none;
952
- margin-top: max(0px, calc((24px - 1lh) / 2));
1047
+ margin-top: max(0px, calc((var(--gl-check-size, 24px) - 1lh) / 2));
953
1048
  }
954
1049
 
955
1050
 
@@ -1071,6 +1166,9 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
1071
1166
  );
1072
1167
  box-shadow: 0 0 8px color-mix(in srgb, var(--gl-color-error) 30%, transparent), var(--gl-inset-strong);
1073
1168
  }
1169
+ /* The fill grows from the inline start; right to left its gradient turns
1170
+ round so the darker end still leads. */
1171
+ .glass-progress__fill:dir(rtl) { scale: -1 1; }
1074
1172
 
1075
1173
 
1076
1174
  /* ============================================================
@@ -1089,9 +1187,20 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
1089
1187
  -webkit-font-smoothing: antialiased;
1090
1188
  }
1091
1189
  .glass-modal-overlay.is-active { opacity: 1; pointer-events: auto; }
1190
+ /* The overlay may be a native <dialog>: opened with showModal() it lies in
1191
+ the top layer, the page behind it is inert, focus moves in and Escape
1192
+ closes it. These rules undo the dialog's own box and backdrop; the
1193
+ overlay above paints the dimmed, blurred layer. GlassKit Elements'
1194
+ <glk-modal> uses exactly this. */
1195
+ dialog.glass-modal-overlay {
1196
+ width: auto; height: auto; max-width: none; max-height: none;
1197
+ margin: 0; border: 0; color: inherit;
1198
+ }
1199
+ dialog.glass-modal-overlay:not([open]) { display: none; }
1200
+ dialog.glass-modal-overlay::backdrop { background: transparent; }
1092
1201
 
1093
1202
  .glass-modal {
1094
- width: 100%; max-width: 340px;
1203
+ width: 100%; max-width: var(--gl-modal-max-width, 340px);
1095
1204
  background: linear-gradient(to bottom,
1096
1205
  var(--gl-card-glow-top) 0%, var(--gl-card-glow-bottom) 100%
1097
1206
  );
@@ -1123,7 +1232,7 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
1123
1232
  .glass-modal__footer { display: flex; border-top: 1px solid var(--gl-border-subtle); }
1124
1233
 
1125
1234
  .glass-modal__action {
1126
- flex: 1; height: 52px;
1235
+ flex: 1; height: var(--gl-modal-action-height, 52px);
1127
1236
  background: transparent; border: none;
1128
1237
  font-family: var(--gl-font-family);
1129
1238
  font-size: var(--gl-font-size-btn);
@@ -1132,7 +1241,7 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
1132
1241
  cursor: pointer; transition: var(--gl-transition-fast); outline: none;
1133
1242
  }
1134
1243
  .glass-modal__action:hover { background: var(--gl-surface-1); }
1135
- .glass-modal__action + .glass-modal__action { border-left: 1px solid var(--gl-border-subtle); }
1244
+ .glass-modal__action + .glass-modal__action { border-inline-start: 1px solid var(--gl-border-subtle); }
1136
1245
  .glass-modal__action--primary { color: var(--gl-color-primary); font-weight: var(--gl-font-weight-semibold); }
1137
1246
  .glass-modal__action--danger { color: var(--gl-color-error); font-weight: var(--gl-font-weight-semibold); }
1138
1247
 
@@ -1243,7 +1352,7 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
1243
1352
  }
1244
1353
  .glass-toast__action:active { transform: scale(0.96); }
1245
1354
  .glass-toast__close {
1246
- width: 28px; height: 28px; margin: 0 -8px 0 -4px; padding: 0;
1355
+ width: 28px; height: 28px; margin-block: 0; margin-inline: -4px -8px; padding: 0;
1247
1356
  display: inline-flex; align-items: center; justify-content: center;
1248
1357
  border: 0; border-radius: 50%;
1249
1358
  background: transparent;
@@ -1327,7 +1436,7 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
1327
1436
  .glass-tab-bar__item:not(.is-active):hover .glass-tab-bar__label { color: var(--gl-icon-default); }
1328
1437
 
1329
1438
  .glass-tab-bar__badge {
1330
- position: absolute; top: -4px; right: -8px;
1439
+ position: absolute; top: -4px; inset-inline-end: -8px;
1331
1440
  min-width: 16px; height: 16px; padding: 0 4px;
1332
1441
  border-radius: var(--gl-radius-full);
1333
1442
  /* 10px text needs 4.5:1 – white on the plain error red only reaches 3.55:1
@@ -1513,7 +1622,7 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
1513
1622
  font-family: var(--gl-font-family);
1514
1623
  font-size: var(--gl-font-size-base);
1515
1624
  font-weight: var(--gl-font-weight-semibold);
1516
- color: var(--gl-color-text); text-align: left;
1625
+ color: var(--gl-color-text); text-align: start;
1517
1626
  outline: none; transition: var(--gl-transition);
1518
1627
  -webkit-tap-highlight-color: transparent;
1519
1628
  }
@@ -1782,18 +1891,19 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
1782
1891
  transform-origin: bottom center;
1783
1892
  }
1784
1893
  .glass-popover--start {
1785
- left: 0;
1894
+ inset-inline-start: 0; inset-inline-end: auto;
1786
1895
  transform: translateY(-4px) scale(0.96);
1787
1896
  transform-origin: top left;
1788
1897
  }
1898
+ .glass-popover--start:dir(rtl) { transform-origin: top right; }
1789
1899
  .glass-popover--start.is-open { transform: translateY(0) scale(1); }
1790
1900
 
1791
1901
  .glass-popover--end {
1792
- left: auto;
1793
- right: 0;
1902
+ inset-inline-start: auto; inset-inline-end: 0;
1794
1903
  transform: translateY(-4px) scale(0.96);
1795
1904
  transform-origin: top right;
1796
1905
  }
1906
+ .glass-popover--end:dir(rtl) { transform-origin: top left; }
1797
1907
  .glass-popover--end.is-open { transform: translateY(0) scale(1); }
1798
1908
 
1799
1909
 
@@ -1850,15 +1960,15 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
1850
1960
  display: flex;
1851
1961
  align-items: center;
1852
1962
  gap: var(--gl-space-sm);
1853
- padding: var(--gl-space-md) var(--gl-space-lg);
1854
- min-height: 56px;
1963
+ padding: var(--gl-list-item-padding-y, var(--gl-space-md)) var(--gl-space-lg);
1964
+ min-height: var(--gl-list-item-min-height, 56px);
1855
1965
  color: var(--gl-color-text);
1856
1966
  font-size: var(--gl-font-size-base);
1857
1967
  text-decoration: none;
1858
1968
  background: transparent;
1859
1969
  border: none;
1860
1970
  width: 100%;
1861
- text-align: left;
1971
+ text-align: start;
1862
1972
  font-family: inherit;
1863
1973
  list-style: none;
1864
1974
  }
@@ -1866,20 +1976,20 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
1866
1976
  .glass-list__item:not(:last-child)::after {
1867
1977
  content: '';
1868
1978
  position: absolute;
1869
- left: calc(var(--gl-space-lg) + 28px + var(--gl-space-sm));
1870
- right: var(--gl-space-lg);
1979
+ inset-inline-start: calc(var(--gl-space-lg) + 28px + var(--gl-space-sm));
1980
+ inset-inline-end: var(--gl-space-lg);
1871
1981
  bottom: 0;
1872
1982
  height: 1px;
1873
1983
  background: var(--gl-border-subtle);
1874
1984
  pointer-events: none;
1875
1985
  }
1876
- /* Items without a leading icon: divider follows the standard horizontal
1877
- padding instead of the (non-existent) icon slot. */
1878
- .glass-list__item:not(:has(.glass-list__leading)):not(:last-child)::after {
1879
- left: var(--gl-space-lg);
1986
+ /* Items without a leading icon — no slot, or a hidden one: divider follows
1987
+ the standard horizontal padding instead of the icon slot. */
1988
+ .glass-list__item:not(:has(.glass-list__leading:not([hidden]))):not(:last-child)::after {
1989
+ inset-inline-start: var(--gl-space-lg);
1880
1990
  }
1881
- .glass-list__item:has(.glass-list__leading--lg):not(:last-child)::after {
1882
- left: calc(var(--gl-space-lg) + 40px + var(--gl-space-sm));
1991
+ .glass-list__item:has(.glass-list__leading--lg:not([hidden])):not(:last-child)::after {
1992
+ inset-inline-start: calc(var(--gl-space-lg) + 40px + var(--gl-space-sm));
1883
1993
  }
1884
1994
 
1885
1995
  .glass-list__item--center {
@@ -1925,6 +2035,10 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
1925
2035
  justify-content: center;
1926
2036
  color: var(--gl-icon-strong);
1927
2037
  }
2038
+ /* display: flex above beats the hidden attribute's own rule: an empty slot
2039
+ hidden by script kept its 28px and its gap. */
2040
+ .glass-list__leading[hidden],
2041
+ .glass-list__trailing[hidden] { display: none; }
1928
2042
  .glass-list__leading svg,
1929
2043
  .glass-list__leading ::slotted(svg) {
1930
2044
  width: 24px;
@@ -2049,7 +2163,7 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
2049
2163
  font-size: var(--gl-font-size-sm); color: var(--gl-color-text);
2050
2164
  }
2051
2165
  .glass-table th {
2052
- text-align: left; padding: 8px 10px; white-space: nowrap;
2166
+ text-align: start; padding: 8px 10px; white-space: nowrap;
2053
2167
  font-weight: var(--gl-font-weight-medium); color: var(--gl-color-text-muted);
2054
2168
  border-bottom: 1px solid var(--gl-border-medium);
2055
2169
  }
@@ -2059,7 +2173,7 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
2059
2173
  }
2060
2174
  .glass-table tbody tr:last-child td { border-bottom: 0; }
2061
2175
  .glass-table tbody tr:hover td { background: var(--gl-surface-1); }
2062
- .glass-table__num { text-align: right; font-variant-numeric: tabular-nums; }
2176
+ .glass-table__num { text-align: end; font-variant-numeric: tabular-nums; }
2063
2177
  .glass-table__muted { color: var(--gl-color-text-muted); }
2064
2178
 
2065
2179
 
@@ -2085,7 +2199,7 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
2085
2199
  .glass-prose h3 { font-size: var(--gl-font-size-lg); }
2086
2200
  .glass-prose p, .glass-prose ul, .glass-prose ol, .glass-prose pre,
2087
2201
  .glass-prose blockquote, .glass-prose table { margin: 0 0 0.9em; }
2088
- .glass-prose ul, .glass-prose ol { padding-left: 1.3em; }
2202
+ .glass-prose ul, .glass-prose ol { padding-inline-start: 1.3em; }
2089
2203
  .glass-prose strong { color: var(--gl-color-text-heading); }
2090
2204
  /* Links in running text are underlined: colour alone does not mark them. */
2091
2205
  .glass-prose a { color: var(--gl-color-primary); text-decoration: underline; text-underline-offset: 2px; }
@@ -2100,15 +2214,16 @@ a.glass-btn { display: inline-flex; text-decoration: none; }
2100
2214
  .glass-prose pre code { background: none; padding: 0; }
2101
2215
  .glass-prose blockquote {
2102
2216
  padding: 8px 14px;
2103
- border-left: 3px solid var(--gl-color-primary);
2217
+ border-inline-start: 3px solid var(--gl-color-primary);
2104
2218
  background: var(--gl-surface-1);
2105
- border-radius: 0 var(--gl-radius-xs) var(--gl-radius-xs) 0;
2219
+ border-start-start-radius: 0; border-end-start-radius: 0;
2220
+ border-start-end-radius: var(--gl-radius-xs); border-end-end-radius: var(--gl-radius-xs);
2106
2221
  }
2107
2222
  .glass-prose img { max-width: 100%; height: auto; border-radius: var(--gl-radius-sm); }
2108
2223
  .glass-prose hr { height: 1px; border: 0; margin: 1.2em 0; background: var(--gl-border-subtle); }
2109
2224
  .glass-prose table { width: 100%; border-collapse: collapse; }
2110
2225
  .glass-prose th, .glass-prose td {
2111
- padding: 6px 8px; text-align: left;
2226
+ padding: 6px 8px; text-align: start;
2112
2227
  border-bottom: 1px solid var(--gl-border-subtle);
2113
2228
  }
2114
2229