@swiftbrowser/web 0.2.0 → 0.3.1

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/src/styles.css CHANGED
@@ -63,13 +63,15 @@
63
63
  color-scheme: light;
64
64
  --sb-color-primary: #000000;
65
65
  --sb-color-secondary: rgba(60, 60, 67, 0.6);
66
- --sb-color-accent: #007aff;
66
+ --sb-color-accent: #0088ff;
67
67
  --sb-color-system-background: #ffffff;
68
68
  --sb-color-secondary-system-background: #f2f2f7;
69
69
  --sb-color-separator: rgba(60, 60, 67, 0.29);
70
+ /* iOS 26 draws the hairlines between list rows lighter than `separator`. */
71
+ --sb-color-row-separator: rgba(0, 0, 0, 0.09);
70
72
  --sb-color-tertiary-label: rgba(60, 60, 67, 0.3);
71
73
  --sb-color-quaternary-label: rgba(60, 60, 67, 0.18);
72
- --sb-color-red: #ff3b30;
74
+ --sb-color-red: #ff383c;
73
75
  --sb-color-green: #34c759;
74
76
  --sb-color-grouped-background: #f2f2f7;
75
77
  --sb-color-grouped-cell: #ffffff;
@@ -88,7 +90,11 @@
88
90
  --sb-color-system-gray6: #f2f2f7;
89
91
  /* Phase 6 materials: rgb components of the color blurred over the backdrop. */
90
92
  --sb-color-material-base: 255, 255, 255;
91
- --sb-color-switch-off: #e9e9ea;
93
+ /* iOS 26 (measured on its simulator, as the dark values below). */
94
+ --sb-color-switch-off: rgba(120, 120, 128, 0.42);
95
+ --sb-tint-fill-amount: 18%;
96
+ --sb-alert-scrim: rgba(0, 0, 0, 0.2);
97
+ --sb-alert-card: rgba(255, 255, 255, 0.7);
92
98
  --sb-color-field-border: #c6c6c8;
93
99
  --sb-color-field-background: #ffffff;
94
100
  --sb-color-placeholder: rgba(60, 60, 67, 0.3);
@@ -96,6 +102,12 @@
96
102
  --sb-color-grabber: rgba(60, 60, 67, 0.3);
97
103
  /* Phase 4 */
98
104
  --sb-color-tabbar-background: rgba(249, 249, 249, 0.94);
105
+ /* iOS 26 Liquid Glass, approximated: a near-white translucent fill over a
106
+ blurred backdrop with a soft shadow (bar buttons, the tab bar, the
107
+ bottom search field). */
108
+ --sb-glass-background: rgba(252, 252, 255, 0.86);
109
+ --sb-glass-shadow: 0 4px 24px rgba(0, 0, 0, 0.08), 0 0 0 0.5px rgba(0, 0, 0, 0.04);
110
+ --sb-glass-selected: rgba(120, 120, 128, 0.16);
99
111
  --sb-color-segment-track: rgba(118, 118, 128, 0.12);
100
112
  --sb-color-segment-selected: #ffffff;
101
113
  --sb-color-menu-background: rgba(250, 250, 250, 0.96);
@@ -105,13 +117,14 @@
105
117
  color-scheme: dark;
106
118
  --sb-color-primary: #ffffff;
107
119
  --sb-color-secondary: rgba(235, 235, 245, 0.6);
108
- --sb-color-accent: #0a84ff;
120
+ --sb-color-accent: #0091ff;
109
121
  --sb-color-system-background: #000000;
110
122
  --sb-color-secondary-system-background: #1c1c1e;
111
123
  --sb-color-separator: rgba(84, 84, 88, 0.6);
124
+ --sb-color-row-separator: rgba(84, 84, 88, 0.6);
112
125
  --sb-color-tertiary-label: rgba(235, 235, 245, 0.3);
113
126
  --sb-color-quaternary-label: rgba(235, 235, 245, 0.16);
114
- --sb-color-red: #ff453a;
127
+ --sb-color-red: #ff4245;
115
128
  --sb-color-green: #30d158;
116
129
  --sb-color-grouped-background: #000000;
117
130
  --sb-color-grouped-cell: #1c1c1e;
@@ -128,17 +141,23 @@
128
141
  --sb-color-system-gray5: #2c2c2e;
129
142
  --sb-color-system-gray6: #1c1c1e;
130
143
  --sb-color-material-base: 30, 30, 30;
131
- --sb-color-switch-off: #39393d;
144
+ --sb-color-switch-off: rgba(120, 120, 128, 0.68);
145
+ --sb-tint-fill-amount: 25%;
146
+ --sb-alert-scrim: rgba(0, 0, 0, 0.46);
147
+ --sb-alert-card: rgba(22, 22, 23, 0.9);
132
148
  --sb-color-field-border: #3a3a3c;
133
149
  --sb-color-field-background: #1c1c1e;
134
150
  --sb-color-placeholder: rgba(235, 235, 245, 0.3);
151
+ --sb-glass-background: rgba(30, 30, 30, 0.84);
152
+ --sb-glass-shadow: 0 4px 24px rgba(0, 0, 0, 0.4), 0 0 0 0.5px rgba(255, 255, 255, 0.08);
153
+ --sb-glass-selected: rgba(120, 120, 128, 0.32);
135
154
  /* Sheets float above the window and take the elevated system background. */
