@swiftbrowser/web 0.1.0 → 0.3.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/README.md +141 -63
- package/app/index.html +21 -10
- package/node/dist/node/apps.d.ts +16 -0
- package/node/dist/node/apps.d.ts.map +1 -1
- package/node/dist/node/apps.js +9 -0
- package/node/dist/node/apps.js.map +1 -1
- package/node/dist/node/config.d.ts +14 -1
- package/node/dist/node/config.d.ts.map +1 -1
- package/node/dist/node/config.js +20 -3
- package/node/dist/node/config.js.map +1 -1
- package/node/dist/node/examples.d.ts +2 -1
- package/node/dist/node/examples.d.ts.map +1 -1
- package/node/dist/node/examples.js +12 -2
- package/node/dist/node/examples.js.map +1 -1
- package/node/dist/plugins/app-manifests.d.ts +4 -1
- package/node/dist/plugins/app-manifests.d.ts.map +1 -1
- package/node/dist/plugins/app-manifests.js +3 -2
- package/node/dist/plugins/app-manifests.js.map +1 -1
- package/node/dist/plugins/asset-catalogs.d.ts +15 -3
- package/node/dist/plugins/asset-catalogs.d.ts.map +1 -1
- package/node/dist/plugins/asset-catalogs.js +111 -13
- package/node/dist/plugins/asset-catalogs.js.map +1 -1
- package/node/dist/plugins/included-sites.d.ts +26 -0
- package/node/dist/plugins/included-sites.d.ts.map +1 -0
- package/node/dist/plugins/included-sites.js +70 -0
- package/node/dist/plugins/included-sites.js.map +1 -0
- package/node/dist/plugins/pdfImages.d.ts +27 -0
- package/node/dist/plugins/pdfImages.d.ts.map +1 -0
- package/node/dist/plugins/pdfImages.js +145 -0
- package/node/dist/plugins/pdfImages.js.map +1 -0
- package/node/dist/plugins/swift-wasm.d.ts +19 -1
- package/node/dist/plugins/swift-wasm.d.ts.map +1 -1
- package/node/dist/plugins/swift-wasm.js +45 -7
- package/node/dist/plugins/swift-wasm.js.map +1 -1
- package/node/dist/plugins/symbolTemplates.d.ts +22 -0
- package/node/dist/plugins/symbolTemplates.d.ts.map +1 -0
- package/node/dist/plugins/symbolTemplates.js +83 -0
- package/node/dist/plugins/symbolTemplates.js.map +1 -0
- package/package.json +4 -2
- package/plugins/app-manifests.ts +3 -2
- package/plugins/asset-catalogs.ts +119 -14
- package/plugins/included-sites.ts +74 -0
- package/plugins/pdfImages.ts +163 -0
- package/plugins/swift-wasm.ts +52 -6
- package/plugins/symbolTemplates.ts +88 -0
- package/public/_headers +7 -0
- package/public/app/oauth-callback.html +62 -0
- package/src/animation.ts +235 -8
- package/src/appFiles.ts +201 -0
- package/src/base64.ts +43 -0
- package/src/bitmapPlan.ts +143 -0
- package/src/bitmapStore.ts +206 -0
- package/src/bitmapWorker.ts +41 -0
- package/src/bitmapWorkerProtocol.ts +15 -0
- package/src/bitmaps.ts +283 -0
- package/src/blockedHosts.ts +29 -0
- package/src/canvasDrawing.ts +361 -0
- package/src/chartDom.ts +2 -2
- package/src/chromeColor.ts +179 -0
- package/src/clipboard.ts +78 -0
- package/src/colorMultiply.ts +93 -0
- package/src/colorPicker.ts +44 -0
- package/src/corsProxy.ts +45 -0
- package/src/gaugeRing.ts +74 -0
- package/src/gif.ts +297 -0
- package/src/hostRequests.ts +426 -0
- package/src/imageOps.ts +592 -0
- package/src/interaction.ts +68 -3
- package/src/landing.ts +2 -2
- package/src/largeTitle.ts +79 -0
- package/src/layout/chart.ts +9 -3
- package/src/layout/engine.ts +681 -174
- package/src/layout/index.ts +3 -1
- package/src/layout/text.ts +217 -1
- package/src/layout/types.ts +31 -0
- package/src/layout/typography.ts +24 -7
- package/src/layout/video.ts +24 -0
- package/src/layoutDom.ts +66 -6
- package/src/main.ts +127 -4
- package/src/media.ts +132 -0
- package/src/mock.ts +4 -3
- package/src/notifications.ts +109 -0
- package/src/pathShape.ts +156 -0
- package/src/protocol.ts +782 -13
- package/src/renderer.ts +603 -44
- package/src/richText.ts +65 -0
- package/src/scanner.ts +234 -0
- package/src/scrollTo.ts +123 -0
- package/src/styles.css +521 -100
- package/src/styles.ts +61 -0
- package/src/swiftData.ts +128 -0
- package/src/symbols.ts +658 -28
- package/src/translation.ts +126 -0
- package/src/video.ts +299 -0
- package/src/vite-env.d.ts +6 -0
- package/src/wasm.ts +227 -32
- package/src/webAuth.ts +267 -0
- package/src/webSocket.ts +148 -0
- package/src/zoneinfo.ts +269 -0
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: #
|
|
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: #
|
|
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;
|
|
@@ -77,9 +79,22 @@
|
|
|
77
79
|
--sb-color-secondary-fill: rgba(120, 120, 128, 0.16);
|
|
78
80
|
--sb-color-tertiary-fill: rgba(118, 118, 128, 0.12);
|
|
79
81
|
--sb-color-quaternary-fill: rgba(116, 116, 128, 0.08);
|
|
82
|
+
/* UIKit's semantic colors without a token above (`Color(uiColor:)`). */
|
|
83
|
+
--sb-color-tertiary-system-background: #ffffff;
|
|
84
|
+
--sb-color-tertiary-system-grouped-background: #f2f2f7;
|
|
85
|
+
--sb-color-opaque-separator: #c6c6c8;
|
|
86
|
+
--sb-color-system-gray2: #aeaeb2;
|
|
87
|
+
--sb-color-system-gray3: #c7c7cc;
|
|
88
|
+
--sb-color-system-gray4: #d1d1d6;
|
|
89
|
+
--sb-color-system-gray5: #e5e5ea;
|
|
90
|
+
--sb-color-system-gray6: #f2f2f7;
|
|
80
91
|
/* Phase 6 materials: rgb components of the color blurred over the backdrop. */
|
|
81
92
|
--sb-color-material-base: 255, 255, 255;
|
|
82
|
-
|
|
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);
|
|
83
98
|
--sb-color-field-border: #c6c6c8;
|
|
84
99
|
--sb-color-field-background: #ffffff;
|
|
85
100
|
--sb-color-placeholder: rgba(60, 60, 67, 0.3);
|
|
@@ -87,6 +102,12 @@
|
|
|
87
102
|
--sb-color-grabber: rgba(60, 60, 67, 0.3);
|
|
88
103
|
/* Phase 4 */
|
|
89
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);
|
|
90
111
|
--sb-color-segment-track: rgba(118, 118, 128, 0.12);
|
|
91
112
|
--sb-color-segment-selected: #ffffff;
|
|
92
113
|
--sb-color-menu-background: rgba(250, 250, 250, 0.96);
|
|
@@ -96,13 +117,14 @@
|
|
|
96
117
|
color-scheme: dark;
|
|
97
118
|
--sb-color-primary: #ffffff;
|
|
98
119
|
--sb-color-secondary: rgba(235, 235, 245, 0.6);
|
|
99
|
-
--sb-color-accent: #
|
|
120
|
+
--sb-color-accent: #0091ff;
|
|
100
121
|
--sb-color-system-background: #000000;
|
|
101
122
|
--sb-color-secondary-system-background: #1c1c1e;
|
|
102
123
|
--sb-color-separator: rgba(84, 84, 88, 0.6);
|
|
124
|
+
--sb-color-row-separator: rgba(84, 84, 88, 0.6);
|
|
103
125
|
--sb-color-tertiary-label: rgba(235, 235, 245, 0.3);
|
|
104
126
|
--sb-color-quaternary-label: rgba(235, 235, 245, 0.16);
|
|
105
|
-
--sb-color-red: #
|
|
127
|
+
--sb-color-red: #ff4245;
|
|
106
128
|
--sb-color-green: #30d158;
|
|
107
129
|
--sb-color-grouped-background: #000000;
|
|
108
130
|
--sb-color-grouped-cell: #1c1c1e;
|
|
@@ -110,18 +132,32 @@
|
|
|
110
132
|
--sb-color-secondary-fill: rgba(120, 120, 128, 0.32);
|
|
111
133
|
--sb-color-tertiary-fill: rgba(118, 118, 128, 0.24);
|
|
112
134
|
--sb-color-quaternary-fill: rgba(118, 118, 128, 0.18);
|
|
135
|
+
--sb-color-tertiary-system-background: #2c2c2e;
|
|
136
|
+
--sb-color-tertiary-system-grouped-background: #2c2c2e;
|
|
137
|
+
--sb-color-opaque-separator: #38383a;
|
|
138
|
+
--sb-color-system-gray2: #636366;
|
|
139
|
+
--sb-color-system-gray3: #48484a;
|
|
140
|
+
--sb-color-system-gray4: #3a3a3c;
|
|
141
|
+
--sb-color-system-gray5: #2c2c2e;
|
|
142
|
+
--sb-color-system-gray6: #1c1c1e;
|
|
113
143
|
--sb-color-material-base: 30, 30, 30;
|
|
114
|
-
--sb-color-switch-off:
|
|
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);
|
|
115
148
|
--sb-color-field-border: #3a3a3c;
|
|
116
149
|
--sb-color-field-background: #1c1c1e;
|
|
117
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);
|
|
118
154
|
/* Sheets float above the window and take the elevated system background. */
|
|
119
155
|
--sb-color-sheet-background: #1c1c1e;
|
|
120
156
|
--sb-color-grabber: rgba(235, 235, 245, 0.3);
|
|
121
157
|
/* Phase 4 */
|
|
122
158
|
--sb-color-tabbar-background: rgba(29, 29, 29, 0.94);
|
|
123
159
|
--sb-color-segment-track: rgba(118, 118, 128, 0.24);
|
|
124
|
-
--sb-color-segment-selected: #
|
|
160
|
+
--sb-color-segment-selected: #5a5a5f;
|
|
125
161
|
--sb-color-menu-background: rgba(37, 37, 37, 0.96);
|
|
126
162
|
}
|
|
127
163
|
|
|
@@ -336,7 +372,7 @@ body {
|
|
|
336
372
|
position: absolute;
|
|
337
373
|
top: 11px;
|
|
338
374
|
left: 50%;
|
|
339
|
-
width:
|
|
375
|
+
width: 125px;
|
|
340
376
|
height: 37px;
|
|
341
377
|
transform: translateX(-50%);
|
|
342
378
|
border-radius: 20px;
|
|
@@ -353,23 +389,30 @@ body {
|
|
|
353
389
|
display: flex;
|
|
354
390
|
align-items: center;
|
|
355
391
|
justify-content: space-between;
|
|
356
|
-
|
|
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;
|
|
357
396
|
pointer-events: none;
|
|
358
397
|
z-index: 2;
|
|
359
398
|
color: var(--sb-color-primary);
|
|
360
399
|
}
|
|
361
400
|
|
|
362
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;
|
|
363
407
|
font-size: 17px;
|
|
364
408
|
font-weight: 600;
|
|
365
409
|
letter-spacing: -0.02em;
|
|
366
|
-
font-variant-numeric: tabular-nums;
|
|
367
410
|
}
|
|
368
411
|
|
|
369
412
|
.status-glyphs {
|
|
370
413
|
display: flex;
|
|
371
414
|
align-items: center;
|
|
372
|
-
gap:
|
|
415
|
+
gap: 7.67px;
|
|
373
416
|
}
|
|
374
417
|
|
|
375
418
|
.glyph {
|
|
@@ -377,6 +420,12 @@ body {
|
|
|
377
420
|
fill: currentColor;
|
|
378
421
|
}
|
|
379
422
|
|
|
423
|
+
.glyph-wifi .wifi-band {
|
|
424
|
+
fill: none;
|
|
425
|
+
stroke: currentColor;
|
|
426
|
+
stroke-linecap: round;
|
|
427
|
+
}
|
|
428
|
+
|
|
380
429
|
.glyph-battery .battery-shell {
|
|
381
430
|
fill: none;
|
|
382
431
|
stroke: currentColor;
|
|
@@ -455,7 +504,21 @@ html[data-sb-mode="device"] body {
|
|
|
455
504
|
overflow: hidden;
|
|
456
505
|
overscroll-behavior: none;
|
|
457
506
|
touch-action: manipulation;
|
|
458
|
-
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/* The page behind the app is the OS appearance's system background from the
|
|
510
|
+
first paint (Safari tints its status bar strip from it); chromeColor.ts
|
|
511
|
+
then keeps it at the color the app draws at its top edge. */
|
|
512
|
+
html[data-sb-mode="device"],
|
|
513
|
+
html[data-sb-mode="device"] body {
|
|
514
|
+
background: #ffffff;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
@media (prefers-color-scheme: dark) {
|
|
518
|
+
html[data-sb-mode="device"],
|
|
519
|
+
html[data-sb-mode="device"] body {
|
|
520
|
+
background: #000000;
|
|
521
|
+
}
|
|
459
522
|
}
|
|
460
523
|
|
|
461
524
|
html[data-sb-mode="device"] .toolbar {
|
|
@@ -542,6 +605,9 @@ html[data-sb-mode="device"] .status {
|
|
|
542
605
|
-webkit-background-clip: var(--sb-text-clip, border-box);
|
|
543
606
|
background-clip: var(--sb-text-clip, border-box);
|
|
544
607
|
color: var(--sb-text-color);
|
|
608
|
+
/* View-level `.underline()` / `.strikethrough()` (renderer.ts `applyStyle`);
|
|
609
|
+
with both unset the declaration is invalid and the line is `none`. */
|
|
610
|
+
text-decoration-line: var(--sb-text-underline,) var(--sb-text-strikethrough,);
|
|
545
611
|
}
|
|
546
612
|
|
|
547
613
|
.sb-vstack,
|
|
@@ -589,7 +655,8 @@ html[data-sb-mode="device"] .status {
|
|
|
589
655
|
}
|
|
590
656
|
|
|
591
657
|
.sb-button[data-sb-style="bordered"] {
|
|
592
|
-
|
|
658
|
+
/* iOS 26: secondarySystemFill, or the view's tint at 18% (dark 25%). */
|
|
659
|
+
background: var(--sb-tint-fill, var(--sb-color-secondary-fill));
|
|
593
660
|
color: var(--sb-button-color, var(--sb-tint, var(--sb-color-accent)));
|
|
594
661
|
}
|
|
595
662
|
|
|
@@ -607,6 +674,18 @@ html[data-sb-mode="device"] .status {
|
|
|
607
674
|
color: #ffffff;
|
|
608
675
|
}
|
|
609
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
|
+
|
|
610
689
|
/* .disabled(true): opacity and pointer-events are set inline by the renderer
|
|
611
690
|
(so update() clears them); the attribute carries the state for tooling. */
|
|
612
691
|
[data-sb-disabled] {
|
|
@@ -629,6 +708,47 @@ html[data-sb-mode="device"] .status {
|
|
|
629
708
|
stroke: currentColor;
|
|
630
709
|
}
|
|
631
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
|
+
|
|
632
752
|
/* Unknown symbol or catalog image: a dashed box named by `title`. */
|
|
633
753
|
.sb-image[data-sb-missing] {
|
|
634
754
|
border: 1px dashed currentColor;
|
|
@@ -639,7 +759,8 @@ html[data-sb-mode="device"] .status {
|
|
|
639
759
|
/* Phase 5: a catalog image. The layout engine decides the frame (intrinsic
|
|
640
760
|
size, or the proposal for `.resizable()`), so the bitmap fills the box;
|
|
641
761
|
`.aspectRatio` fitting is a layout concern, not object-fit's. */
|
|
642
|
-
.sb-image > img
|
|
762
|
+
.sb-image > img,
|
|
763
|
+
.sb-image > canvas {
|
|
643
764
|
display: block;
|
|
644
765
|
width: 100%;
|
|
645
766
|
height: 100%;
|
|
@@ -649,6 +770,12 @@ html[data-sb-mode="device"] .status {
|
|
|
649
770
|
-webkit-user-select: none;
|
|
650
771
|
}
|
|
651
772
|
|
|
773
|
+
/* `.interpolation(.none)`: a QR code's modules stay sharp when scaled up. */
|
|
774
|
+
.sb-image[data-sb-interpolation='none'] > img,
|
|
775
|
+
.sb-image[data-sb-interpolation='none'] > canvas {
|
|
776
|
+
image-rendering: pixelated;
|
|
777
|
+
}
|
|
778
|
+
|
|
652
779
|
/* Phase 5: a `.symbolset` SVG drawn as a template: the SVG masks a box
|
|
653
780
|
filled with the current foreground color, like an SF Symbol. */
|
|
654
781
|
.sb-image > .sb-image-mask {
|
|
@@ -760,8 +887,9 @@ html[data-sb-mode="device"] .status {
|
|
|
760
887
|
.sb-switch {
|
|
761
888
|
appearance: none;
|
|
762
889
|
position: absolute;
|
|
763
|
-
|
|
764
|
-
|
|
890
|
+
/* iOS 26: 63 × 28 with a pill knob. */
|
|
891
|
+
width: 63px;
|
|
892
|
+
height: 28px;
|
|
765
893
|
margin: 0;
|
|
766
894
|
padding: 0;
|
|
767
895
|
border: 0;
|
|
@@ -786,9 +914,9 @@ html[data-sb-mode="device"] .status {
|
|
|
786
914
|
position: absolute;
|
|
787
915
|
top: 2px;
|
|
788
916
|
left: 2px;
|
|
789
|
-
width:
|
|
790
|
-
height:
|
|
791
|
-
border-radius:
|
|
917
|
+
width: 37px;
|
|
918
|
+
height: 24px;
|
|
919
|
+
border-radius: 12px;
|
|
792
920
|
background: #ffffff;
|
|
793
921
|
box-shadow:
|
|
794
922
|
0 3px 8px rgba(0, 0, 0, 0.15),
|
|
@@ -797,7 +925,7 @@ html[data-sb-mode="device"] .status {
|
|
|
797
925
|
}
|
|
798
926
|
|
|
799
927
|
.sb-switch[aria-checked="true"] .sb-switch-knob {
|
|
800
|
-
transform: translateX(
|
|
928
|
+
transform: translateX(22px);
|
|
801
929
|
}
|
|
802
930
|
|
|
803
931
|
/* ---------------------------------------------------------------------------
|
|
@@ -828,7 +956,8 @@ html[data-sb-mode="device"] .status {
|
|
|
828
956
|
|
|
829
957
|
.sb-section-rows {
|
|
830
958
|
position: absolute;
|
|
831
|
-
|
|
959
|
+
/* iOS 26's inset grouped cards. */
|
|
960
|
+
border-radius: 26px;
|
|
832
961
|
background: var(--sb-color-grouped-cell);
|
|
833
962
|
overflow: hidden;
|
|
834
963
|
}
|
|
@@ -844,26 +973,56 @@ html[data-sb-mode="device"] .status {
|
|
|
844
973
|
|
|
845
974
|
.sb-list-content > :not(.sb-section):first-child,
|
|
846
975
|
.sb-list-content > .sb-section + :not(.sb-section) {
|
|
847
|
-
border-top-left-radius:
|
|
848
|
-
border-top-right-radius:
|
|
976
|
+
border-top-left-radius: 26px;
|
|
977
|
+
border-top-right-radius: 26px;
|
|
849
978
|
}
|
|
850
979
|
|
|
851
980
|
.sb-list-content > :not(.sb-section):last-child,
|
|
852
981
|
.sb-list-content > :not(.sb-section):has(+ .sb-section) {
|
|
853
|
-
border-bottom-left-radius:
|
|
854
|
-
border-bottom-right-radius:
|
|
982
|
+
border-bottom-left-radius: 26px;
|
|
983
|
+
border-bottom-right-radius: 26px;
|
|
855
984
|
}
|
|
856
985
|
|
|
857
|
-
/*
|
|
986
|
+
/* Lazy rows not built yet (a trailing `lazyspacer`): no cell of their own, and
|
|
987
|
+
the row before them ends the card as the last row would. */
|
|
988
|
+
.sb-lazyspacer {
|
|
989
|
+
background: none !important;
|
|
990
|
+
pointer-events: none;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
.sb-list-content > :not(.sb-section):has(+ .sb-lazyspacer:last-child) {
|
|
994
|
+
border-bottom-left-radius: 26px;
|
|
995
|
+
border-bottom-right-radius: 26px;
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
.sb-section-rows > :has(+ .sb-lazyspacer:last-child)::after,
|
|
999
|
+
.sb-list-content > :has(+ .sb-lazyspacer:last-child)::after,
|
|
1000
|
+
.sb-section-rows > .sb-lazyspacer::after,
|
|
1001
|
+
.sb-list-content > .sb-lazyspacer::after {
|
|
1002
|
+
display: none;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
/* `.listRowSeparator(.hidden)`: the row's own line (below it) or the
|
|
1006
|
+
previous row's (above it) is left out. */
|
|
1007
|
+
.sb-section-rows > [data-sb-separator-bottom="hidden"]::after,
|
|
1008
|
+
.sb-list-content > [data-sb-separator-bottom="hidden"]::after,
|
|
1009
|
+
.sb-section-rows > :has(+ [data-sb-separator-top="hidden"])::after,
|
|
1010
|
+
.sb-list-content > :has(+ [data-sb-separator-top="hidden"])::after {
|
|
1011
|
+
display: none !important;
|
|
1012
|
+
}
|
|
1013
|
+
|
|
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. */
|
|
858
1017
|
.sb-section-rows > :not(:last-child)::after,
|
|
859
1018
|
.sb-list-content > :not(.sb-section):not(:last-child):not(:has(+ .sb-section))::after {
|
|
860
1019
|
content: "";
|
|
861
1020
|
position: absolute;
|
|
862
|
-
left: 16px;
|
|
863
|
-
right:
|
|
1021
|
+
left: var(--sb-separator-leading, 16px);
|
|
1022
|
+
right: 16px;
|
|
864
1023
|
bottom: 0;
|
|
865
1024
|
height: 1px;
|
|
866
|
-
background: var(--sb-color-separator);
|
|
1025
|
+
background: var(--sb-color-row-separator);
|
|
867
1026
|
pointer-events: none;
|
|
868
1027
|
}
|
|
869
1028
|
|
|
@@ -902,7 +1061,7 @@ html[data-sb-mode="device"] .status {
|
|
|
902
1061
|
|
|
903
1062
|
/* A screen with a list as its content takes the grouped background, like a
|
|
904
1063
|
UINavigationController hosting an inset-grouped table view. */
|
|
905
|
-
.sb-navscreen:has(> .sb-navscreen-body > .sb-navscreen-content > .sb-list) {
|
|
1064
|
+
.sb-navscreen:has(> .sb-navscreen-body > .sb-navscreen-content > .sb-list:not([data-sb-background="hidden"])) {
|
|
906
1065
|
background: var(--sb-color-grouped-background);
|
|
907
1066
|
}
|
|
908
1067
|
|
|
@@ -955,7 +1114,7 @@ html[data-sb-mode="device"] .status {
|
|
|
955
1114
|
display: grid;
|
|
956
1115
|
grid-template-columns: 1fr auto 1fr;
|
|
957
1116
|
align-items: center;
|
|
958
|
-
padding: 0
|
|
1117
|
+
padding: 0 16px;
|
|
959
1118
|
box-sizing: border-box;
|
|
960
1119
|
background: transparent;
|
|
961
1120
|
/* Phase 8: content that ignores the safe area paints under the bar; the
|
|
@@ -978,14 +1137,21 @@ html[data-sb-mode="device"] .status {
|
|
|
978
1137
|
justify-self: start;
|
|
979
1138
|
display: inline-flex;
|
|
980
1139
|
align-items: center;
|
|
1140
|
+
justify-content: center;
|
|
981
1141
|
gap: 2px;
|
|
982
|
-
/*
|
|
1142
|
+
/* iOS 26: a 44pt glass circle with the chevron (chrome.back). */
|
|
1143
|
+
width: 44px;
|
|
1144
|
+
height: 44px;
|
|
983
1145
|
max-width: 45%;
|
|
984
1146
|
margin: 0;
|
|
985
1147
|
padding: 0;
|
|
986
1148
|
border: 0;
|
|
987
|
-
|
|
988
|
-
|
|
1149
|
+
border-radius: 22px;
|
|
1150
|
+
background: var(--sb-glass-background);
|
|
1151
|
+
box-shadow: var(--sb-glass-shadow);
|
|
1152
|
+
backdrop-filter: blur(12px) saturate(180%);
|
|
1153
|
+
-webkit-backdrop-filter: blur(12px) saturate(180%);
|
|
1154
|
+
color: var(--sb-color-primary);
|
|
989
1155
|
font: inherit;
|
|
990
1156
|
font-size: var(--sb-font-size-body);
|
|
991
1157
|
line-height: 1.3;
|
|
@@ -1005,11 +1171,15 @@ html[data-sb-mode="device"] .status {
|
|
|
1005
1171
|
.sb-navbar-back > svg {
|
|
1006
1172
|
width: 1.4em;
|
|
1007
1173
|
height: 1.4em;
|
|
1008
|
-
margin-left: -
|
|
1009
|
-
stroke-width: 2.
|
|
1174
|
+
margin-left: -2px;
|
|
1175
|
+
stroke-width: 2.25;
|
|
1010
1176
|
flex-shrink: 0;
|
|
1011
1177
|
}
|
|
1012
1178
|
|
|
1179
|
+
.sb-navbar-back-label:empty {
|
|
1180
|
+
display: none;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1013
1183
|
.sb-navbar-back-label {
|
|
1014
1184
|
overflow: hidden;
|
|
1015
1185
|
text-overflow: ellipsis;
|
|
@@ -1047,14 +1217,16 @@ html[data-sb-mode="device"] .status {
|
|
|
1047
1217
|
display: block;
|
|
1048
1218
|
}
|
|
1049
1219
|
|
|
1050
|
-
/* Large title: the engine's `largeTitle` rect (
|
|
1220
|
+
/* Large title: the engine's `largeTitle` rect (62px row), text already
|
|
1051
1221
|
truncated to one line. */
|
|
1052
1222
|
.sb-navbar-large-title {
|
|
1053
1223
|
position: absolute;
|
|
1054
1224
|
z-index: 1; /* Phase 8: above content that ignores the safe area */
|
|
1055
1225
|
display: flex;
|
|
1056
|
-
|
|
1057
|
-
|
|
1226
|
+
/* iOS 26: the title's line starts 13 into its 62px row. */
|
|
1227
|
+
align-items: flex-start;
|
|
1228
|
+
line-height: 41px;
|
|
1229
|
+
padding: 13px 16px 0;
|
|
1058
1230
|
box-sizing: border-box;
|
|
1059
1231
|
font-size: var(--sb-ts-largeTitle-size, 34px);
|
|
1060
1232
|
font-weight: 700;
|
|
@@ -1069,8 +1241,34 @@ html[data-sb-mode="device"] .status {
|
|
|
1069
1241
|
display: none;
|
|
1070
1242
|
}
|
|
1071
1243
|
|
|
1244
|
+
/* The large title scrolls with the screen's root list or scroll view
|
|
1245
|
+
(src/largeTitle.ts moves it); drags on it scroll that view. */
|
|
1246
|
+
.sb-navbar-large-title {
|
|
1247
|
+
pointer-events: none;
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
/* Until the large title has scrolled under the bar, the inline title (whose
|
|
1251
|
+
rect the engine reports for a title that can collapse) stays hidden. */
|
|
1252
|
+
.sb-navscreen[data-sb-display="large"] > .sb-navbar > .sb-navbar-title {
|
|
1253
|
+
opacity: 0;
|
|
1254
|
+
transition: opacity 0.15s ease;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
.sb-navscreen[data-sb-display="large"][data-sb-collapsed] > .sb-navbar > .sb-navbar-title {
|
|
1258
|
+
opacity: 1;
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
/* iOS 26's scroll edge effect, simplified: scrolled content fades in over
|
|
1262
|
+
16px below the bar instead of being cut off at it (src/largeTitle.ts). */
|
|
1263
|
+
[data-sb-scrolls-title][data-sb-scrolled] {
|
|
1264
|
+
-webkit-mask-image: linear-gradient(to bottom, transparent, #000 16px);
|
|
1265
|
+
mask-image: linear-gradient(to bottom, transparent, #000 16px);
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1072
1268
|
/* NavigationLink: a tappable label. Inside a list it is a row with a chevron
|
|
1073
|
-
at the engine's `chevron` rect
|
|
1269
|
+
at the engine's `chevron` rect, its label in the primary color (also when
|
|
1270
|
+
the row is wrapped: a selectable or deletable `listrow`, a modifier);
|
|
1271
|
+
elsewhere it reads as plain accent text. */
|
|
1074
1272
|
.sb-navlink {
|
|
1075
1273
|
color: var(--sb-color-accent);
|
|
1076
1274
|
cursor: pointer;
|
|
@@ -1094,9 +1292,16 @@ html[data-sb-mode="device"] .status {
|
|
|
1094
1292
|
display: none;
|
|
1095
1293
|
}
|
|
1096
1294
|
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1295
|
+
/* An explicit `.foregroundStyle` on or around the link (which sets
|
|
1296
|
+
`--sb-button-color`) wins, as in SwiftUI. */
|
|
1297
|
+
.sb-list .sb-navlink {
|
|
1298
|
+
color: var(--sb-button-color, var(--sb-color-primary));
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
/* A `Label`'s icon in a list row takes the tint; its title keeps the row's
|
|
1302
|
+
color. */
|
|
1303
|
+
.sb-list [data-sb-label-icon] {
|
|
1304
|
+
color: var(--sb-button-color, var(--sb-tint, var(--sb-color-accent)));
|
|
1100
1305
|
}
|
|
1101
1306
|
|
|
1102
1307
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -1161,6 +1366,26 @@ html[data-sb-mode="device"] .status {
|
|
|
1161
1366
|
transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
1162
1367
|
}
|
|
1163
1368
|
|
|
1369
|
+
/* iOS 26: a sheet at a partial detent floats inset from the screen edges,
|
|
1370
|
+
rounded all around, over a lighter dim. */
|
|
1371
|
+
.sb-sheet[data-sb-floating] > .sb-sheet-card {
|
|
1372
|
+
border-radius: 38px;
|
|
1373
|
+
background: color-mix(in srgb, var(--sb-color-sheet-background) 55%, transparent);
|
|
1374
|
+
backdrop-filter: blur(24px) saturate(180%);
|
|
1375
|
+
-webkit-backdrop-filter: blur(24px) saturate(180%);
|
|
1376
|
+
box-shadow: 0 8px 40px rgba(0, 0, 0, 0.18);
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
/* The sheet's glass shows through a navigation stack in it, as on iOS 26
|
|
1380
|
+
(an opaque screen would paint over it). */
|
|
1381
|
+
.sb-sheet[data-sb-floating] .sb-navscreen {
|
|
1382
|
+
background: transparent;
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
.sb-sheet[data-sb-floating] > .sb-sheet-scrim {
|
|
1386
|
+
background: rgba(0, 0, 0, 0.22);
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1164
1389
|
.sb-sheet-grabber {
|
|
1165
1390
|
position: absolute;
|
|
1166
1391
|
border-radius: 2.5px;
|
|
@@ -1300,8 +1525,8 @@ html[data-sb-mode="device"] .status {
|
|
|
1300
1525
|
/* ---------------------------------------------------------------------------
|
|
1301
1526
|
Phase 4: tab views. The tabs are stacked in the content slot (every tab has
|
|
1302
1527
|
the tab view's frame); the selected one is visible, the others keep their
|
|
1303
|
-
DOM hidden. The bar is the engine's `bar` rect
|
|
1304
|
-
|
|
1528
|
+
DOM hidden. The bar is the engine's `bar` rect: iOS 26's glass capsule
|
|
1529
|
+
floating over the content, the selected item on a gray pill.
|
|
1305
1530
|
------------------------------------------------------------------------- */
|
|
1306
1531
|
|
|
1307
1532
|
.sb-tabview {
|
|
@@ -1325,35 +1550,27 @@ html[data-sb-mode="device"] .status {
|
|
|
1325
1550
|
.sb-tabbar {
|
|
1326
1551
|
position: absolute;
|
|
1327
1552
|
z-index: 3;
|
|
1328
|
-
|
|
1553
|
+
border-radius: 31px;
|
|
1554
|
+
background: var(--sb-glass-background);
|
|
1555
|
+
box-shadow: var(--sb-glass-shadow);
|
|
1329
1556
|
backdrop-filter: blur(20px) saturate(180%);
|
|
1330
1557
|
-webkit-backdrop-filter: blur(20px) saturate(180%);
|
|
1331
1558
|
}
|
|
1332
1559
|
|
|
1333
|
-
.sb-tabbar::before {
|
|
1334
|
-
content: "";
|
|
1335
|
-
position: absolute;
|
|
1336
|
-
left: 0;
|
|
1337
|
-
right: 0;
|
|
1338
|
-
top: 0;
|
|
1339
|
-
height: 1px;
|
|
1340
|
-
background: var(--sb-color-separator);
|
|
1341
|
-
pointer-events: none;
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
1560
|
.sb-tabbar-item {
|
|
1345
1561
|
appearance: none;
|
|
1346
1562
|
position: absolute;
|
|
1347
1563
|
display: flex;
|
|
1348
1564
|
flex-direction: column;
|
|
1349
1565
|
align-items: center;
|
|
1350
|
-
justify-content:
|
|
1351
|
-
gap:
|
|
1566
|
+
justify-content: center;
|
|
1567
|
+
gap: 1px;
|
|
1352
1568
|
margin: 0;
|
|
1353
|
-
padding:
|
|
1569
|
+
padding: 0;
|
|
1354
1570
|
border: 0;
|
|
1571
|
+
border-radius: 27px;
|
|
1355
1572
|
background: none;
|
|
1356
|
-
color: var(--sb-color-
|
|
1573
|
+
color: var(--sb-color-primary);
|
|
1357
1574
|
font-family: var(--sb-font-family);
|
|
1358
1575
|
cursor: pointer;
|
|
1359
1576
|
touch-action: manipulation;
|
|
@@ -1362,6 +1579,7 @@ html[data-sb-mode="device"] .status {
|
|
|
1362
1579
|
|
|
1363
1580
|
.sb-tabbar-item[aria-selected="true"] {
|
|
1364
1581
|
color: var(--sb-color-accent);
|
|
1582
|
+
background: var(--sb-glass-selected);
|
|
1365
1583
|
}
|
|
1366
1584
|
|
|
1367
1585
|
.sb-tabbar-item:focus-visible {
|
|
@@ -1401,11 +1619,35 @@ html[data-sb-mode="device"] .status {
|
|
|
1401
1619
|
.sb-tabbar-title {
|
|
1402
1620
|
font-size: 10px;
|
|
1403
1621
|
line-height: 12px;
|
|
1404
|
-
font-weight:
|
|
1622
|
+
font-weight: 600;
|
|
1405
1623
|
letter-spacing: -0.01em;
|
|
1406
1624
|
white-space: nowrap;
|
|
1407
1625
|
}
|
|
1408
1626
|
|
|
1627
|
+
/* iOS 18 `Tab(…).badge(…)`: a red capsule at the top trailing corner of the icon. */
|
|
1628
|
+
.sb-tabbar-badge {
|
|
1629
|
+
position: absolute;
|
|
1630
|
+
top: 2px;
|
|
1631
|
+
left: calc(50% + 5px);
|
|
1632
|
+
box-sizing: border-box;
|
|
1633
|
+
min-width: 18px;
|
|
1634
|
+
height: 18px;
|
|
1635
|
+
padding: 0 5px;
|
|
1636
|
+
border-radius: 9px;
|
|
1637
|
+
background: var(--sb-color-red);
|
|
1638
|
+
color: #fff;
|
|
1639
|
+
font-size: 13px;
|
|
1640
|
+
line-height: 18px;
|
|
1641
|
+
font-weight: 400;
|
|
1642
|
+
text-align: center;
|
|
1643
|
+
white-space: nowrap;
|
|
1644
|
+
pointer-events: none;
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
.sb-tabbar-badge[hidden] {
|
|
1648
|
+
display: none;
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1409
1651
|
/* ---------------------------------------------------------------------------
|
|
1410
1652
|
Phase 4: pickers. Segmented: a gray track with equal segments at the
|
|
1411
1653
|
engine's `segment<i>` rects; the selected one carries a raised pill inset
|
|
@@ -1421,7 +1663,8 @@ html[data-sb-mode="device"] .status {
|
|
|
1421
1663
|
box is the whole row, so the track cannot be the element's background). */
|
|
1422
1664
|
.sb-segment-track {
|
|
1423
1665
|
position: absolute;
|
|
1424
|
-
|
|
1666
|
+
/* iOS 26: a capsule track with a capsule for the selected segment. */
|
|
1667
|
+
border-radius: 9999px;
|
|
1425
1668
|
background: var(--sb-color-segment-track);
|
|
1426
1669
|
pointer-events: none;
|
|
1427
1670
|
}
|
|
@@ -1449,7 +1692,7 @@ html[data-sb-mode="device"] .status {
|
|
|
1449
1692
|
content: "";
|
|
1450
1693
|
position: absolute;
|
|
1451
1694
|
inset: 2px;
|
|
1452
|
-
border-radius:
|
|
1695
|
+
border-radius: 9999px;
|
|
1453
1696
|
transition: background-color 0.15s ease, box-shadow 0.15s ease;
|
|
1454
1697
|
}
|
|
1455
1698
|
|
|
@@ -1658,6 +1901,28 @@ html[data-sb-mode="device"] .status {
|
|
|
1658
1901
|
background: var(--sb-tint, var(--sb-color-accent));
|
|
1659
1902
|
}
|
|
1660
1903
|
|
|
1904
|
+
.sb-gauge-ring {
|
|
1905
|
+
position: absolute;
|
|
1906
|
+
pointer-events: none;
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
.sb-gauge-ring > svg {
|
|
1910
|
+
display: block;
|
|
1911
|
+
width: 100%;
|
|
1912
|
+
height: 100%;
|
|
1913
|
+
overflow: visible;
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
.sb-gauge-ring-track {
|
|
1917
|
+
stroke: var(--sb-color-fill);
|
|
1918
|
+
stroke-linecap: round;
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
.sb-gauge-ring-value {
|
|
1922
|
+
stroke: var(--sb-tint, var(--sb-color-accent));
|
|
1923
|
+
stroke-linecap: round;
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1661
1926
|
@media (prefers-reduced-motion: reduce) {
|
|
1662
1927
|
.sb-picker-menu,
|
|
1663
1928
|
.sb-segment::before {
|
|
@@ -1834,7 +2099,7 @@ html[data-sb-mode="device"] .status {
|
|
|
1834
2099
|
right: 0;
|
|
1835
2100
|
top: 0;
|
|
1836
2101
|
height: 1px;
|
|
1837
|
-
background: var(--sb-color-separator);
|
|
2102
|
+
background: var(--sb-color-row-separator);
|
|
1838
2103
|
pointer-events: none;
|
|
1839
2104
|
}
|
|
1840
2105
|
|
|
@@ -1857,6 +2122,25 @@ html[data-sb-mode="device"] .status {
|
|
|
1857
2122
|
display: block;
|
|
1858
2123
|
}
|
|
1859
2124
|
|
|
2125
|
+
/* iOS 26: a bar's leading and trailing items share a glass capsule (the
|
|
2126
|
+
engine sizes the group: 44 tall, each item in a slot at least 44 wide),
|
|
2127
|
+
drawn in the label color unless tinted. */
|
|
2128
|
+
.sb-toolbar[data-sb-placement="leading"],
|
|
2129
|
+
.sb-toolbar[data-sb-placement="trailing"] {
|
|
2130
|
+
border-radius: 22px;
|
|
2131
|
+
background: var(--sb-glass-background);
|
|
2132
|
+
box-shadow: var(--sb-glass-shadow);
|
|
2133
|
+
backdrop-filter: blur(12px) saturate(180%);
|
|
2134
|
+
-webkit-backdrop-filter: blur(12px) saturate(180%);
|
|
2135
|
+
--sb-color-accent: var(--sb-color-primary);
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
/* Their symbols are drawn larger and heavier, as iOS 26 draws bar buttons. */
|
|
2139
|
+
.sb-toolbar:is([data-sb-placement="leading"], [data-sb-placement="trailing"]) .sb-image > svg {
|
|
2140
|
+
transform: scale(1.25);
|
|
2141
|
+
stroke-width: 2.1;
|
|
2142
|
+
}
|
|
2143
|
+
|
|
1860
2144
|
.sb-toolbar[data-sb-placement="bottom"] {
|
|
1861
2145
|
z-index: 3;
|
|
1862
2146
|
background: var(--sb-color-tabbar-background);
|
|
@@ -1875,16 +2159,21 @@ html[data-sb-mode="device"] .status {
|
|
|
1875
2159
|
pointer-events: none;
|
|
1876
2160
|
}
|
|
1877
2161
|
|
|
1878
|
-
/* Search field: a
|
|
2162
|
+
/* Search field: a capsule (engine `searchField` rect) with a
|
|
1879
2163
|
magnifier leading; the input sends `text` events like a textfield. */
|
|
1880
2164
|
.sb-searchfield {
|
|
1881
2165
|
display: flex;
|
|
1882
2166
|
align-items: center;
|
|
1883
|
-
gap:
|
|
1884
|
-
padding: 0
|
|
1885
|
-
|
|
1886
|
-
|
|
2167
|
+
gap: 8px;
|
|
2168
|
+
padding: 0 16px;
|
|
2169
|
+
/* iOS 26: a glass capsule floating at the bottom (engine `searchField`). */
|
|
2170
|
+
border-radius: 9999px;
|
|
2171
|
+
background: var(--sb-glass-background);
|
|
2172
|
+
box-shadow: var(--sb-glass-shadow);
|
|
2173
|
+
backdrop-filter: blur(20px) saturate(180%);
|
|
2174
|
+
-webkit-backdrop-filter: blur(20px) saturate(180%);
|
|
1887
2175
|
color: var(--sb-color-primary);
|
|
2176
|
+
z-index: 2;
|
|
1888
2177
|
}
|
|
1889
2178
|
|
|
1890
2179
|
.sb-searchfield > svg {
|
|
@@ -2042,33 +2331,34 @@ html[data-sb-mode="device"] .status {
|
|
|
2042
2331
|
}
|
|
2043
2332
|
|
|
2044
2333
|
/* The SwiftUI chart palette (and the other system colors) as `--sb-color-<name>`,
|
|
2045
|
-
which a named `Color` without a semantic token resolves to (src/styles.ts).
|
|
2334
|
+
which a named `Color` without a semantic token resolves to (src/styles.ts).
|
|
2335
|
+
iOS 26's palette (measured in the iOS 26.2 simulator, Examples/Fidelity). */
|
|
2046
2336
|
.device,
|
|
2047
2337
|
.device[data-theme="light"] {
|
|
2048
|
-
--sb-color-blue: #
|
|
2049
|
-
--sb-color-orange: #
|
|
2338
|
+
--sb-color-blue: #0088ff;
|
|
2339
|
+
--sb-color-orange: #ff8d28;
|
|
2050
2340
|
--sb-color-yellow: #ffcc00;
|
|
2051
|
-
--sb-color-purple: #
|
|
2341
|
+
--sb-color-purple: #cb30e0;
|
|
2052
2342
|
--sb-color-pink: #ff2d55;
|
|
2053
|
-
--sb-color-teal: #
|
|
2054
|
-
--sb-color-indigo: #
|
|
2055
|
-
--sb-color-mint: #
|
|
2056
|
-
--sb-color-cyan: #
|
|
2057
|
-
--sb-color-brown: #
|
|
2343
|
+
--sb-color-teal: #00c3d0;
|
|
2344
|
+
--sb-color-indigo: #6155f5;
|
|
2345
|
+
--sb-color-mint: #00c8b3;
|
|
2346
|
+
--sb-color-cyan: #00c0e8;
|
|
2347
|
+
--sb-color-brown: #ac7f5e;
|
|
2058
2348
|
--sb-color-gray: #8e8e93;
|
|
2059
2349
|
}
|
|
2060
2350
|
|
|
2061
2351
|
.device[data-theme="dark"] {
|
|
2062
|
-
--sb-color-blue: #
|
|
2063
|
-
--sb-color-orange: #
|
|
2064
|
-
--sb-color-yellow: #
|
|
2065
|
-
--sb-color-purple: #
|
|
2352
|
+
--sb-color-blue: #0091ff;
|
|
2353
|
+
--sb-color-orange: #ff9230;
|
|
2354
|
+
--sb-color-yellow: #ffd600;
|
|
2355
|
+
--sb-color-purple: #db34f2;
|
|
2066
2356
|
--sb-color-pink: #ff375f;
|
|
2067
|
-
--sb-color-teal: #
|
|
2068
|
-
--sb-color-indigo: #
|
|
2069
|
-
--sb-color-mint: #
|
|
2070
|
-
--sb-color-cyan: #
|
|
2071
|
-
--sb-color-brown: #
|
|
2357
|
+
--sb-color-teal: #00d2e0;
|
|
2358
|
+
--sb-color-indigo: #6d7cff;
|
|
2359
|
+
--sb-color-mint: #00dac3;
|
|
2360
|
+
--sb-color-cyan: #3cd3fe;
|
|
2361
|
+
--sb-color-brown: #b78a66;
|
|
2072
2362
|
--sb-color-gray: #8e8e93;
|
|
2073
2363
|
}
|
|
2074
2364
|
|
|
@@ -2479,7 +2769,7 @@ html[data-sb-mode="device"] .status {
|
|
|
2479
2769
|
.sb-alert-scrim {
|
|
2480
2770
|
position: absolute;
|
|
2481
2771
|
inset: 0;
|
|
2482
|
-
background:
|
|
2772
|
+
background: var(--sb-alert-scrim);
|
|
2483
2773
|
animation: sb-alert-fade-in 0.25s ease-out both;
|
|
2484
2774
|
}
|
|
2485
2775
|
|
|
@@ -2537,6 +2827,13 @@ html[data-sb-mode="device"] .status {
|
|
|
2537
2827
|
background: var(--sb-color-sheet-background);
|
|
2538
2828
|
}
|
|
2539
2829
|
|
|
2830
|
+
/* iOS 26: the alert is a rounder glass card. */
|
|
2831
|
+
.sb-alert[data-sb-style="alert"] .sb-alert-card {
|
|
2832
|
+
border-radius: 34px;
|
|
2833
|
+
background: var(--sb-alert-card);
|
|
2834
|
+
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.12);
|
|
2835
|
+
}
|
|
2836
|
+
|
|
2540
2837
|
.sb-alert-title,
|
|
2541
2838
|
.sb-alert-message {
|
|
2542
2839
|
position: absolute;
|
|
@@ -2547,6 +2844,16 @@ html[data-sb-mode="device"] .status {
|
|
|
2547
2844
|
overflow: hidden;
|
|
2548
2845
|
}
|
|
2549
2846
|
|
|
2847
|
+
/* iOS 26: an alert's title and message are leading-aligned, the message dim. */
|
|
2848
|
+
.sb-alert[data-sb-style="alert"] .sb-alert-title,
|
|
2849
|
+
.sb-alert[data-sb-style="alert"] .sb-alert-message {
|
|
2850
|
+
text-align: left;
|
|
2851
|
+
}
|
|
2852
|
+
|
|
2853
|
+
.sb-alert[data-sb-style="alert"] .sb-alert-message {
|
|
2854
|
+
color: var(--sb-color-secondary);
|
|
2855
|
+
}
|
|
2856
|
+
|
|
2550
2857
|
/* The action sheet's title and message are a dim caption. */
|
|
2551
2858
|
.sb-alert[data-sb-style="dialog"] .sb-alert-title,
|
|
2552
2859
|
.sb-alert[data-sb-style="dialog"] .sb-alert-message {
|
|
@@ -2587,11 +2894,32 @@ html[data-sb-mode="device"] .status {
|
|
|
2587
2894
|
pointer-events: none;
|
|
2588
2895
|
}
|
|
2589
2896
|
|
|
2590
|
-
|
|
2897
|
+
/* iOS 26: an alert's buttons are gray capsules, in the label color. */
|
|
2898
|
+
.sb-alert[data-sb-style="alert"] .sb-alert-buttons > .sb-button,
|
|
2899
|
+
.sb-alert[data-sb-style="alert"] .sb-alert-ok {
|
|
2900
|
+
border-radius: 9999px;
|
|
2901
|
+
background: var(--sb-color-fill);
|
|
2902
|
+
color: var(--sb-color-primary);
|
|
2903
|
+
}
|
|
2904
|
+
|
|
2905
|
+
.sb-alert[data-sb-style="alert"] .sb-alert-buttons > .sb-button::before,
|
|
2906
|
+
.sb-alert[data-sb-style="alert"] .sb-alert-buttons > .sb-button::after,
|
|
2907
|
+
.sb-alert[data-sb-style="alert"] .sb-alert-ok::before {
|
|
2908
|
+
display: none;
|
|
2909
|
+
}
|
|
2910
|
+
|
|
2911
|
+
.sb-alert[data-sb-style="alert"] .sb-alert-buttons > .sb-button:active,
|
|
2912
|
+
.sb-alert[data-sb-style="alert"] .sb-alert-ok:active {
|
|
2913
|
+
background: var(--sb-color-secondary-fill);
|
|
2914
|
+
filter: brightness(0.92);
|
|
2915
|
+
}
|
|
2916
|
+
|
|
2917
|
+
.sb-alert[data-sb-style="dialog"] .sb-alert-buttons > .sb-button[data-sb-role="cancel"] {
|
|
2591
2918
|
font-weight: 600;
|
|
2592
2919
|
}
|
|
2593
2920
|
|
|
2594
|
-
.sb-alert-buttons > .sb-button[data-sb-role="destructive"]
|
|
2921
|
+
.sb-alert-buttons > .sb-button[data-sb-role="destructive"],
|
|
2922
|
+
.sb-alert[data-sb-style="alert"] .sb-alert-buttons > .sb-button[data-sb-role="destructive"] {
|
|
2595
2923
|
color: var(--sb-color-red);
|
|
2596
2924
|
}
|
|
2597
2925
|
|
|
@@ -2672,14 +3000,14 @@ html[data-sb-mode="device"] .status {
|
|
|
2672
3000
|
|
|
2673
3001
|
/* ---------------------------------------------------------------------------
|
|
2674
3002
|
Stepper: the label children are positioned by their frames; the 94×32
|
|
2675
|
-
|
|
2676
|
-
by a
|
|
3003
|
+
capsule (iOS 26) sits at the engine's `control` rect, its halves divided
|
|
3004
|
+
by a short line. A dimmed half (`aria-disabled`) swallows taps.
|
|
2677
3005
|
------------------------------------------------------------------------- */
|
|
2678
3006
|
.sb-stepper-control {
|
|
2679
3007
|
position: absolute;
|
|
2680
3008
|
display: flex;
|
|
2681
3009
|
box-sizing: border-box;
|
|
2682
|
-
border-radius:
|
|
3010
|
+
border-radius: 16px;
|
|
2683
3011
|
background: var(--sb-color-tertiary-fill);
|
|
2684
3012
|
overflow: hidden;
|
|
2685
3013
|
}
|
|
@@ -2703,7 +3031,17 @@ html[data-sb-mode="device"] .status {
|
|
|
2703
3031
|
}
|
|
2704
3032
|
|
|
2705
3033
|
.sb-stepper-button + .sb-stepper-button {
|
|
2706
|
-
|
|
3034
|
+
position: relative;
|
|
3035
|
+
}
|
|
3036
|
+
|
|
3037
|
+
.sb-stepper-button + .sb-stepper-button::before {
|
|
3038
|
+
content: "";
|
|
3039
|
+
position: absolute;
|
|
3040
|
+
left: 0;
|
|
3041
|
+
top: 7px;
|
|
3042
|
+
bottom: 7px;
|
|
3043
|
+
width: 1px;
|
|
3044
|
+
background: var(--sb-color-opaque-separator);
|
|
2707
3045
|
}
|
|
2708
3046
|
|
|
2709
3047
|
.sb-stepper-button > svg {
|
|
@@ -2724,8 +3062,9 @@ html[data-sb-mode="device"] .status {
|
|
|
2724
3062
|
}
|
|
2725
3063
|
|
|
2726
3064
|
/* ---------------------------------------------------------------------------
|
|
2727
|
-
Slider: the label children are positioned by their frames; the
|
|
2728
|
-
the accent fill and the
|
|
3065
|
+
Slider: the label children are positioned by their frames; the 6pt track,
|
|
3066
|
+
the accent fill and the 38 × 24 pill thumb (iOS 26) are chrome at the
|
|
3067
|
+
engine's rects. A
|
|
2729
3068
|
pointer anywhere on the slider moves the thumb (touch-action none).
|
|
2730
3069
|
------------------------------------------------------------------------- */
|
|
2731
3070
|
.sb-slider {
|
|
@@ -2736,20 +3075,20 @@ html[data-sb-mode="device"] .status {
|
|
|
2736
3075
|
|
|
2737
3076
|
.sb-slider-track {
|
|
2738
3077
|
position: absolute;
|
|
2739
|
-
border-radius:
|
|
3078
|
+
border-radius: 3px;
|
|
2740
3079
|
background: var(--sb-color-tertiary-fill);
|
|
2741
3080
|
}
|
|
2742
3081
|
|
|
2743
3082
|
.sb-slider-fill {
|
|
2744
3083
|
position: absolute;
|
|
2745
|
-
border-radius:
|
|
3084
|
+
border-radius: 3px;
|
|
2746
3085
|
background: var(--sb-tint, var(--sb-color-accent));
|
|
2747
3086
|
}
|
|
2748
3087
|
|
|
2749
3088
|
.sb-slider-thumb {
|
|
2750
3089
|
position: absolute;
|
|
2751
3090
|
box-sizing: border-box;
|
|
2752
|
-
border-radius:
|
|
3091
|
+
border-radius: 12px;
|
|
2753
3092
|
background: #ffffff;
|
|
2754
3093
|
box-shadow:
|
|
2755
3094
|
0 3px 8px rgba(0, 0, 0, 0.15),
|
|
@@ -2832,3 +3171,85 @@ html[data-sb-mode="device"] .status {
|
|
|
2832
3171
|
animation-duration: 0.01s;
|
|
2833
3172
|
}
|
|
2834
3173
|
}
|
|
3174
|
+
|
|
3175
|
+
/* Rich text (src/richText.ts): a block per line, the height the engine gave
|
|
3176
|
+
it; fragments share its baseline. An inline image sits on the baseline. */
|
|
3177
|
+
.sb-text-line {
|
|
3178
|
+
display: block;
|
|
3179
|
+
white-space: pre;
|
|
3180
|
+
}
|
|
3181
|
+
|
|
3182
|
+
.sb-text-image {
|
|
3183
|
+
display: inline-block;
|
|
3184
|
+
position: relative;
|
|
3185
|
+
vertical-align: baseline;
|
|
3186
|
+
overflow: hidden;
|
|
3187
|
+
}
|
|
3188
|
+
|
|
3189
|
+
/* A symbol's glyph is centered in its line-height square, as a standalone
|
|
3190
|
+
`Image(systemName:)` is. */
|
|
3191
|
+
.sb-text-symbol {
|
|
3192
|
+
display: inline-flex;
|
|
3193
|
+
vertical-align: text-bottom;
|
|
3194
|
+
}
|
|
3195
|
+
|
|
3196
|
+
/* A link: tinted unless the run has its own foreground. */
|
|
3197
|
+
.sb-link {
|
|
3198
|
+
color: var(--sb-tint, var(--sb-color-accent));
|
|
3199
|
+
cursor: pointer;
|
|
3200
|
+
}
|
|
3201
|
+
|
|
3202
|
+
/* ---------------------------------------------------------------------------
|
|
3203
|
+
A vertical text field (`TextField(_:text:axis: .vertical)`): a textarea the
|
|
3204
|
+
engine sizes to its lines, in the font it measured them in (layoutDom.ts);
|
|
3205
|
+
it scrolls inside past its `lineLimit`.
|
|
3206
|
+
------------------------------------------------------------------------- */
|
|
3207
|
+
textarea.sb-textfield {
|
|
3208
|
+
box-sizing: border-box;
|
|
3209
|
+
margin: 0;
|
|
3210
|
+
padding: 0;
|
|
3211
|
+
resize: none;
|
|
3212
|
+
overflow-y: auto;
|
|
3213
|
+
scrollbar-width: none;
|
|
3214
|
+
white-space: pre-wrap;
|
|
3215
|
+
overflow-wrap: break-word;
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3218
|
+
textarea.sb-textfield[data-sb-style="roundedBorder"] {
|
|
3219
|
+
padding: 5px 7px;
|
|
3220
|
+
}
|
|
3221
|
+
|
|
3222
|
+
/* A section's view header / footer: secondary text above or below the card
|
|
3223
|
+
(the engine sets the fonts of the texts inside: headline headers, footnote
|
|
3224
|
+
footers). */
|
|
3225
|
+
.sb-sectionheader,
|
|
3226
|
+
.sb-sectionfooter {
|
|
3227
|
+
color: var(--sb-color-secondary);
|
|
3228
|
+
text-align: left;
|
|
3229
|
+
}
|
|
3230
|
+
|
|
3231
|
+
/* `.scrollContentBackground(.hidden)`: the list's own background goes; rows keep theirs. */
|
|
3232
|
+
.sb-list[data-sb-background="hidden"] {
|
|
3233
|
+
background: transparent;
|
|
3234
|
+
}
|
|
3235
|
+
|
|
3236
|
+
/* A custom shape's path (src/pathShape.ts): an SVG filling the shape's box,
|
|
3237
|
+
whose view box is the size the path was drawn for. Strokes may lie outside
|
|
3238
|
+
the box, as in SwiftUI. */
|
|
3239
|
+
svg.sb-path {
|
|
3240
|
+
display: block;
|
|
3241
|
+
width: 100%;
|
|
3242
|
+
height: 100%;
|
|
3243
|
+
overflow: visible;
|
|
3244
|
+
pointer-events: none;
|
|
3245
|
+
}
|
|
3246
|
+
|
|
3247
|
+
/* A `Canvas` (src/canvasDrawing.ts): its drawing is clipped to its box. */
|
|
3248
|
+
.sb-canvas {
|
|
3249
|
+
overflow: hidden;
|
|
3250
|
+
}
|
|
3251
|
+
|
|
3252
|
+
.sb-canvas > svg {
|
|
3253
|
+
display: block;
|
|
3254
|
+
overflow: hidden;
|
|
3255
|
+
}
|