136
155
  --sb-color-sheet-background: #1c1c1e;
137
156
  --sb-color-grabber: rgba(235, 235, 245, 0.3);
138
157
  /* Phase 4 */
139
158
  --sb-color-tabbar-background: rgba(29, 29, 29, 0.94);
140
159
  --sb-color-segment-track: rgba(118, 118, 128, 0.24);
141
- --sb-color-segment-selected: #636366;
160
+ --sb-color-segment-selected: #5a5a5f;
142
161
  --sb-color-menu-background: rgba(37, 37, 37, 0.96);
143
162
  }
144
163
 
@@ -353,7 +372,7 @@ body {
353
372
  position: absolute;
354
373
  top: 11px;
355
374
  left: 50%;
356
- width: 126px;
375
+ width: 125px;
357
376
  height: 37px;
358
377
  transform: translateX(-50%);
359
378
  border-radius: 20px;
@@ -370,23 +389,30 @@ body {
370
389
  display: flex;
371
390
  align-items: center;
372
391
  justify-content: space-between;
373
- padding: 0 32px 6px 40px;
392
+ /* As iOS 26 draws it (measured on an iPhone 16 simulator): the row is
393
+ centered 29.5pt down, the time centered 70pt from the leading edge, the
394
+ glyphs 7.67pt apart ending 32pt from the trailing edge. */
395
+ padding: 0 32px 0 38px;
374
396
  pointer-events: none;
375
397
  z-index: 2;
376
398
  color: var(--sb-color-primary);
377
399
  }
378
400
 
379
401
  .status-time {
402
+ min-width: 64px;
403
+ text-align: center;
404
+ /* The digits' optical center, not the line box's, sits on the row's. */
405
+ position: relative;
406
+ top: 1.67px;
380
407
  font-size: 17px;
381
408
  font-weight: 600;
382
409
  letter-spacing: -0.02em;
383
- font-variant-numeric: tabular-nums;
384
410
  }
385
411
 
386
412
  .status-glyphs {
387
413
  display: flex;
388
414
  align-items: center;
389
- gap: 7px;
415
+ gap: 7.67px;
390
416
  }
391
417
 
392
418
  .glyph {
@@ -394,6 +420,12 @@ body {
394
420
  fill: currentColor;
395
421
  }
396
422
 
423
+ .glyph-wifi .wifi-band {
424
+ fill: none;
425
+ stroke: currentColor;
426
+ stroke-linecap: round;
427
+ }
428
+
397
429
  .glyph-battery .battery-shell {
398
430
  fill: none;
399
431
  stroke: currentColor;
@@ -623,7 +655,8 @@ html[data-sb-mode="device"] .status {
623
655
  }
624
656
 
625
657
  .sb-button[data-sb-style="bordered"] {
626
- background: var(--sb-color-fill);
658
+ /* iOS 26: secondarySystemFill, or the view's tint at 18% (dark 25%). */
659
+ background: var(--sb-tint-fill, var(--sb-color-secondary-fill));
627
660
  color: var(--sb-button-color, var(--sb-tint, var(--sb-color-accent)));
628
661
  }
629
662
 
@@ -641,6 +674,18 @@ html[data-sb-mode="device"] .status {
641
674
  color: #ffffff;
642
675
  }
643
676
 
677
+ /* iOS 26 draws a disabled bordered button gray rather than faded
678
+ (`.disabled` marks the element around the button and dims it inline; the
679
+ renderer keeps the undimmed opacity in `--sb-undimmed-opacity`). */
680
+ [data-sb-disabled]:has(> .sb-button:is([data-sb-style="bordered"], [data-sb-style="borderedProminent"])) {
681
+ opacity: var(--sb-undimmed-opacity, 1) !important;
682
+ }
683
+
684
+ [data-sb-disabled] > .sb-button:is([data-sb-style="bordered"], [data-sb-style="borderedProminent"]) {
685
+ background: var(--sb-color-secondary-fill);
686
+ color: var(--sb-color-tertiary-label);
687
+ }
688
+
644
689
  /* .disabled(true): opacity and pointer-events are set inline by the renderer
645
690
  (so update() clears them); the attribute carries the state for tooling. */
646
691
  [data-sb-disabled] {
@@ -663,6 +708,47 @@ html[data-sb-mode="device"] .status {
663
708
  stroke: currentColor;
664
709
  }
665
710
 
711
+ /* An indeterminate ProgressView: the activity indicator (symbols.ts), in
712
+ the secondary label color (or the view's tint). As on iOS the spokes stay
713
+ put and the fade steps around them clockwise, once a second: each spoke
714
+ runs the same ramp an eighth of a second after the one before it.
715
+ (Rotating the glyph instead resamples it at every step, which wobbles.) */
716
+ svg[data-sb-symbol="progress.indicator"] {
717
+ color: var(--sb-tint, var(--sb-color-secondary));
718
+ }
719
+
720
+ svg[data-sb-symbol="progress.indicator"] > line {
721
+ animation: sb-activity-spoke 1s step-end infinite;
722
+ }
723
+
724
+ svg[data-sb-symbol="progress.indicator"] > line:nth-child(2) { animation-delay: -0.875s; }
725
+ svg[data-sb-symbol="progress.indicator"] > line:nth-child(3) { animation-delay: -0.75s; }
726
+ svg[data-sb-symbol="progress.indicator"] > line:nth-child(4) { animation-delay: -0.625s; }
727
+ svg[data-sb-symbol="progress.indicator"] > line:nth-child(5) { animation-delay: -0.5s; }
728
+ svg[data-sb-symbol="progress.indicator"] > line:nth-child(6) { animation-delay: -0.375s; }
729
+ svg[data-sb-symbol="progress.indicator"] > line:nth-child(7) { animation-delay: -0.25s; }
730
+ svg[data-sb-symbol="progress.indicator"] > line:nth-child(8) { animation-delay: -0.125s; }
731
+
732
+ /* A spoke's strength from the moment it leads: the ramp symbols.ts draws. */
733
+ @keyframes sb-activity-spoke {
734
+ 0% {
735
+ stroke-opacity: 1;
736
+ }
737
+ 12.5% {
738
+ stroke-opacity: 0.83;
739
+ }
740
+ 25% {
741
+ stroke-opacity: 0.66;
742
+ }
743
+ 37.5% {
744
+ stroke-opacity: 0.49;
745
+ }
746
+ 50%,
747
+ 100% {
748
+ stroke-opacity: 0.31;
749
+ }
750
+ }
751
+
666
752
  /* Unknown symbol or catalog image: a dashed box named by `title`. */
667
753
  .sb-image[data-sb-missing] {
668
754
  border: 1px dashed currentColor;
@@ -801,8 +887,9 @@ html[data-sb-mode="device"] .status {
801
887
  .sb-switch {
802
888
  appearance: none;
803
889
  position: absolute;
804
- width: 51px;
805
- height: 31px;
890
+ /* iOS 26: 63 × 28 with a pill knob. */
891
+ width: 63px;
892
+ height: 28px;
806
893
  margin: 0;
807
894
  padding: 0;
808
895
  border: 0;
@@ -827,9 +914,9 @@ html[data-sb-mode="device"] .status {
827
914
  position: absolute;
828
915
  top: 2px;
829
916
  left: 2px;
830
- width: 27px;
831
- height: 27px;
832
- border-radius: 50%;
917
+ width: 37px;
918
+ height: 24px;
919
+ border-radius: 12px;
833
920
  background: #ffffff;
834
921
  box-shadow:
835
922
  0 3px 8px rgba(0, 0, 0, 0.15),
@@ -838,7 +925,7 @@ html[data-sb-mode="device"] .status {
838
925
  }
839
926
 
840
927
  .sb-switch[aria-checked="true"] .sb-switch-knob {
841
- transform: translateX(20px);
928
+ transform: translateX(22px);
842
929
  }
843
930
 
844
931
  /* ---------------------------------------------------------------------------
@@ -869,7 +956,8 @@ html[data-sb-mode="device"] .status {
869
956
 
870
957
  .sb-section-rows {
871
958
  position: absolute;
872
- border-radius: 10px;
959
+ /* iOS 26's inset grouped cards. */
960
+ border-radius: 26px;
873
961
  background: var(--sb-color-grouped-cell);
874
962
  overflow: hidden;
875
963
  }
@@ -885,14 +973,14 @@ html[data-sb-mode="device"] .status {
885
973
 
886
974
  .sb-list-content > :not(.sb-section):first-child,
887
975
  .sb-list-content > .sb-section + :not(.sb-section) {
888
- border-top-left-radius: 10px;
889
- border-top-right-radius: 10px;
976
+ border-top-left-radius: 26px;
977
+ border-top-right-radius: 26px;
890
978
  }
891
979
 
892
980
  .sb-list-content > :not(.sb-section):last-child,
893
981
  .sb-list-content > :not(.sb-section):has(+ .sb-section) {
894
- border-bottom-left-radius: 10px;
895
- border-bottom-right-radius: 10px;
982
+ border-bottom-left-radius: 26px;
983
+ border-bottom-right-radius: 26px;
896
984
  }
897
985
 
898
986
  /* Lazy rows not built yet (a trailing `lazyspacer`): no cell of their own, and
@@ -903,8 +991,8 @@ html[data-sb-mode="device"] .status {
903
991
  }
904
992
 
905
993
  .sb-list-content > :not(.sb-section):has(+ .sb-lazyspacer:last-child) {
906
- border-bottom-left-radius: 10px;
907
- border-bottom-right-radius: 10px;
994
+ border-bottom-left-radius: 26px;
995
+ border-bottom-right-radius: 26px;
908
996
  }
909
997
 
910
998
  .sb-section-rows > :has(+ .sb-lazyspacer:last-child)::after,
@@ -923,19 +1011,38 @@ html[data-sb-mode="device"] .status {
923
1011
  display: none !important;
924
1012
  }
925
1013
 
926
- /* Hairline separators, inset 16px from the leading edge. */
1014
+ /* Separators: iOS 26 starts the line at the row's first text (the engine's
1015
+ `--sb-separator-leading`, else 16px) and ends it 16px before the trailing
1016
+ edge. */
927
1017
  .sb-section-rows > :not(:last-child)::after,
928
1018
  .sb-list-content > :not(.sb-section):not(:last-child):not(:has(+ .sb-section))::after {
929
1019
  content: "";
930
1020
  position: absolute;
931
- left: 16px;
932
- right: 0;
1021
+ left: var(--sb-separator-leading, 16px);
1022
+ right: 16px;
933
1023
  bottom: 0;
934
1024
  height: 1px;
935
- background: var(--sb-color-separator);
1025
+ background: var(--sb-color-row-separator);
936
1026
  pointer-events: none;
937
1027
  }
938
1028
 
1029
+ /* A text field row is the <input> (or <textarea>) itself, which has no
1030
+ ::after: its separator is a 1px background image in the same place. */
1031
+ .sb-section-rows > :is(input, textarea):not(:last-child),
1032
+ .sb-list-content > :is(input, textarea):not(:last-child):not(:has(+ .sb-section)) {
1033
+ background-image: linear-gradient(var(--sb-color-row-separator), var(--sb-color-row-separator));
1034
+ background-repeat: no-repeat;
1035
+ background-position: var(--sb-separator-leading, 16px) 100%;
1036
+ background-size: calc(100% - var(--sb-separator-leading, 16px) - 16px) 1px;
1037
+ }
1038
+
1039
+ .sb-section-rows > :is(input, textarea)[data-sb-separator-bottom="hidden"],
1040
+ .sb-list-content > :is(input, textarea)[data-sb-separator-bottom="hidden"],
1041
+ .sb-section-rows > :is(input, textarea):has(+ [data-sb-separator-top="hidden"]),
1042
+ .sb-list-content > :is(input, textarea):has(+ [data-sb-separator-top="hidden"]) {
1043
+ background-image: none;
1044
+ }
1045
+
939
1046
  .sb-section-rows > .sb-button,
940
1047
  .sb-list-content > .sb-button {
941
1048
  border-radius: 0;
@@ -1024,7 +1131,7 @@ html[data-sb-mode="device"] .status {
1024
1131
  display: grid;
1025
1132
  grid-template-columns: 1fr auto 1fr;
1026
1133
  align-items: center;
1027
- padding: 0 8px;
1134
+ padding: 0 16px;
1028
1135
  box-sizing: border-box;
1029
1136
  background: transparent;
1030
1137
  /* Phase 8: content that ignores the safe area paints under the bar; the
@@ -1047,14 +1154,21 @@ html[data-sb-mode="device"] .status {
1047
1154
  justify-self: start;
1048
1155
  display: inline-flex;
1049
1156
  align-items: center;
1157
+ justify-content: center;
1050
1158
  gap: 2px;
1051
- /* Width comes from the layout engine (chrome.back); this is only a fallback. */
1159
+ /* iOS 26: a 44pt glass circle with the chevron (chrome.back). */
1160
+ width: 44px;
1161
+ height: 44px;
1052
1162
  max-width: 45%;
1053
1163
  margin: 0;
1054
1164
  padding: 0;
1055
1165
  border: 0;
1056
- background: none;
1057
- color: var(--sb-color-accent);
1166
+ border-radius: 22px;
1167
+ background: var(--sb-glass-background);
1168
+ box-shadow: var(--sb-glass-shadow);
1169
+ backdrop-filter: blur(12px) saturate(180%);
1170
+ -webkit-backdrop-filter: blur(12px) saturate(180%);
1171
+ color: var(--sb-color-primary);
1058
1172
  font: inherit;
1059
1173
  font-size: var(--sb-font-size-body);
1060
1174
  line-height: 1.3;
@@ -1074,11 +1188,15 @@ html[data-sb-mode="device"] .status {
1074
1188
  .sb-navbar-back > svg {
1075
1189
  width: 1.4em;
1076
1190
  height: 1.4em;
1077
- margin-left: -4px;
1078
- stroke-width: 2.5;
1191
+ margin-left: -2px;
1192
+ stroke-width: 2.25;
1079
1193
  flex-shrink: 0;
1080
1194
  }
1081
1195
 
1196
+ .sb-navbar-back-label:empty {
1197
+ display: none;
1198
+ }
1199
+
1082
1200
  .sb-navbar-back-label {
1083
1201
  overflow: hidden;
1084
1202
  text-overflow: ellipsis;
@@ -1116,14 +1234,16 @@ html[data-sb-mode="device"] .status {
1116
1234
  display: block;
1117
1235
  }
1118
1236
 
1119
- /* Large title: the engine's `largeTitle` rect (52px row), text already
1237
+ /* Large title: the engine's `largeTitle` rect (62px row), text already
1120
1238
  truncated to one line. */
1121
1239
  .sb-navbar-large-title {
1122
1240
  position: absolute;
1123
1241
  z-index: 1; /* Phase 8: above content that ignores the safe area */
1124
1242
  display: flex;
1125
- align-items: center;
1126
- padding: 0 16px;
1243
+ /* iOS 26: the title's line starts 13 into its 62px row. */
1244
+ align-items: flex-start;
1245
+ line-height: 41px;
1246
+ padding: 13px 16px 0;
1127
1247
  box-sizing: border-box;
1128
1248
  font-size: var(--sb-ts-largeTitle-size, 34px);
1129
1249
  font-weight: 700;
@@ -1138,6 +1258,30 @@ html[data-sb-mode="device"] .status {
1138
1258
  display: none;
1139
1259
  }
1140
1260
 
1261
+ /* The large title scrolls with the screen's root list or scroll view
1262
+ (src/largeTitle.ts moves it); drags on it scroll that view. */
1263
+ .sb-navbar-large-title {
1264
+ pointer-events: none;
1265
+ }
1266
+
1267
+ /* Until the large title has scrolled under the bar, the inline title (whose
1268
+ rect the engine reports for a title that can collapse) stays hidden. */
1269
+ .sb-navscreen[data-sb-display="large"] > .sb-navbar > .sb-navbar-title {
1270
+ opacity: 0;
1271
+ transition: opacity 0.15s ease;
1272
+ }
1273
+
1274
+ .sb-navscreen[data-sb-display="large"][data-sb-collapsed] > .sb-navbar > .sb-navbar-title {
1275
+ opacity: 1;
1276
+ }
1277
+
1278
+ /* iOS 26's scroll edge effect, simplified: scrolled content fades in over
1279
+ 16px below the bar instead of being cut off at it (src/largeTitle.ts). */
1280
+ [data-sb-scrolls-title][data-sb-scrolled] {
1281
+ -webkit-mask-image: linear-gradient(to bottom, transparent, #000 16px);
1282
+ mask-image: linear-gradient(to bottom, transparent, #000 16px);
1283
+ }
1284
+
1141
1285
  /* NavigationLink: a tappable label. Inside a list it is a row with a chevron
1142
1286
  at the engine's `chevron` rect, its label in the primary color (also when
1143
1287
  the row is wrapped: a selectable or deletable `listrow`, a modifier);
@@ -1239,6 +1383,26 @@ html[data-sb-mode="device"] .status {
1239
1383
  transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
1240
1384
  }
1241
1385
 
1386
+ /* iOS 26: a sheet at a partial detent floats inset from the screen edges,
1387
+ rounded all around, over a lighter dim. */
1388
+ .sb-sheet[data-sb-floating] > .sb-sheet-card {
1389
+ border-radius: 38px;
1390
+ background: color-mix(in srgb, var(--sb-color-sheet-background) 55%, transparent);
1391
+ backdrop-filter: blur(24px) saturate(180%);
1392
+ -webkit-backdrop-filter: blur(24px) saturate(180%);
1393
+ box-shadow: 0 8px 40px rgba(0, 0, 0, 0.18);
1394
+ }
1395
+
1396
+ /* The sheet's glass shows through a navigation stack in it, as on iOS 26
1397
+ (an opaque screen would paint over it). */
1398
+ .sb-sheet[data-sb-floating] .sb-navscreen {
1399
+ background: transparent;
1400
+ }
1401
+
1402
+ .sb-sheet[data-sb-floating] > .sb-sheet-scrim {
1403
+ background: rgba(0, 0, 0, 0.22);
1404
+ }
1405
+
1242
1406
  .sb-sheet-grabber {
1243
1407
  position: absolute;
1244
1408
  border-radius: 2.5px;
@@ -1378,8 +1542,8 @@ html[data-sb-mode="device"] .status {
1378
1542
  /* ---------------------------------------------------------------------------
1379
1543
  Phase 4: tab views. The tabs are stacked in the content slot (every tab has
1380
1544
  the tab view's frame); the selected one is visible, the others keep their
1381
- DOM hidden. The bar is the engine's `bar` rect (49 + bottom safe area):
1382
- translucent system background, hairline on top, items in the 49px row.
1545
+ DOM hidden. The bar is the engine's `bar` rect: iOS 26's glass capsule
1546
+ floating over the content, the selected item on a gray pill.
1383
1547
  ------------------------------------------------------------------------- */
1384
1548
 
1385
1549
  .sb-tabview {
@@ -1403,35 +1567,27 @@ html[data-sb-mode="device"] .status {
1403
1567
  .sb-tabbar {
1404
1568
  position: absolute;
1405
1569
  z-index: 3;
1406
- background: var(--sb-color-tabbar-background);
1570
+ border-radius: 31px;
1571
+ background: var(--sb-glass-background);
1572
+ box-shadow: var(--sb-glass-shadow);
1407
1573
  backdrop-filter: blur(20px) saturate(180%);
1408
1574
  -webkit-backdrop-filter: blur(20px) saturate(180%);
1409
1575
  }
1410
1576
 
1411
- .sb-tabbar::before {
1412
- content: "";
1413
- position: absolute;
1414
- left: 0;
1415
- right: 0;
1416
- top: 0;
1417
- height: 1px;
1418
- background: var(--sb-color-separator);
1419
- pointer-events: none;
1420
- }
1421
-
1422
1577
  .sb-tabbar-item {
1423
1578
  appearance: none;
1424
1579
  position: absolute;
1425
1580
  display: flex;
1426
1581
  flex-direction: column;
1427
1582
  align-items: center;
1428
- justify-content: flex-start;
1429
- gap: 2px;
1583
+ justify-content: center;
1584
+ gap: 1px;
1430
1585
  margin: 0;
1431
- padding: 6px 0 0;
1586
+ padding: 0;
1432
1587
  border: 0;
1588
+ border-radius: 27px;
1433
1589
  background: none;
1434
- color: var(--sb-color-secondary);
1590
+ color: var(--sb-color-primary);
1435
1591
  font-family: var(--sb-font-family);
1436
1592
  cursor: pointer;
1437
1593
  touch-action: manipulation;
@@ -1440,6 +1596,7 @@ html[data-sb-mode="device"] .status {
1440
1596
 
1441
1597
  .sb-tabbar-item[aria-selected="true"] {
1442
1598
  color: var(--sb-color-accent);
1599
+ background: var(--sb-glass-selected);
1443
1600
  }
1444
1601
 
1445
1602
  .sb-tabbar-item:focus-visible {
@@ -1479,7 +1636,7 @@ html[data-sb-mode="device"] .status {
1479
1636
  .sb-tabbar-title {
1480
1637
  font-size: 10px;
1481
1638
  line-height: 12px;
1482
- font-weight: 500;
1639
+ font-weight: 600;
1483
1640
  letter-spacing: -0.01em;
1484
1641
  white-space: nowrap;
1485
1642
  }
@@ -1523,7 +1680,8 @@ html[data-sb-mode="device"] .status {
1523
1680
  box is the whole row, so the track cannot be the element's background). */
1524
1681
  .sb-segment-track {
1525
1682
  position: absolute;
1526
- border-radius: 8px;
1683
+ /* iOS 26: a capsule track with a capsule for the selected segment. */
1684
+ border-radius: 9999px;
1527
1685
  background: var(--sb-color-segment-track);
1528
1686
  pointer-events: none;
1529
1687
  }
@@ -1551,7 +1709,7 @@ html[data-sb-mode="device"] .status {
1551
1709
  content: "";
1552
1710
  position: absolute;
1553
1711
  inset: 2px;
1554
- border-radius: 7px;
1712
+ border-radius: 9999px;
1555
1713
  transition: background-color 0.15s ease, box-shadow 0.15s ease;
1556
1714
  }
1557
1715
 
@@ -1958,7 +2116,7 @@ html[data-sb-mode="device"] .status {
1958
2116
  right: 0;
1959
2117
  top: 0;
1960
2118
  height: 1px;
1961
- background: var(--sb-color-separator);
2119
+ background: var(--sb-color-row-separator);
1962
2120
  pointer-events: none;
1963
2121
  }
1964
2122
 
@@ -1981,6 +2139,25 @@ html[data-sb-mode="device"] .status {
1981
2139
  display: block;
1982
2140
  }
1983
2141
 
2142
+ /* iOS 26: a bar's leading and trailing items share a glass capsule (the
2143
+ engine sizes the group: 44 tall, each item in a slot at least 44 wide),
2144
+ drawn in the label color unless tinted. */
2145
+ .sb-toolbar[data-sb-placement="leading"],
2146
+ .sb-toolbar[data-sb-placement="trailing"] {
2147
+ border-radius: 22px;
2148
+ background: var(--sb-glass-background);
2149
+ box-shadow: var(--sb-glass-shadow);
2150
+ backdrop-filter: blur(12px) saturate(180%);
2151
+ -webkit-backdrop-filter: blur(12px) saturate(180%);
2152
+ --sb-color-accent: var(--sb-color-primary);
2153
+ }
2154
+
2155
+ /* Their symbols are drawn larger and heavier, as iOS 26 draws bar buttons. */
2156
+ .sb-toolbar:is([data-sb-placement="leading"], [data-sb-placement="trailing"]) .sb-image > svg {
2157
+ transform: scale(1.25);
2158
+ stroke-width: 2.1;
2159
+ }
2160
+
1984
2161
  .sb-toolbar[data-sb-placement="bottom"] {
1985
2162
  z-index: 3;
1986
2163
  background: var(--sb-color-tabbar-background);
@@ -1999,16 +2176,21 @@ html[data-sb-mode="device"] .status {
1999
2176
  pointer-events: none;
2000
2177
  }
2001
2178
 
2002
- /* Search field: a 36px rounded gray field (engine `searchField` rect) with a
2179
+ /* Search field: a capsule (engine `searchField` rect) with a
2003
2180
  magnifier leading; the input sends `text` events like a textfield. */
2004
2181
  .sb-searchfield {
2005
2182
  display: flex;
2006
2183
  align-items: center;
2007
- gap: 6px;
2008
- padding: 0 8px;
2009
- border-radius: 10px;
2010
- background: var(--sb-color-fill);
2184
+ gap: 8px;
2185
+ padding: 0 16px;
2186
+ /* iOS 26: a glass capsule floating at the bottom (engine `searchField`). */
2187
+ border-radius: 9999px;
2188
+ background: var(--sb-glass-background);
2189
+ box-shadow: var(--sb-glass-shadow);
2190
+ backdrop-filter: blur(20px) saturate(180%);
2191
+ -webkit-backdrop-filter: blur(20px) saturate(180%);
2011
2192
  color: var(--sb-color-primary);
2193
+ z-index: 2;
2012
2194
  }
2013
2195
 
2014
2196
  .sb-searchfield > svg {
@@ -2166,33 +2348,34 @@ html[data-sb-mode="device"] .status {
2166
2348
  }
2167
2349
 
2168
2350
  /* The SwiftUI chart palette (and the other system colors) as `--sb-color-<name>`,
2169
- which a named `Color` without a semantic token resolves to (src/styles.ts). */
2351
+ which a named `Color` without a semantic token resolves to (src/styles.ts).
2352
+ iOS 26's palette (measured in the iOS 26.2 simulator, Examples/Fidelity). */
2170
2353
  .device,
2171
2354
  .device[data-theme="light"] {
2172
- --sb-color-blue: #007aff;
2173
- --sb-color-orange: #ff9500;
2355
+ --sb-color-blue: #0088ff;
2356
+ --sb-color-orange: #ff8d28;
2174
2357
  --sb-color-yellow: #ffcc00;
2175
- --sb-color-purple: #af52de;
2358
+ --sb-color-purple: #cb30e0;
2176
2359
  --sb-color-pink: #ff2d55;
2177
- --sb-color-teal: #30b0c7;
2178
- --sb-color-indigo: #5856d6;
2179
- --sb-color-mint: #00c7be;
2180
- --sb-color-cyan: #32ade6;
2181
- --sb-color-brown: #a2845e;
2360
+ --sb-color-teal: #00c3d0;
2361
+ --sb-color-indigo: #6155f5;
2362
+ --sb-color-mint: #00c8b3;
2363
+ --sb-color-cyan: #00c0e8;
2364
+ --sb-color-brown: #ac7f5e;
2182
2365
  --sb-color-gray: #8e8e93;
2183
2366
  }
2184
2367
 
2185
2368
  .device[data-theme="dark"] {
2186
- --sb-color-blue: #0a84ff;
2187
- --sb-color-orange: #ff9f0a;
2188
- --sb-color-yellow: #ffd60a;
2189
- --sb-color-purple: #bf5af2;
2369
+ --sb-color-blue: #0091ff;
2370
+ --sb-color-orange: #ff9230;
2371
+ --sb-color-yellow: #ffd600;
2372
+ --sb-color-purple: #db34f2;
2190
2373
  --sb-color-pink: #ff375f;
2191
- --sb-color-teal: #40c8e0;
2192
- --sb-color-indigo: #5e5ce6;
2193
- --sb-color-mint: #63e6e2;
2194
- --sb-color-cyan: #64d2ff;
2195
- --sb-color-brown: #ac8e68;
2374
+ --sb-color-teal: #00d2e0;
2375
+ --sb-color-indigo: #6d7cff;
2376
+ --sb-color-mint: #00dac3;
2377
+ --sb-color-cyan: #3cd3fe;
2378
+ --sb-color-brown: #b78a66;
2196
2379
  --sb-color-gray: #8e8e93;
2197
2380
  }
2198
2381
 
@@ -2603,7 +2786,7 @@ html[data-sb-mode="device"] .status {
2603
2786
  .sb-alert-scrim {
2604
2787
  position: absolute;
2605
2788
  inset: 0;
2606
- background: rgba(0, 0, 0, 0.3);
2789
+ background: var(--sb-alert-scrim);
2607
2790
  animation: sb-alert-fade-in 0.25s ease-out both;
2608
2791
  }
2609
2792
 
@@ -2661,6 +2844,13 @@ html[data-sb-mode="device"] .status {
2661
2844
  background: var(--sb-color-sheet-background);
2662
2845
  }
2663
2846
 
2847
+ /* iOS 26: the alert is a rounder glass card. */
2848
+ .sb-alert[data-sb-style="alert"] .sb-alert-card {
2849
+ border-radius: 34px;
2850
+ background: var(--sb-alert-card);
2851
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.12);
2852
+ }
2853
+
2664
2854
  .sb-alert-title,
2665
2855
  .sb-alert-message {
2666
2856
  position: absolute;
@@ -2671,6 +2861,16 @@ html[data-sb-mode="device"] .status {
2671
2861
  overflow: hidden;
2672
2862
  }
2673
2863
 
2864
+ /* iOS 26: an alert's title and message are leading-aligned, the message dim. */
2865
+ .sb-alert[data-sb-style="alert"] .sb-alert-title,
2866
+ .sb-alert[data-sb-style="alert"] .sb-alert-message {
2867
+ text-align: left;
2868
+ }
2869
+
2870
+ .sb-alert[data-sb-style="alert"] .sb-alert-message {
2871
+ color: var(--sb-color-secondary);
2872
+ }
2873
+
2674
2874
  /* The action sheet's title and message are a dim caption. */
2675
2875
  .sb-alert[data-sb-style="dialog"] .sb-alert-title,
2676
2876
  .sb-alert[data-sb-style="dialog"] .sb-alert-message {
@@ -2711,11 +2911,32 @@ html[data-sb-mode="device"] .status {
2711
2911
  pointer-events: none;
2712
2912
  }
2713
2913
 
2714
- .sb-alert-buttons > .sb-button[data-sb-role="cancel"] {
2914
+ /* iOS 26: an alert's buttons are gray capsules, in the label color. */
2915
+ .sb-alert[data-sb-style="alert"] .sb-alert-buttons > .sb-button,
2916
+ .sb-alert[data-sb-style="alert"] .sb-alert-ok {
2917
+ border-radius: 9999px;
2918
+ background: var(--sb-color-fill);
2919
+ color: var(--sb-color-primary);
2920
+ }
2921
+
2922
+ .sb-alert[data-sb-style="alert"] .sb-alert-buttons > .sb-button::before,
2923
+ .sb-alert[data-sb-style="alert"] .sb-alert-buttons > .sb-button::after,
2924
+ .sb-alert[data-sb-style="alert"] .sb-alert-ok::before {
2925
+ display: none;
2926
+ }
2927
+
2928
+ .sb-alert[data-sb-style="alert"] .sb-alert-buttons > .sb-button:active,
2929
+ .sb-alert[data-sb-style="alert"] .sb-alert-ok:active {
2930
+ background: var(--sb-color-secondary-fill);
2931
+ filter: brightness(0.92);
2932
+ }
2933
+
2934
+ .sb-alert[data-sb-style="dialog"] .sb-alert-buttons > .sb-button[data-sb-role="cancel"] {
2715
2935
  font-weight: 600;
2716
2936
  }
2717
2937
 
2718
- .sb-alert-buttons > .sb-button[data-sb-role="destructive"] {
2938
+ .sb-alert-buttons > .sb-button[data-sb-role="destructive"],
2939
+ .sb-alert[data-sb-style="alert"] .sb-alert-buttons > .sb-button[data-sb-role="destructive"] {
2719
2940
  color: var(--sb-color-red);
2720
2941
  }
2721
2942
 
@@ -2796,14 +3017,14 @@ html[data-sb-mode="device"] .status {
2796
3017
 
2797
3018
  /* ---------------------------------------------------------------------------
2798
3019
  Stepper: the label children are positioned by their frames; the 94×32
2799
- segmented control sits at the engine's `control` rect, its halves divided
2800
- by a hairline. A dimmed half (`aria-disabled`) swallows taps.
3020
+ capsule (iOS 26) sits at the engine's `control` rect, its halves divided
3021
+ by a short line. A dimmed half (`aria-disabled`) swallows taps.
2801
3022
  ------------------------------------------------------------------------- */
2802
3023
  .sb-stepper-control {
2803
3024
  position: absolute;
2804
3025
  display: flex;
2805
3026
  box-sizing: border-box;
2806
- border-radius: 8px;
3027
+ border-radius: 16px;
2807
3028
  background: var(--sb-color-tertiary-fill);
2808
3029
  overflow: hidden;
2809
3030
  }
@@ -2827,7 +3048,17 @@ html[data-sb-mode="device"] .status {
2827
3048
  }
2828
3049
 
2829
3050
  .sb-stepper-button + .sb-stepper-button {
2830
- box-shadow: -1px 0 0 var(--sb-color-separator);
3051
+ position: relative;
3052
+ }
3053
+
3054
+ .sb-stepper-button + .sb-stepper-button::before {
3055
+ content: "";
3056
+ position: absolute;
3057
+ left: 0;
3058
+ top: 7px;
3059
+ bottom: 7px;
3060
+ width: 1px;
3061
+ background: var(--sb-color-opaque-separator);
2831
3062
  }
2832
3063
 
2833
3064
  .sb-stepper-button > svg {
@@ -2848,8 +3079,9 @@ html[data-sb-mode="device"] .status {
2848
3079
  }
2849
3080
 
2850
3081
  /* ---------------------------------------------------------------------------
2851
- Slider: the label children are positioned by their frames; the 4pt track,
2852
- the accent fill and the 27pt thumb are chrome at the engine's rects. A
3082
+ Slider: the label children are positioned by their frames; the 6pt track,
3083
+ the accent fill and the 38 × 24 pill thumb (iOS 26) are chrome at the
3084
+ engine's rects. A
2853
3085
  pointer anywhere on the slider moves the thumb (touch-action none).
2854
3086
  ------------------------------------------------------------------------- */
2855
3087
  .sb-slider {
@@ -2860,20 +3092,20 @@ html[data-sb-mode="device"] .status {
2860
3092
 
2861
3093
  .sb-slider-track {
2862
3094
  position: absolute;
2863
- border-radius: 2px;
3095
+ border-radius: 3px;
2864
3096
  background: var(--sb-color-tertiary-fill);
2865
3097
  }
2866
3098
 
2867
3099
  .sb-slider-fill {
2868
3100
  position: absolute;
2869
- border-radius: 2px;
3101
+ border-radius: 3px;
2870
3102
  background: var(--sb-tint, var(--sb-color-accent));
2871
3103
  }
2872
3104
 
2873
3105
  .sb-slider-thumb {
2874
3106
  position: absolute;
2875
3107
  box-sizing: border-box;
2876
- border-radius: 50%;
3108
+ border-radius: 12px;
2877
3109
  background: #ffffff;
2878
3110
  box-shadow:
2879
3111
  0 3px 8px rgba(0, 0, 0, 0.15),
@@ -3004,8 +3236,9 @@ textarea.sb-textfield[data-sb-style="roundedBorder"] {
3004
3236
  padding: 5px 7px;
3005
3237
  }
3006
3238
 
3007
- /* A section's view header / footer: footnote-sized secondary text above or
3008
- below the card (the engine sets the fonts of the texts inside). */
3239
+ /* A section's view header / footer: secondary text above or below the card
3240
+ (the engine sets the fonts of the texts inside: headline headers, footnote
3241
+ footers). */
3009
3242
  .sb-sectionheader,
3010
3243
  .sb-sectionfooter {
3011
3244
  color: var(--sb-color-secondary);