@swiftbrowser/web 0.1.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 +600 -0
- package/app/index.html +125 -0
- package/index.html +56 -0
- package/node/dist/node/apps.d.ts +58 -0
- package/node/dist/node/apps.d.ts.map +1 -0
- package/node/dist/node/apps.js +60 -0
- package/node/dist/node/apps.js.map +1 -0
- package/node/dist/node/config.d.ts +42 -0
- package/node/dist/node/config.d.ts.map +1 -0
- package/node/dist/node/config.js +99 -0
- package/node/dist/node/config.js.map +1 -0
- package/node/dist/node/examples.d.ts +39 -0
- package/node/dist/node/examples.d.ts.map +1 -0
- package/node/dist/node/examples.js +116 -0
- package/node/dist/node/examples.js.map +1 -0
- package/node/dist/node/index.d.ts +47 -0
- package/node/dist/node/index.d.ts.map +1 -0
- package/node/dist/node/index.js +52 -0
- package/node/dist/node/index.js.map +1 -0
- package/node/dist/plugins/app-manifests.d.ts +61 -0
- package/node/dist/plugins/app-manifests.d.ts.map +1 -0
- package/node/dist/plugins/app-manifests.js +168 -0
- package/node/dist/plugins/app-manifests.js.map +1 -0
- package/node/dist/plugins/asset-catalogs.d.ts +119 -0
- package/node/dist/plugins/asset-catalogs.d.ts.map +1 -0
- package/node/dist/plugins/asset-catalogs.js +488 -0
- package/node/dist/plugins/asset-catalogs.js.map +1 -0
- package/node/dist/plugins/bundle-resources.d.ts +49 -0
- package/node/dist/plugins/bundle-resources.d.ts.map +1 -0
- package/node/dist/plugins/bundle-resources.js +172 -0
- package/node/dist/plugins/bundle-resources.js.map +1 -0
- package/node/dist/plugins/swift-wasm.d.ts +33 -0
- package/node/dist/plugins/swift-wasm.d.ts.map +1 -0
- package/node/dist/plugins/swift-wasm.js +241 -0
- package/node/dist/plugins/swift-wasm.js.map +1 -0
- package/node/dist/src/assets.d.ts +60 -0
- package/node/dist/src/assets.d.ts.map +1 -0
- package/node/dist/src/assets.js +54 -0
- package/node/dist/src/assets.js.map +1 -0
- package/node/dist/src/devEvents.d.ts +31 -0
- package/node/dist/src/devEvents.d.ts.map +1 -0
- package/node/dist/src/devEvents.js +9 -0
- package/node/dist/src/devEvents.js.map +1 -0
- package/package.json +56 -0
- package/plugins/app-manifests.ts +192 -0
- package/plugins/asset-catalogs.ts +558 -0
- package/plugins/bundle-resources.ts +195 -0
- package/plugins/swift-wasm.ts +259 -0
- package/public/_headers +23 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/icon.svg +14 -0
- package/public/manifest.webmanifest +14 -0
- package/src/animation.ts +521 -0
- package/src/assets.ts +86 -0
- package/src/chartDom.ts +481 -0
- package/src/devEvents.ts +35 -0
- package/src/deviceMode.ts +80 -0
- package/src/ghost.ts +25 -0
- package/src/interaction.ts +1113 -0
- package/src/landing.css +261 -0
- package/src/landing.ts +141 -0
- package/src/layout/chart.ts +477 -0
- package/src/layout/controls.ts +116 -0
- package/src/layout/engine.ts +3581 -0
- package/src/layout/index.ts +91 -0
- package/src/layout/measure.ts +86 -0
- package/src/layout/text.ts +124 -0
- package/src/layout/types.ts +153 -0
- package/src/layout/typography.ts +188 -0
- package/src/layoutDom.ts +549 -0
- package/src/layoutEvents.ts +110 -0
- package/src/locale.ts +64 -0
- package/src/main.ts +554 -0
- package/src/mock.ts +752 -0
- package/src/nodeQueries.ts +60 -0
- package/src/protocol.ts +1421 -0
- package/src/renderer.ts +2882 -0
- package/src/styles.css +2834 -0
- package/src/styles.ts +373 -0
- package/src/swipe.ts +110 -0
- package/src/symbols.ts +595 -0
- package/src/typography.ts +76 -0
- package/src/vite-env.d.ts +26 -0
- package/src/wasm.ts +400 -0
package/src/styles.css
ADDED
|
@@ -0,0 +1,2834 @@
|
|
|
1
|
+
/* ---------------------------------------------------------------------------
|
|
2
|
+
iOS design tokens. Light values live on the device frame; dark ones switch
|
|
3
|
+
with the frame's data-theme so the page chrome and the "phone" are
|
|
4
|
+
independent.
|
|
5
|
+
------------------------------------------------------------------------- */
|
|
6
|
+
|
|
7
|
+
:root {
|
|
8
|
+
/* The UINavigationController push curve: fast start, long settle. */
|
|
9
|
+
--sb-nav-easing: cubic-bezier(0.32, 0.72, 0, 1);
|
|
10
|
+
--sb-font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", Inter,
|
|
11
|
+
system-ui, sans-serif;
|
|
12
|
+
/* Body size for the default (Large) Dynamic Type size; the device frame
|
|
13
|
+
overrides this with the current size (see .device below). */
|
|
14
|
+
--sb-font-size-body: 17px;
|
|
15
|
+
|
|
16
|
+
/* Page chrome (not iOS) */
|
|
17
|
+
--page-bg: #e9e9ee;
|
|
18
|
+
--page-fg: #1c1c1e;
|
|
19
|
+
--page-muted: #6e6e73;
|
|
20
|
+
--page-border: rgba(0, 0, 0, 0.08);
|
|
21
|
+
--page-surface: rgba(255, 255, 255, 0.8);
|
|
22
|
+
--page-error-bg: #ffe5e5;
|
|
23
|
+
--page-error-fg: #a1000d;
|
|
24
|
+
|
|
25
|
+
/* Device dimensions (iPhone 15 Pro logical points) */
|
|
26
|
+
--device-screen-width: 393px;
|
|
27
|
+
--device-screen-height: 852px;
|
|
28
|
+
--device-bezel: 12px;
|
|
29
|
+
--device-radius: 58px;
|
|
30
|
+
--device-safe-top: 59px;
|
|
31
|
+
--device-safe-bottom: 34px;
|
|
32
|
+
|
|
33
|
+
/* The real safe-area insets (device mode). env() is substituted at
|
|
34
|
+
computed-value time, so main.ts reads these with getComputedStyle; they
|
|
35
|
+
are 0 outside standalone (home screen) mode and landscape. */
|
|
36
|
+
--sb-inset-top: env(safe-area-inset-top, 0px);
|
|
37
|
+
--sb-inset-right: env(safe-area-inset-right, 0px);
|
|
38
|
+
--sb-inset-bottom: env(safe-area-inset-bottom, 0px);
|
|
39
|
+
--sb-inset-left: env(safe-area-inset-left, 0px);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@media (prefers-color-scheme: dark) {
|
|
43
|
+
:root {
|
|
44
|
+
--page-bg: #111114;
|
|
45
|
+
--page-fg: #f2f2f7;
|
|
46
|
+
--page-muted: #98989f;
|
|
47
|
+
--page-border: rgba(255, 255, 255, 0.1);
|
|
48
|
+
--page-surface: rgba(28, 28, 30, 0.85);
|
|
49
|
+
--page-error-bg: #3a0b10;
|
|
50
|
+
--page-error-fg: #ff8a8a;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.device {
|
|
55
|
+
/* Dynamic Type: main.ts writes --sb-ts-<style>-size / -lh / -scale on the
|
|
56
|
+
device frame for the selected size (typography.ts); the fallbacks are the
|
|
57
|
+
Large values. Unstyled text is `body` and scales with it. */
|
|
58
|
+
--sb-font-size-body: var(--sb-ts-body-size, 17px);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.device,
|
|
62
|
+
.device[data-theme="light"] {
|
|
63
|
+
color-scheme: light;
|
|
64
|
+
--sb-color-primary: #000000;
|
|
65
|
+
--sb-color-secondary: rgba(60, 60, 67, 0.6);
|
|
66
|
+
--sb-color-accent: #007aff;
|
|
67
|
+
--sb-color-system-background: #ffffff;
|
|
68
|
+
--sb-color-secondary-system-background: #f2f2f7;
|
|
69
|
+
--sb-color-separator: rgba(60, 60, 67, 0.29);
|
|
70
|
+
--sb-color-tertiary-label: rgba(60, 60, 67, 0.3);
|
|
71
|
+
--sb-color-quaternary-label: rgba(60, 60, 67, 0.18);
|
|
72
|
+
--sb-color-red: #ff3b30;
|
|
73
|
+
--sb-color-green: #34c759;
|
|
74
|
+
--sb-color-grouped-background: #f2f2f7;
|
|
75
|
+
--sb-color-grouped-cell: #ffffff;
|
|
76
|
+
--sb-color-fill: rgba(120, 120, 128, 0.2);
|
|
77
|
+
--sb-color-secondary-fill: rgba(120, 120, 128, 0.16);
|
|
78
|
+
--sb-color-tertiary-fill: rgba(118, 118, 128, 0.12);
|
|
79
|
+
--sb-color-quaternary-fill: rgba(116, 116, 128, 0.08);
|
|
80
|
+
/* Phase 6 materials: rgb components of the color blurred over the backdrop. */
|
|
81
|
+
--sb-color-material-base: 255, 255, 255;
|
|
82
|
+
--sb-color-switch-off: #e9e9ea;
|
|
83
|
+
--sb-color-field-border: #c6c6c8;
|
|
84
|
+
--sb-color-field-background: #ffffff;
|
|
85
|
+
--sb-color-placeholder: rgba(60, 60, 67, 0.3);
|
|
86
|
+
--sb-color-sheet-background: #ffffff;
|
|
87
|
+
--sb-color-grabber: rgba(60, 60, 67, 0.3);
|
|
88
|
+
/* Phase 4 */
|
|
89
|
+
--sb-color-tabbar-background: rgba(249, 249, 249, 0.94);
|
|
90
|
+
--sb-color-segment-track: rgba(118, 118, 128, 0.12);
|
|
91
|
+
--sb-color-segment-selected: #ffffff;
|
|
92
|
+
--sb-color-menu-background: rgba(250, 250, 250, 0.96);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.device[data-theme="dark"] {
|
|
96
|
+
color-scheme: dark;
|
|
97
|
+
--sb-color-primary: #ffffff;
|
|
98
|
+
--sb-color-secondary: rgba(235, 235, 245, 0.6);
|
|
99
|
+
--sb-color-accent: #0a84ff;
|
|
100
|
+
--sb-color-system-background: #000000;
|
|
101
|
+
--sb-color-secondary-system-background: #1c1c1e;
|
|
102
|
+
--sb-color-separator: rgba(84, 84, 88, 0.6);
|
|
103
|
+
--sb-color-tertiary-label: rgba(235, 235, 245, 0.3);
|
|
104
|
+
--sb-color-quaternary-label: rgba(235, 235, 245, 0.16);
|
|
105
|
+
--sb-color-red: #ff453a;
|
|
106
|
+
--sb-color-green: #30d158;
|
|
107
|
+
--sb-color-grouped-background: #000000;
|
|
108
|
+
--sb-color-grouped-cell: #1c1c1e;
|
|
109
|
+
--sb-color-fill: rgba(120, 120, 128, 0.36);
|
|
110
|
+
--sb-color-secondary-fill: rgba(120, 120, 128, 0.32);
|
|
111
|
+
--sb-color-tertiary-fill: rgba(118, 118, 128, 0.24);
|
|
112
|
+
--sb-color-quaternary-fill: rgba(118, 118, 128, 0.18);
|
|
113
|
+
--sb-color-material-base: 30, 30, 30;
|
|
114
|
+
--sb-color-switch-off: #39393d;
|
|
115
|
+
--sb-color-field-border: #3a3a3c;
|
|
116
|
+
--sb-color-field-background: #1c1c1e;
|
|
117
|
+
--sb-color-placeholder: rgba(235, 235, 245, 0.3);
|
|
118
|
+
/* Sheets float above the window and take the elevated system background. */
|
|
119
|
+
--sb-color-sheet-background: #1c1c1e;
|
|
120
|
+
--sb-color-grabber: rgba(235, 235, 245, 0.3);
|
|
121
|
+
/* Phase 4 */
|
|
122
|
+
--sb-color-tabbar-background: rgba(29, 29, 29, 0.94);
|
|
123
|
+
--sb-color-segment-track: rgba(118, 118, 128, 0.24);
|
|
124
|
+
--sb-color-segment-selected: #636366;
|
|
125
|
+
--sb-color-menu-background: rgba(37, 37, 37, 0.96);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* ---------------------------------------------------------------------------
|
|
129
|
+
Page layout
|
|
130
|
+
------------------------------------------------------------------------- */
|
|
131
|
+
|
|
132
|
+
*,
|
|
133
|
+
*::before,
|
|
134
|
+
*::after {
|
|
135
|
+
box-sizing: border-box;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
html,
|
|
139
|
+
body {
|
|
140
|
+
margin: 0;
|
|
141
|
+
min-height: 100%;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
body {
|
|
145
|
+
display: flex;
|
|
146
|
+
flex-direction: column;
|
|
147
|
+
min-height: 100vh;
|
|
148
|
+
background: var(--page-bg);
|
|
149
|
+
color: var(--page-fg);
|
|
150
|
+
font-family: var(--sb-font-family);
|
|
151
|
+
font-size: 14px;
|
|
152
|
+
-webkit-font-smoothing: antialiased;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.toolbar {
|
|
156
|
+
position: sticky;
|
|
157
|
+
top: 0;
|
|
158
|
+
z-index: 10;
|
|
159
|
+
display: flex;
|
|
160
|
+
align-items: center;
|
|
161
|
+
gap: 10px;
|
|
162
|
+
padding: 10px 16px;
|
|
163
|
+
background: var(--page-surface);
|
|
164
|
+
border-bottom: 1px solid var(--page-border);
|
|
165
|
+
backdrop-filter: blur(16px);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.toolbar-brand {
|
|
169
|
+
font-weight: 600;
|
|
170
|
+
letter-spacing: -0.01em;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.toolbar-sep {
|
|
174
|
+
width: 1px;
|
|
175
|
+
height: 18px;
|
|
176
|
+
background: var(--page-border);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.toolbar-label {
|
|
180
|
+
color: var(--page-muted);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.toolbar-value {
|
|
184
|
+
font-variant-numeric: tabular-nums;
|
|
185
|
+
padding: 2px 8px;
|
|
186
|
+
border-radius: 6px;
|
|
187
|
+
background: var(--page-border);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.toolbar-spacer {
|
|
191
|
+
flex: 1 1 0;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/* Hot-reload indicator, driven by the dev server's sb:build-* events. */
|
|
195
|
+
.toolbar-build {
|
|
196
|
+
display: inline-flex;
|
|
197
|
+
align-items: center;
|
|
198
|
+
gap: 6px;
|
|
199
|
+
color: var(--page-muted);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.toolbar-build[hidden] {
|
|
203
|
+
display: none;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.toolbar-build::before {
|
|
207
|
+
content: "";
|
|
208
|
+
width: 10px;
|
|
209
|
+
height: 10px;
|
|
210
|
+
border-radius: 50%;
|
|
211
|
+
border: 1.5px solid currentColor;
|
|
212
|
+
border-top-color: transparent;
|
|
213
|
+
animation: sb-spin 0.9s linear infinite;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.toolbar-build[data-state="error"] {
|
|
217
|
+
color: var(--page-error-fg);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.toolbar-build[data-state="error"]::before {
|
|
221
|
+
animation: none;
|
|
222
|
+
border-color: currentColor;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.toolbar-button {
|
|
226
|
+
font: inherit;
|
|
227
|
+
padding: 6px 12px;
|
|
228
|
+
border: 1px solid var(--page-border);
|
|
229
|
+
border-radius: 8px;
|
|
230
|
+
background: transparent;
|
|
231
|
+
color: var(--page-fg);
|
|
232
|
+
cursor: pointer;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.toolbar-button:hover {
|
|
236
|
+
background: var(--page-border);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.toolbar-button[aria-pressed="true"] {
|
|
240
|
+
background: var(--page-fg);
|
|
241
|
+
color: var(--page-bg);
|
|
242
|
+
border-color: transparent;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.toolbar-select {
|
|
246
|
+
font: inherit;
|
|
247
|
+
padding: 5px 8px;
|
|
248
|
+
border: 1px solid var(--page-border);
|
|
249
|
+
border-radius: 8px;
|
|
250
|
+
background: transparent;
|
|
251
|
+
color: var(--page-fg);
|
|
252
|
+
cursor: pointer;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.stage {
|
|
256
|
+
display: flex;
|
|
257
|
+
flex-direction: column;
|
|
258
|
+
align-items: center;
|
|
259
|
+
gap: 16px;
|
|
260
|
+
padding: 32px 16px 48px;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.status {
|
|
264
|
+
max-width: 560px;
|
|
265
|
+
margin: 0;
|
|
266
|
+
padding: 10px 14px;
|
|
267
|
+
border-radius: 10px;
|
|
268
|
+
background: var(--page-error-bg);
|
|
269
|
+
color: var(--page-error-fg);
|
|
270
|
+
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
271
|
+
font-size: 12px;
|
|
272
|
+
white-space: pre-wrap;
|
|
273
|
+
word-break: break-word;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/* ---------------------------------------------------------------------------
|
|
277
|
+
Device frame
|
|
278
|
+
------------------------------------------------------------------------- */
|
|
279
|
+
|
|
280
|
+
.device {
|
|
281
|
+
position: relative;
|
|
282
|
+
flex: 0 0 auto;
|
|
283
|
+
width: calc(var(--device-screen-width) + 2 * var(--device-bezel));
|
|
284
|
+
height: calc(var(--device-screen-height) + 2 * var(--device-bezel));
|
|
285
|
+
padding: var(--device-bezel);
|
|
286
|
+
border-radius: var(--device-radius);
|
|
287
|
+
background: linear-gradient(160deg, #3a3a3f 0%, #141417 45%, #0c0c0e 100%);
|
|
288
|
+
box-shadow:
|
|
289
|
+
0 0 0 1.5px #5a5a60,
|
|
290
|
+
0 0 0 3px #1a1a1d,
|
|
291
|
+
0 30px 60px -20px rgba(0, 0, 0, 0.55),
|
|
292
|
+
0 12px 24px -12px rgba(0, 0, 0, 0.4);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/* Side buttons */
|
|
296
|
+
.device::before,
|
|
297
|
+
.device::after {
|
|
298
|
+
content: "";
|
|
299
|
+
position: absolute;
|
|
300
|
+
background: #2a2a2e;
|
|
301
|
+
border-radius: 2px;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.device::before {
|
|
305
|
+
/* volume up + down (one element, drawn with a gradient gap) */
|
|
306
|
+
left: -4.5px;
|
|
307
|
+
top: 180px;
|
|
308
|
+
width: 3px;
|
|
309
|
+
height: 160px;
|
|
310
|
+
background: linear-gradient(#2a2a2e 0 40%, transparent 40% 60%, #2a2a2e 60%);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.device::after {
|
|
314
|
+
/* power */
|
|
315
|
+
right: -4.5px;
|
|
316
|
+
top: 220px;
|
|
317
|
+
width: 3px;
|
|
318
|
+
height: 100px;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.device-screen {
|
|
322
|
+
position: relative;
|
|
323
|
+
width: var(--device-screen-width);
|
|
324
|
+
height: var(--device-screen-height);
|
|
325
|
+
overflow: hidden;
|
|
326
|
+
border-radius: calc(var(--device-radius) - var(--device-bezel));
|
|
327
|
+
background: var(--sb-color-system-background);
|
|
328
|
+
color: var(--sb-color-primary);
|
|
329
|
+
font-size: var(--sb-font-size-body);
|
|
330
|
+
/* Unitless so fixed-size fonts keep proportional line boxes; text styles
|
|
331
|
+
set their iOS line height in px on the styled box. */
|
|
332
|
+
line-height: 1.3;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.dynamic-island {
|
|
336
|
+
position: absolute;
|
|
337
|
+
top: 11px;
|
|
338
|
+
left: 50%;
|
|
339
|
+
width: 126px;
|
|
340
|
+
height: 37px;
|
|
341
|
+
transform: translateX(-50%);
|
|
342
|
+
border-radius: 20px;
|
|
343
|
+
background: #000;
|
|
344
|
+
z-index: 3;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.status-bar {
|
|
348
|
+
position: absolute;
|
|
349
|
+
top: 0;
|
|
350
|
+
left: 0;
|
|
351
|
+
right: 0;
|
|
352
|
+
height: var(--device-safe-top);
|
|
353
|
+
display: flex;
|
|
354
|
+
align-items: center;
|
|
355
|
+
justify-content: space-between;
|
|
356
|
+
padding: 0 32px 6px 40px;
|
|
357
|
+
pointer-events: none;
|
|
358
|
+
z-index: 2;
|
|
359
|
+
color: var(--sb-color-primary);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.status-time {
|
|
363
|
+
font-size: 17px;
|
|
364
|
+
font-weight: 600;
|
|
365
|
+
letter-spacing: -0.02em;
|
|
366
|
+
font-variant-numeric: tabular-nums;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.status-glyphs {
|
|
370
|
+
display: flex;
|
|
371
|
+
align-items: center;
|
|
372
|
+
gap: 7px;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.glyph {
|
|
376
|
+
display: block;
|
|
377
|
+
fill: currentColor;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.glyph-battery .battery-shell {
|
|
381
|
+
fill: none;
|
|
382
|
+
stroke: currentColor;
|
|
383
|
+
stroke-opacity: 0.4;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.glyph-battery .battery-cap {
|
|
387
|
+
fill: currentColor;
|
|
388
|
+
fill-opacity: 0.4;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.home-indicator {
|
|
392
|
+
position: absolute;
|
|
393
|
+
bottom: 8px;
|
|
394
|
+
left: 50%;
|
|
395
|
+
width: 139px;
|
|
396
|
+
height: 5px;
|
|
397
|
+
transform: translateX(-50%);
|
|
398
|
+
border-radius: 3px;
|
|
399
|
+
background: var(--sb-color-primary);
|
|
400
|
+
pointer-events: none;
|
|
401
|
+
z-index: 2;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/* Root container (element id 0). The layout engine positions every element
|
|
405
|
+
absolutely in screen coordinates (safe areas are part of the proposal), so
|
|
406
|
+
the root is just a clipping box. */
|
|
407
|
+
.screen {
|
|
408
|
+
position: absolute;
|
|
409
|
+
inset: 0;
|
|
410
|
+
overflow: hidden;
|
|
411
|
+
z-index: 1;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
.device[data-state="loading"] .screen::after {
|
|
415
|
+
content: "";
|
|
416
|
+
position: absolute;
|
|
417
|
+
left: 50%;
|
|
418
|
+
top: 50%;
|
|
419
|
+
width: 22px;
|
|
420
|
+
height: 22px;
|
|
421
|
+
margin: -11px 0 0 -11px;
|
|
422
|
+
border-radius: 50%;
|
|
423
|
+
border: 2.5px solid var(--sb-color-secondary);
|
|
424
|
+
border-top-color: transparent;
|
|
425
|
+
animation: sb-spin 0.9s linear infinite;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
@keyframes sb-spin {
|
|
429
|
+
to {
|
|
430
|
+
transform: rotate(360deg);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/* ---------------------------------------------------------------------------
|
|
435
|
+
Device mode (html[data-sb-mode="device"], see src/deviceMode.ts)
|
|
436
|
+
|
|
437
|
+
On a real phone there is no toolbar and no fake bezel: the OS draws the
|
|
438
|
+
status bar and #screen fills the viewport. The layout engine still gets a
|
|
439
|
+
status-bar / home-indicator safe area, but from env(safe-area-inset-*)
|
|
440
|
+
(via --sb-inset-*), so it is only non-zero in standalone mode / landscape.
|
|
441
|
+
------------------------------------------------------------------------- */
|
|
442
|
+
|
|
443
|
+
html[data-sb-mode="device"] {
|
|
444
|
+
height: 100%;
|
|
445
|
+
overflow: hidden;
|
|
446
|
+
overscroll-behavior: none;
|
|
447
|
+
/* No double-tap zoom, no text inflation in landscape. */
|
|
448
|
+
touch-action: manipulation;
|
|
449
|
+
-webkit-text-size-adjust: 100%;
|
|
450
|
+
text-size-adjust: 100%;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
html[data-sb-mode="device"] body {
|
|
454
|
+
min-height: 100dvh;
|
|
455
|
+
overflow: hidden;
|
|
456
|
+
overscroll-behavior: none;
|
|
457
|
+
touch-action: manipulation;
|
|
458
|
+
background: var(--sb-color-system-background, var(--page-bg));
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
html[data-sb-mode="device"] .toolbar {
|
|
462
|
+
display: none;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
html[data-sb-mode="device"] .stage {
|
|
466
|
+
padding: 0;
|
|
467
|
+
gap: 0;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
html[data-sb-mode="device"] .device {
|
|
471
|
+
position: fixed;
|
|
472
|
+
top: 0;
|
|
473
|
+
left: 0;
|
|
474
|
+
width: 100dvw;
|
|
475
|
+
height: 100dvh;
|
|
476
|
+
padding: 0;
|
|
477
|
+
border-radius: 0;
|
|
478
|
+
background: var(--sb-color-system-background);
|
|
479
|
+
box-shadow: none;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
html[data-sb-mode="device"] .device::before,
|
|
483
|
+
html[data-sb-mode="device"] .device::after,
|
|
484
|
+
html[data-sb-mode="device"] .dynamic-island,
|
|
485
|
+
html[data-sb-mode="device"] .status-bar,
|
|
486
|
+
html[data-sb-mode="device"] .home-indicator {
|
|
487
|
+
display: none;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
html[data-sb-mode="device"] .device-screen {
|
|
491
|
+
width: 100dvw;
|
|
492
|
+
height: 100dvh;
|
|
493
|
+
border-radius: 0;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/* The error banner floats over the app, clear of the home indicator. */
|
|
497
|
+
html[data-sb-mode="device"] .status {
|
|
498
|
+
position: fixed;
|
|
499
|
+
left: 16px;
|
|
500
|
+
right: 16px;
|
|
501
|
+
bottom: calc(16px + var(--sb-inset-bottom));
|
|
502
|
+
z-index: 10;
|
|
503
|
+
max-width: none;
|
|
504
|
+
max-height: 40dvh;
|
|
505
|
+
overflow: auto;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/* ---------------------------------------------------------------------------
|
|
509
|
+
Rendered elements
|
|
510
|
+
|
|
511
|
+
Every protocol element is an absolutely positioned box whose left/top/
|
|
512
|
+
width/height come from the layout engine (src/layoutDom.ts). Slots of
|
|
513
|
+
compound kinds (.sb-slot) stay static so their children are positioned
|
|
514
|
+
against the compound element itself; the content boxes of lists and scroll
|
|
515
|
+
views are the exception (they scroll).
|
|
516
|
+
------------------------------------------------------------------------- */
|
|
517
|
+
|
|
518
|
+
.screen [data-sb-id],
|
|
519
|
+
.screen [data-sb-ghost] {
|
|
520
|
+
position: absolute;
|
|
521
|
+
box-sizing: border-box;
|
|
522
|
+
margin: 0;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
.sb-slot {
|
|
526
|
+
position: static;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/* Text: the engine hands over the exact lines, joined with newlines.
|
|
530
|
+
Phase 6: a gradient foreground is painted as the text's own background
|
|
531
|
+
clipped to the glyphs. The styled box sets `--sb-text-gradient`,
|
|
532
|
+
`--sb-text-clip` and `--sb-text-color` (inherited by every text below it;
|
|
533
|
+
a nested flat foreground resets them to `initial`). With the properties
|
|
534
|
+
unset the declarations are invalid at computed-value time: the image and
|
|
535
|
+
clip fall back, and `color` becomes `unset`, i.e. inherits as before. */
|
|
536
|
+
.sb-text {
|
|
537
|
+
display: block;
|
|
538
|
+
white-space: pre;
|
|
539
|
+
overflow: visible;
|
|
540
|
+
text-align: left;
|
|
541
|
+
background-image: var(--sb-text-gradient, none);
|
|
542
|
+
-webkit-background-clip: var(--sb-text-clip, border-box);
|
|
543
|
+
background-clip: var(--sb-text-clip, border-box);
|
|
544
|
+
color: var(--sb-text-color);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
.sb-vstack,
|
|
548
|
+
.sb-hstack,
|
|
549
|
+
.sb-zstack,
|
|
550
|
+
.sb-styled,
|
|
551
|
+
.sb-spacer {
|
|
552
|
+
display: block;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
.sb-button {
|
|
556
|
+
appearance: none;
|
|
557
|
+
display: block;
|
|
558
|
+
padding: 0;
|
|
559
|
+
border: 0;
|
|
560
|
+
background: none;
|
|
561
|
+
color: var(--sb-button-color, var(--sb-tint, var(--sb-color-accent)));
|
|
562
|
+
font: inherit;
|
|
563
|
+
font-size: var(--sb-font-size-body);
|
|
564
|
+
line-height: 1.3;
|
|
565
|
+
text-align: left;
|
|
566
|
+
cursor: pointer;
|
|
567
|
+
touch-action: manipulation;
|
|
568
|
+
-webkit-tap-highlight-color: transparent;
|
|
569
|
+
transition: opacity 0.1s ease;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
.sb-button:active {
|
|
573
|
+
opacity: 0.3;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
.sb-button:focus-visible {
|
|
577
|
+
outline: 2px solid var(--sb-color-accent);
|
|
578
|
+
outline-offset: 4px;
|
|
579
|
+
border-radius: 6px;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/* ---------------------------------------------------------------------------
|
|
583
|
+
Phase 2: button styles, disabled, image, divider, scrollview
|
|
584
|
+
------------------------------------------------------------------------- */
|
|
585
|
+
|
|
586
|
+
.sb-button[data-sb-style="bordered"],
|
|
587
|
+
.sb-button[data-sb-style="borderedProminent"] {
|
|
588
|
+
border-radius: 9999px;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.sb-button[data-sb-style="bordered"] {
|
|
592
|
+
background: var(--sb-color-fill);
|
|
593
|
+
color: var(--sb-button-color, var(--sb-tint, var(--sb-color-accent)));
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
.sb-button[data-sb-style="borderedProminent"] {
|
|
597
|
+
background: var(--sb-tint, var(--sb-color-accent));
|
|
598
|
+
color: #ffffff;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
.sb-button[data-sb-role="destructive"] {
|
|
602
|
+
color: var(--sb-color-red);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
.sb-button[data-sb-style="borderedProminent"][data-sb-role="destructive"] {
|
|
606
|
+
background: var(--sb-color-red);
|
|
607
|
+
color: #ffffff;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/* .disabled(true): opacity and pointer-events are set inline by the renderer
|
|
611
|
+
(so update() clears them); the attribute carries the state for tooling. */
|
|
612
|
+
[data-sb-disabled] {
|
|
613
|
+
user-select: none;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/* Image(systemName:): a 1em glyph centered in its line-height square; the
|
|
617
|
+
renderer sets font-size from the resolved font. */
|
|
618
|
+
.sb-image {
|
|
619
|
+
display: flex;
|
|
620
|
+
align-items: center;
|
|
621
|
+
justify-content: center;
|
|
622
|
+
line-height: 1;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
.sb-image > svg {
|
|
626
|
+
display: block;
|
|
627
|
+
width: 1em;
|
|
628
|
+
height: 1em;
|
|
629
|
+
stroke: currentColor;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/* Unknown symbol or catalog image: a dashed box named by `title`. */
|
|
633
|
+
.sb-image[data-sb-missing] {
|
|
634
|
+
border: 1px dashed currentColor;
|
|
635
|
+
border-radius: 2px;
|
|
636
|
+
opacity: 0.6;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/* Phase 5: a catalog image. The layout engine decides the frame (intrinsic
|
|
640
|
+
size, or the proposal for `.resizable()`), so the bitmap fills the box;
|
|
641
|
+
`.aspectRatio` fitting is a layout concern, not object-fit's. */
|
|
642
|
+
.sb-image > img {
|
|
643
|
+
display: block;
|
|
644
|
+
width: 100%;
|
|
645
|
+
height: 100%;
|
|
646
|
+
object-fit: fill;
|
|
647
|
+
pointer-events: none;
|
|
648
|
+
user-select: none;
|
|
649
|
+
-webkit-user-select: none;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/* Phase 5: a `.symbolset` SVG drawn as a template: the SVG masks a box
|
|
653
|
+
filled with the current foreground color, like an SF Symbol. */
|
|
654
|
+
.sb-image > .sb-image-mask {
|
|
655
|
+
display: block;
|
|
656
|
+
width: 1em;
|
|
657
|
+
height: 1em;
|
|
658
|
+
background-color: currentColor;
|
|
659
|
+
mask-size: contain;
|
|
660
|
+
mask-repeat: no-repeat;
|
|
661
|
+
mask-position: center;
|
|
662
|
+
-webkit-mask-size: contain;
|
|
663
|
+
-webkit-mask-repeat: no-repeat;
|
|
664
|
+
-webkit-mask-position: center;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
.sb-divider {
|
|
668
|
+
background: var(--sb-color-separator);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
/* ScrollView: the engine sizes the viewport; the inner content box is sized
|
|
672
|
+
to the content so the browser scrolls it. iOS-style invisible scrollbars. */
|
|
673
|
+
.sb-scrollview {
|
|
674
|
+
overflow: hidden;
|
|
675
|
+
scrollbar-width: none;
|
|
676
|
+
-webkit-overflow-scrolling: touch;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
.sb-scrollview::-webkit-scrollbar,
|
|
680
|
+
.sb-list::-webkit-scrollbar {
|
|
681
|
+
display: none;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
.sb-scrollview[data-sb-axes="vertical"] {
|
|
685
|
+
overflow-y: auto;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
.sb-scrollview[data-sb-axes="horizontal"] {
|
|
689
|
+
overflow-x: auto;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
.sb-scrollview[data-sb-axes="both"] {
|
|
693
|
+
overflow: auto;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
/* Phase 12: `.scrollTargetBehavior(.viewAligned)` / `.paging` snap the scroll
|
|
697
|
+
view on its axis; the children of the `.scrollTargetLayout()` stack (or of
|
|
698
|
+
the content, for paging) are the snap points. */
|
|
699
|
+
.sb-scrollview[data-sb-snap][data-sb-axes="horizontal"] {
|
|
700
|
+
scroll-snap-type: x mandatory;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
.sb-scrollview[data-sb-snap][data-sb-axes="vertical"] {
|
|
704
|
+
scroll-snap-type: y mandatory;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
.sb-scrollview[data-sb-snap][data-sb-axes="both"] {
|
|
708
|
+
scroll-snap-type: both mandatory;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
[data-sb-scroll-target] > * {
|
|
712
|
+
scroll-snap-align: start;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
.sb-scrollview[data-sb-snap="paging"] > .sb-scroll-content > * {
|
|
716
|
+
scroll-snap-align: start;
|
|
717
|
+
scroll-snap-stop: always;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
.sb-scroll-content,
|
|
721
|
+
.sb-list-content {
|
|
722
|
+
position: relative;
|
|
723
|
+
display: block;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/* ---------------------------------------------------------------------------
|
|
727
|
+
Phase 2: text fields and toggles
|
|
728
|
+
------------------------------------------------------------------------- */
|
|
729
|
+
|
|
730
|
+
.sb-textfield {
|
|
731
|
+
appearance: none;
|
|
732
|
+
display: block;
|
|
733
|
+
font: inherit;
|
|
734
|
+
font-size: var(--sb-font-size-body);
|
|
735
|
+
line-height: 1.3;
|
|
736
|
+
color: var(--sb-color-primary);
|
|
737
|
+
background: transparent;
|
|
738
|
+
border: 0;
|
|
739
|
+
outline: none;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
.sb-textfield::placeholder {
|
|
743
|
+
color: var(--sb-color-placeholder);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
.sb-textfield[data-sb-style="roundedBorder"] {
|
|
747
|
+
padding: 0 7px;
|
|
748
|
+
border: 1px solid var(--sb-color-field-border);
|
|
749
|
+
border-radius: 5px;
|
|
750
|
+
background: var(--sb-color-field-background);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
.sb-textfield[data-sb-style="roundedBorder"]:focus-visible {
|
|
754
|
+
border-color: var(--sb-color-accent);
|
|
755
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--sb-color-accent) 30%, transparent);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/* Toggle: the label children are positioned by their frames; the switch sits
|
|
759
|
+
at the engine's `switch` rect (51×31 at the trailing edge). */
|
|
760
|
+
.sb-switch {
|
|
761
|
+
appearance: none;
|
|
762
|
+
position: absolute;
|
|
763
|
+
width: 51px;
|
|
764
|
+
height: 31px;
|
|
765
|
+
margin: 0;
|
|
766
|
+
padding: 0;
|
|
767
|
+
border: 0;
|
|
768
|
+
border-radius: 9999px;
|
|
769
|
+
background: var(--sb-color-switch-off);
|
|
770
|
+
cursor: pointer;
|
|
771
|
+
transition: background-color 0.25s ease;
|
|
772
|
+
touch-action: manipulation;
|
|
773
|
+
-webkit-tap-highlight-color: transparent;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
.sb-switch[aria-checked="true"] {
|
|
777
|
+
background: var(--sb-tint, var(--sb-color-green));
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
.sb-switch:focus-visible {
|
|
781
|
+
outline: 2px solid var(--sb-color-accent);
|
|
782
|
+
outline-offset: 2px;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
.sb-switch-knob {
|
|
786
|
+
position: absolute;
|
|
787
|
+
top: 2px;
|
|
788
|
+
left: 2px;
|
|
789
|
+
width: 27px;
|
|
790
|
+
height: 27px;
|
|
791
|
+
border-radius: 50%;
|
|
792
|
+
background: #ffffff;
|
|
793
|
+
box-shadow:
|
|
794
|
+
0 3px 8px rgba(0, 0, 0, 0.15),
|
|
795
|
+
0 3px 1px rgba(0, 0, 0, 0.06);
|
|
796
|
+
transition: transform 0.25s cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
.sb-switch[aria-checked="true"] .sb-switch-knob {
|
|
800
|
+
transform: translateX(20px);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/* ---------------------------------------------------------------------------
|
|
804
|
+
Phase 2: lists (iOS inset grouped). Geometry comes from the engine: the
|
|
805
|
+
list is the scrolling viewport, sections carry the inset card (`rows`),
|
|
806
|
+
every row's DOM box is the full-width row inside the card.
|
|
807
|
+
------------------------------------------------------------------------- */
|
|
808
|
+
|
|
809
|
+
.sb-list {
|
|
810
|
+
background: var(--sb-color-grouped-background);
|
|
811
|
+
overflow-y: auto;
|
|
812
|
+
scrollbar-width: none;
|
|
813
|
+
--sb-icon-knockout: var(--sb-color-grouped-cell);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
.sb-section-header,
|
|
817
|
+
.sb-section-footer {
|
|
818
|
+
position: absolute;
|
|
819
|
+
white-space: pre;
|
|
820
|
+
color: var(--sb-color-secondary);
|
|
821
|
+
text-align: left;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
.sb-section-header[hidden],
|
|
825
|
+
.sb-section-footer[hidden] {
|
|
826
|
+
display: none;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
.sb-section-rows {
|
|
830
|
+
position: absolute;
|
|
831
|
+
border-radius: 10px;
|
|
832
|
+
background: var(--sb-color-grouped-cell);
|
|
833
|
+
overflow: hidden;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/* Rows: every direct child of a section's card, and every non-section child
|
|
837
|
+
of a list (loose rows form their own card). */
|
|
838
|
+
.sb-section-rows > [data-sb-id],
|
|
839
|
+
.sb-section-rows > [data-sb-ghost],
|
|
840
|
+
.sb-list-content > :not(.sb-section) {
|
|
841
|
+
background: var(--sb-color-grouped-cell);
|
|
842
|
+
text-align: left;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
.sb-list-content > :not(.sb-section):first-child,
|
|
846
|
+
.sb-list-content > .sb-section + :not(.sb-section) {
|
|
847
|
+
border-top-left-radius: 10px;
|
|
848
|
+
border-top-right-radius: 10px;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
.sb-list-content > :not(.sb-section):last-child,
|
|
852
|
+
.sb-list-content > :not(.sb-section):has(+ .sb-section) {
|
|
853
|
+
border-bottom-left-radius: 10px;
|
|
854
|
+
border-bottom-right-radius: 10px;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
/* Hairline separators, inset 16px from the leading edge. */
|
|
858
|
+
.sb-section-rows > :not(:last-child)::after,
|
|
859
|
+
.sb-list-content > :not(.sb-section):not(:last-child):not(:has(+ .sb-section))::after {
|
|
860
|
+
content: "";
|
|
861
|
+
position: absolute;
|
|
862
|
+
left: 16px;
|
|
863
|
+
right: 0;
|
|
864
|
+
bottom: 0;
|
|
865
|
+
height: 1px;
|
|
866
|
+
background: var(--sb-color-separator);
|
|
867
|
+
pointer-events: none;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
.sb-section-rows > .sb-button,
|
|
871
|
+
.sb-list-content > .sb-button {
|
|
872
|
+
border-radius: 0;
|
|
873
|
+
font-size: var(--sb-font-size-body);
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
.sb-section-rows > .sb-button:active,
|
|
877
|
+
.sb-list-content > .sb-button:active {
|
|
878
|
+
opacity: 1;
|
|
879
|
+
background: var(--sb-color-fill);
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
.sb-section-rows > .sb-navlink:active,
|
|
883
|
+
.sb-list-content > .sb-navlink:active {
|
|
884
|
+
background: var(--sb-color-fill);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
/* ---------------------------------------------------------------------------
|
|
888
|
+
Phase 2: navigation
|
|
889
|
+
------------------------------------------------------------------------- */
|
|
890
|
+
|
|
891
|
+
.sb-navstack {
|
|
892
|
+
overflow: hidden;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
.sb-navscreen {
|
|
896
|
+
background: var(--sb-color-system-background);
|
|
897
|
+
will-change: transform;
|
|
898
|
+
transition:
|
|
899
|
+
transform 0.4s var(--sb-nav-easing),
|
|
900
|
+
visibility 0s linear 0s;
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
/* A screen with a list as its content takes the grouped background, like a
|
|
904
|
+
UINavigationController hosting an inset-grouped table view. */
|
|
905
|
+
.sb-navscreen:has(> .sb-navscreen-body > .sb-navscreen-content > .sb-list) {
|
|
906
|
+
background: var(--sb-color-grouped-background);
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
/* Screens below the top one slide partially left and are hidden once the
|
|
910
|
+
transition has finished. */
|
|
911
|
+
.sb-navscreen[data-sb-nav-position="below"] {
|
|
912
|
+
transform: translateX(-30%);
|
|
913
|
+
visibility: hidden;
|
|
914
|
+
transition:
|
|
915
|
+
transform 0.4s var(--sb-nav-easing),
|
|
916
|
+
visibility 0s linear 0.4s;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
.sb-navscreen--push {
|
|
920
|
+
animation: sb-nav-push 0.4s var(--sb-nav-easing) both;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
.sb-navstack > .sb-navscreen--pop {
|
|
924
|
+
z-index: 2;
|
|
925
|
+
pointer-events: none;
|
|
926
|
+
visibility: visible;
|
|
927
|
+
box-shadow: -8px 0 24px rgba(0, 0, 0, 0.12);
|
|
928
|
+
animation: sb-nav-pop 0.4s var(--sb-nav-easing) both;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
@keyframes sb-nav-push {
|
|
932
|
+
from {
|
|
933
|
+
transform: translateX(100%);
|
|
934
|
+
box-shadow: -8px 0 24px rgba(0, 0, 0, 0.12);
|
|
935
|
+
}
|
|
936
|
+
to {
|
|
937
|
+
transform: translateX(0);
|
|
938
|
+
box-shadow: -8px 0 24px rgba(0, 0, 0, 0);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
@keyframes sb-nav-pop {
|
|
943
|
+
from {
|
|
944
|
+
transform: translateX(0);
|
|
945
|
+
}
|
|
946
|
+
to {
|
|
947
|
+
transform: translateX(100%);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
/* Navigation bar: the engine's `bar` rect (status bar + 44); the renderer
|
|
952
|
+
pads the status bar height so the 44px row sits at the bottom. */
|
|
953
|
+
.sb-navbar {
|
|
954
|
+
position: absolute;
|
|
955
|
+
display: grid;
|
|
956
|
+
grid-template-columns: 1fr auto 1fr;
|
|
957
|
+
align-items: center;
|
|
958
|
+
padding: 0 8px;
|
|
959
|
+
box-sizing: border-box;
|
|
960
|
+
background: transparent;
|
|
961
|
+
/* Phase 8: content that ignores the safe area paints under the bar; the
|
|
962
|
+
bar itself lets pointers through to toolbar items laid out over it. */
|
|
963
|
+
z-index: 1;
|
|
964
|
+
pointer-events: none;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
.sb-navbar > * {
|
|
968
|
+
pointer-events: auto;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
.sb-navbar-trailing:empty {
|
|
972
|
+
pointer-events: none;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
.sb-navbar-back {
|
|
976
|
+
appearance: none;
|
|
977
|
+
grid-column: 1;
|
|
978
|
+
justify-self: start;
|
|
979
|
+
display: inline-flex;
|
|
980
|
+
align-items: center;
|
|
981
|
+
gap: 2px;
|
|
982
|
+
/* Width comes from the layout engine (chrome.back); this is only a fallback. */
|
|
983
|
+
max-width: 45%;
|
|
984
|
+
margin: 0;
|
|
985
|
+
padding: 0;
|
|
986
|
+
border: 0;
|
|
987
|
+
background: none;
|
|
988
|
+
color: var(--sb-color-accent);
|
|
989
|
+
font: inherit;
|
|
990
|
+
font-size: var(--sb-font-size-body);
|
|
991
|
+
line-height: 1.3;
|
|
992
|
+
cursor: pointer;
|
|
993
|
+
touch-action: manipulation;
|
|
994
|
+
-webkit-tap-highlight-color: transparent;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
.sb-navbar-back[hidden] {
|
|
998
|
+
display: none;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
.sb-navbar-back:active {
|
|
1002
|
+
opacity: 0.3;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
.sb-navbar-back > svg {
|
|
1006
|
+
width: 1.4em;
|
|
1007
|
+
height: 1.4em;
|
|
1008
|
+
margin-left: -4px;
|
|
1009
|
+
stroke-width: 2.5;
|
|
1010
|
+
flex-shrink: 0;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
.sb-navbar-back-label {
|
|
1014
|
+
overflow: hidden;
|
|
1015
|
+
text-overflow: ellipsis;
|
|
1016
|
+
white-space: nowrap;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
.sb-navbar-title {
|
|
1020
|
+
/* Positioned at the engine's `title` rect (Phase 6), centered and truncated
|
|
1021
|
+
to the room the back button and the toolbar items leave. Not a grid item:
|
|
1022
|
+
an absolutely positioned grid child would be placed against its grid
|
|
1023
|
+
area, not the bar's padding box. */
|
|
1024
|
+
position: absolute;
|
|
1025
|
+
display: block;
|
|
1026
|
+
max-width: 220px;
|
|
1027
|
+
overflow: hidden;
|
|
1028
|
+
text-overflow: ellipsis;
|
|
1029
|
+
white-space: nowrap;
|
|
1030
|
+
text-align: center;
|
|
1031
|
+
font-size: var(--sb-ts-headline-size, 17px);
|
|
1032
|
+
font-weight: 600;
|
|
1033
|
+
color: var(--sb-color-primary);
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
.sb-navbar-title[hidden] {
|
|
1037
|
+
display: none;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
.sb-navbar-trailing {
|
|
1041
|
+
grid-column: 3;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
.sb-navscreen-body,
|
|
1045
|
+
.sb-navscreen-content {
|
|
1046
|
+
position: static;
|
|
1047
|
+
display: block;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
/* Large title: the engine's `largeTitle` rect (52px row), text already
|
|
1051
|
+
truncated to one line. */
|
|
1052
|
+
.sb-navbar-large-title {
|
|
1053
|
+
position: absolute;
|
|
1054
|
+
z-index: 1; /* Phase 8: above content that ignores the safe area */
|
|
1055
|
+
display: flex;
|
|
1056
|
+
align-items: center;
|
|
1057
|
+
padding: 0 16px;
|
|
1058
|
+
box-sizing: border-box;
|
|
1059
|
+
font-size: var(--sb-ts-largeTitle-size, 34px);
|
|
1060
|
+
font-weight: 700;
|
|
1061
|
+
letter-spacing: -0.01em;
|
|
1062
|
+
text-align: left;
|
|
1063
|
+
color: var(--sb-color-primary);
|
|
1064
|
+
white-space: pre;
|
|
1065
|
+
overflow: hidden;
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
.sb-navbar-large-title[hidden] {
|
|
1069
|
+
display: none;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
/* NavigationLink: a tappable label. Inside a list it is a row with a chevron
|
|
1073
|
+
at the engine's `chevron` rect; elsewhere it reads as plain accent text. */
|
|
1074
|
+
.sb-navlink {
|
|
1075
|
+
color: var(--sb-color-accent);
|
|
1076
|
+
cursor: pointer;
|
|
1077
|
+
touch-action: manipulation;
|
|
1078
|
+
-webkit-tap-highlight-color: transparent;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
.sb-navlink:focus-visible {
|
|
1082
|
+
outline: 2px solid var(--sb-color-accent);
|
|
1083
|
+
outline-offset: 2px;
|
|
1084
|
+
border-radius: 6px;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
.sb-navlink-chevron {
|
|
1088
|
+
position: absolute;
|
|
1089
|
+
stroke-width: 2.5;
|
|
1090
|
+
color: var(--sb-color-tertiary-label);
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
.sb-navlink-chevron[hidden] {
|
|
1094
|
+
display: none;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
.sb-section-rows > .sb-navlink,
|
|
1098
|
+
.sb-list-content > .sb-navlink {
|
|
1099
|
+
color: var(--sb-color-primary);
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1103
|
+
.sb-navscreen,
|
|
1104
|
+
.sb-navscreen--push,
|
|
1105
|
+
.sb-navstack > .sb-navscreen--pop,
|
|
1106
|
+
.sb-switch,
|
|
1107
|
+
.sb-switch-knob {
|
|
1108
|
+
animation-duration: 0.01s;
|
|
1109
|
+
transition-duration: 0.01s;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
/* ---------------------------------------------------------------------------
|
|
1114
|
+
Phase 3: color and shape views (the engine sizes them to their proposal)
|
|
1115
|
+
------------------------------------------------------------------------- */
|
|
1116
|
+
|
|
1117
|
+
.sb-color,
|
|
1118
|
+
.sb-shape {
|
|
1119
|
+
display: block;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
/* An exit clone of a removed element, positioned by the Animator. */
|
|
1123
|
+
.sb-exit-clone {
|
|
1124
|
+
pointer-events: none;
|
|
1125
|
+
user-select: none;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
/* ---------------------------------------------------------------------------
|
|
1129
|
+
Phase 3: sheets
|
|
1130
|
+
------------------------------------------------------------------------- */
|
|
1131
|
+
|
|
1132
|
+
/* A sheet is a root-level layer covering the screen: a scrim over the
|
|
1133
|
+
presenting tree and the card at the engine's frame (large: screen height −
|
|
1134
|
+
status bar − 10; medium: half), sliding up from the bottom. */
|
|
1135
|
+
.screen > .sb-sheet,
|
|
1136
|
+
.sb-sheet {
|
|
1137
|
+
position: absolute;
|
|
1138
|
+
inset: 0;
|
|
1139
|
+
width: auto;
|
|
1140
|
+
height: auto;
|
|
1141
|
+
z-index: 5;
|
|
1142
|
+
display: block;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
.sb-sheet-scrim {
|
|
1146
|
+
position: absolute;
|
|
1147
|
+
inset: 0;
|
|
1148
|
+
background: rgba(0, 0, 0, 0.4);
|
|
1149
|
+
animation: sb-sheet-scrim-in 0.4s ease-out both;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
.sb-sheet-card {
|
|
1153
|
+
position: absolute;
|
|
1154
|
+
box-sizing: border-box;
|
|
1155
|
+
border-radius: 10px 10px 0 0;
|
|
1156
|
+
background: var(--sb-color-sheet-background);
|
|
1157
|
+
box-shadow: 0 -2px 20px rgba(0, 0, 0, 0.15);
|
|
1158
|
+
overflow: hidden;
|
|
1159
|
+
will-change: transform;
|
|
1160
|
+
touch-action: none;
|
|
1161
|
+
transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
.sb-sheet-grabber {
|
|
1165
|
+
position: absolute;
|
|
1166
|
+
border-radius: 2.5px;
|
|
1167
|
+
background: var(--sb-color-grabber);
|
|
1168
|
+
z-index: 1;
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
.sb-sheet-content {
|
|
1172
|
+
position: static;
|
|
1173
|
+
display: block;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
/* Present: the card slides up over 0.4s while the scrim fades in. */
|
|
1177
|
+
.sb-sheet--in > .sb-sheet-card {
|
|
1178
|
+
animation: sb-sheet-in 0.4s cubic-bezier(0.2, 0.8, 0.2, 1) both;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
/* Dismiss: the clone slides down from where a drag left it and fades its scrim. */
|
|
1182
|
+
.sb-sheet--out {
|
|
1183
|
+
pointer-events: none;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
.sb-sheet--out > .sb-sheet-card {
|
|
1187
|
+
transition: none;
|
|
1188
|
+
animation: sb-sheet-out 0.4s cubic-bezier(0.4, 0, 1, 1) both;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
.sb-sheet--out > .sb-sheet-scrim {
|
|
1192
|
+
animation: sb-sheet-scrim-out 0.4s ease-out both;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
/* While dragging the card follows the pointer (inline transform). */
|
|
1196
|
+
.sb-sheet[data-sb-dragging] > .sb-sheet-card {
|
|
1197
|
+
transition: none;
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
/* Sheets under the top one park slightly scaled, like iOS stacks them. */
|
|
1201
|
+
.sb-sheet[data-sb-sheet-position="below"] > .sb-sheet-card {
|
|
1202
|
+
transform: translateY(-10px) scale(0.92);
|
|
1203
|
+
transform-origin: 50% 0;
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
/* Behind a `large` sheet the presenting tree scales to 0.92 and dims. */
|
|
1207
|
+
.screen > :not(.sb-sheet) {
|
|
1208
|
+
transform-origin: 50% 0;
|
|
1209
|
+
transition: transform 0.4s cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
.screen[data-sb-sheet="large"] > :not(.sb-sheet, .sb-exit-clone, .sb-picker-menu) {
|
|
1213
|
+
transform: scale(0.92);
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
@keyframes sb-sheet-in {
|
|
1217
|
+
from {
|
|
1218
|
+
transform: translateY(100%);
|
|
1219
|
+
}
|
|
1220
|
+
to {
|
|
1221
|
+
transform: translateY(0);
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
@keyframes sb-sheet-out {
|
|
1226
|
+
from {
|
|
1227
|
+
transform: translateY(var(--sb-sheet-drag, 0px));
|
|
1228
|
+
}
|
|
1229
|
+
to {
|
|
1230
|
+
transform: translateY(100%);
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
@keyframes sb-sheet-scrim-in {
|
|
1235
|
+
from {
|
|
1236
|
+
opacity: 0;
|
|
1237
|
+
}
|
|
1238
|
+
to {
|
|
1239
|
+
opacity: 1;
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
@keyframes sb-sheet-scrim-out {
|
|
1244
|
+
from {
|
|
1245
|
+
opacity: 1;
|
|
1246
|
+
}
|
|
1247
|
+
to {
|
|
1248
|
+
opacity: 0;
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1253
|
+
.sb-sheet-scrim,
|
|
1254
|
+
.sb-sheet-card,
|
|
1255
|
+
.sb-sheet--in > .sb-sheet-card,
|
|
1256
|
+
.sb-sheet--out > .sb-sheet-card,
|
|
1257
|
+
.sb-sheet--out > .sb-sheet-scrim,
|
|
1258
|
+
.screen > :not(.sb-sheet) {
|
|
1259
|
+
animation-duration: 0.01s;
|
|
1260
|
+
transition-duration: 0.01s;
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
/* ---------------------------------------------------------------------------
|
|
1265
|
+
Phase 4: list styles. `grouped` sections are full width with hairlines
|
|
1266
|
+
instead of inset cards; `plain` has no cards and the system background.
|
|
1267
|
+
The geometry (no inset, no gaps) comes from the engine; this only drops the
|
|
1268
|
+
radii and swaps the backgrounds.
|
|
1269
|
+
------------------------------------------------------------------------- */
|
|
1270
|
+
|
|
1271
|
+
.sb-list[data-sb-list-style="grouped"] .sb-section-rows,
|
|
1272
|
+
.sb-list[data-sb-list-style="plain"] .sb-section-rows,
|
|
1273
|
+
.sb-list[data-sb-list-style="grouped"] > .sb-list-content > :not(.sb-section),
|
|
1274
|
+
.sb-list[data-sb-list-style="plain"] > .sb-list-content > :not(.sb-section) {
|
|
1275
|
+
border-radius: 0;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
.sb-list[data-sb-list-style="grouped"] .sb-section-rows {
|
|
1279
|
+
box-shadow:
|
|
1280
|
+
0 -1px 0 var(--sb-color-separator),
|
|
1281
|
+
0 1px 0 var(--sb-color-separator);
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
.sb-list[data-sb-list-style="plain"] {
|
|
1285
|
+
background: var(--sb-color-system-background);
|
|
1286
|
+
--sb-icon-knockout: var(--sb-color-system-background);
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
.sb-list[data-sb-list-style="plain"] .sb-section-rows,
|
|
1290
|
+
.sb-list[data-sb-list-style="plain"] .sb-section-rows > [data-sb-id],
|
|
1291
|
+
.sb-list[data-sb-list-style="plain"] .sb-section-rows > [data-sb-ghost],
|
|
1292
|
+
.sb-list[data-sb-list-style="plain"] > .sb-list-content > :not(.sb-section) {
|
|
1293
|
+
background: var(--sb-color-system-background);
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
.sb-navscreen:has(> .sb-navscreen-body > .sb-navscreen-content > .sb-list[data-sb-list-style="plain"]) {
|
|
1297
|
+
background: var(--sb-color-system-background);
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
/* ---------------------------------------------------------------------------
|
|
1301
|
+
Phase 4: tab views. The tabs are stacked in the content slot (every tab has
|
|
1302
|
+
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 (49 + bottom safe area):
|
|
1304
|
+
translucent system background, hairline on top, items in the 49px row.
|
|
1305
|
+
------------------------------------------------------------------------- */
|
|
1306
|
+
|
|
1307
|
+
.sb-tabview {
|
|
1308
|
+
overflow: hidden;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
.sb-tabview-content {
|
|
1312
|
+
position: static;
|
|
1313
|
+
display: block;
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
.sb-tab {
|
|
1317
|
+
display: block;
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
.sb-tab[data-sb-tab-hidden] {
|
|
1321
|
+
visibility: hidden;
|
|
1322
|
+
pointer-events: none;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
.sb-tabbar {
|
|
1326
|
+
position: absolute;
|
|
1327
|
+
z-index: 3;
|
|
1328
|
+
background: var(--sb-color-tabbar-background);
|
|
1329
|
+
backdrop-filter: blur(20px) saturate(180%);
|
|
1330
|
+
-webkit-backdrop-filter: blur(20px) saturate(180%);
|
|
1331
|
+
}
|
|
1332
|
+
|
|
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
|
+
.sb-tabbar-item {
|
|
1345
|
+
appearance: none;
|
|
1346
|
+
position: absolute;
|
|
1347
|
+
display: flex;
|
|
1348
|
+
flex-direction: column;
|
|
1349
|
+
align-items: center;
|
|
1350
|
+
justify-content: flex-start;
|
|
1351
|
+
gap: 2px;
|
|
1352
|
+
margin: 0;
|
|
1353
|
+
padding: 6px 0 0;
|
|
1354
|
+
border: 0;
|
|
1355
|
+
background: none;
|
|
1356
|
+
color: var(--sb-color-secondary);
|
|
1357
|
+
font-family: var(--sb-font-family);
|
|
1358
|
+
cursor: pointer;
|
|
1359
|
+
touch-action: manipulation;
|
|
1360
|
+
-webkit-tap-highlight-color: transparent;
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
.sb-tabbar-item[aria-selected="true"] {
|
|
1364
|
+
color: var(--sb-color-accent);
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
.sb-tabbar-item:focus-visible {
|
|
1368
|
+
outline: 2px solid var(--sb-color-accent);
|
|
1369
|
+
outline-offset: -2px;
|
|
1370
|
+
border-radius: 8px;
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
.sb-tabbar-icon {
|
|
1374
|
+
display: flex;
|
|
1375
|
+
align-items: center;
|
|
1376
|
+
justify-content: center;
|
|
1377
|
+
width: 24px;
|
|
1378
|
+
height: 24px;
|
|
1379
|
+
font-size: 24px;
|
|
1380
|
+
line-height: 1;
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
.sb-tabbar-icon[hidden] {
|
|
1384
|
+
display: none;
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
.sb-tabbar-icon > svg {
|
|
1388
|
+
display: block;
|
|
1389
|
+
width: 1em;
|
|
1390
|
+
height: 1em;
|
|
1391
|
+
stroke: currentColor;
|
|
1392
|
+
stroke-width: 1.75;
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
.sb-tabbar-icon[data-sb-missing] {
|
|
1396
|
+
border: 1px dashed currentColor;
|
|
1397
|
+
border-radius: 4px;
|
|
1398
|
+
opacity: 0.6;
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
.sb-tabbar-title {
|
|
1402
|
+
font-size: 10px;
|
|
1403
|
+
line-height: 12px;
|
|
1404
|
+
font-weight: 500;
|
|
1405
|
+
letter-spacing: -0.01em;
|
|
1406
|
+
white-space: nowrap;
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
/* ---------------------------------------------------------------------------
|
|
1410
|
+
Phase 4: pickers. Segmented: a gray track with equal segments at the
|
|
1411
|
+
engine's `segment<i>` rects; the selected one carries a raised pill inset
|
|
1412
|
+
2px. Menu: label leading, value + chevron trailing (engine `label`/`value`
|
|
1413
|
+
rects); the control opens a menu appended to #screen.
|
|
1414
|
+
------------------------------------------------------------------------- */
|
|
1415
|
+
|
|
1416
|
+
.sb-picker[data-sb-style="segmented"] {
|
|
1417
|
+
display: block;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
/* The track is placed at the control's frame (in a list row the element's
|
|
1421
|
+
box is the whole row, so the track cannot be the element's background). */
|
|
1422
|
+
.sb-segment-track {
|
|
1423
|
+
position: absolute;
|
|
1424
|
+
border-radius: 8px;
|
|
1425
|
+
background: var(--sb-color-segment-track);
|
|
1426
|
+
pointer-events: none;
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
.sb-segment {
|
|
1430
|
+
appearance: none;
|
|
1431
|
+
position: absolute;
|
|
1432
|
+
display: flex;
|
|
1433
|
+
align-items: center;
|
|
1434
|
+
justify-content: center;
|
|
1435
|
+
margin: 0;
|
|
1436
|
+
padding: 0;
|
|
1437
|
+
border: 0;
|
|
1438
|
+
background: none;
|
|
1439
|
+
color: var(--sb-color-primary);
|
|
1440
|
+
font: 600 13px/16px var(--sb-font-family);
|
|
1441
|
+
letter-spacing: -0.01em;
|
|
1442
|
+
white-space: nowrap;
|
|
1443
|
+
cursor: pointer;
|
|
1444
|
+
touch-action: manipulation;
|
|
1445
|
+
-webkit-tap-highlight-color: transparent;
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
.sb-segment::before {
|
|
1449
|
+
content: "";
|
|
1450
|
+
position: absolute;
|
|
1451
|
+
inset: 2px;
|
|
1452
|
+
border-radius: 7px;
|
|
1453
|
+
transition: background-color 0.15s ease, box-shadow 0.15s ease;
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
.sb-segment[aria-checked="true"]::before {
|
|
1457
|
+
background: var(--sb-color-segment-selected);
|
|
1458
|
+
box-shadow:
|
|
1459
|
+
0 3px 8px rgba(0, 0, 0, 0.12),
|
|
1460
|
+
0 3px 1px rgba(0, 0, 0, 0.04);
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
.sb-segment-title {
|
|
1464
|
+
position: relative;
|
|
1465
|
+
overflow: hidden;
|
|
1466
|
+
text-overflow: ellipsis;
|
|
1467
|
+
max-width: calc(100% - 8px);
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
.sb-segment:focus-visible {
|
|
1471
|
+
outline: 2px solid var(--sb-color-accent);
|
|
1472
|
+
outline-offset: -2px;
|
|
1473
|
+
border-radius: 7px;
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
.sb-picker[data-sb-style="menu"] {
|
|
1477
|
+
display: block;
|
|
1478
|
+
color: var(--sb-color-primary);
|
|
1479
|
+
cursor: pointer;
|
|
1480
|
+
touch-action: manipulation;
|
|
1481
|
+
-webkit-tap-highlight-color: transparent;
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
.sb-picker[data-sb-style="menu"]:focus-visible {
|
|
1485
|
+
outline: 2px solid var(--sb-color-accent);
|
|
1486
|
+
outline-offset: 2px;
|
|
1487
|
+
border-radius: 6px;
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
.sb-section-rows > .sb-picker[data-sb-style="menu"]:active,
|
|
1491
|
+
.sb-list-content > .sb-picker[data-sb-style="menu"]:active {
|
|
1492
|
+
background: var(--sb-color-fill);
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
.sb-picker-label,
|
|
1496
|
+
.sb-picker-value {
|
|
1497
|
+
position: absolute;
|
|
1498
|
+
display: flex;
|
|
1499
|
+
align-items: center;
|
|
1500
|
+
white-space: nowrap;
|
|
1501
|
+
overflow: hidden;
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
.sb-picker-label {
|
|
1505
|
+
text-overflow: ellipsis;
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
.sb-picker-value {
|
|
1509
|
+
justify-content: flex-end;
|
|
1510
|
+
gap: 4px;
|
|
1511
|
+
color: var(--sb-color-secondary);
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
.sb-picker-value-text {
|
|
1515
|
+
overflow: hidden;
|
|
1516
|
+
text-overflow: ellipsis;
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
.sb-picker-value > svg {
|
|
1520
|
+
display: block;
|
|
1521
|
+
width: 16px;
|
|
1522
|
+
height: 16px;
|
|
1523
|
+
flex-shrink: 0;
|
|
1524
|
+
stroke: currentColor;
|
|
1525
|
+
stroke-width: 2.5;
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
/* The option menu: anchored under (or above) the control by the renderer. */
|
|
1529
|
+
.sb-picker-menu {
|
|
1530
|
+
position: absolute;
|
|
1531
|
+
z-index: 6;
|
|
1532
|
+
border-radius: 13px;
|
|
1533
|
+
background: var(--sb-color-menu-background);
|
|
1534
|
+
backdrop-filter: blur(30px) saturate(180%);
|
|
1535
|
+
-webkit-backdrop-filter: blur(30px) saturate(180%);
|
|
1536
|
+
box-shadow:
|
|
1537
|
+
0 0 0 0.5px rgba(0, 0, 0, 0.08),
|
|
1538
|
+
0 10px 40px rgba(0, 0, 0, 0.22);
|
|
1539
|
+
overflow: hidden;
|
|
1540
|
+
transform-origin: 50% 0;
|
|
1541
|
+
animation: sb-menu-in 0.18s cubic-bezier(0.2, 0.8, 0.2, 1) both;
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
.sb-picker-menu[data-sb-placement="above"] {
|
|
1545
|
+
transform-origin: 50% 100%;
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
.sb-picker-menu-item {
|
|
1549
|
+
appearance: none;
|
|
1550
|
+
display: flex;
|
|
1551
|
+
align-items: center;
|
|
1552
|
+
gap: 8px;
|
|
1553
|
+
width: 100%;
|
|
1554
|
+
height: 44px;
|
|
1555
|
+
margin: 0;
|
|
1556
|
+
padding: 0 16px;
|
|
1557
|
+
border: 0;
|
|
1558
|
+
background: none;
|
|
1559
|
+
color: var(--sb-color-primary);
|
|
1560
|
+
font: 400 var(--sb-font-size-body) / 1.3 var(--sb-font-family);
|
|
1561
|
+
text-align: left;
|
|
1562
|
+
cursor: pointer;
|
|
1563
|
+
touch-action: manipulation;
|
|
1564
|
+
-webkit-tap-highlight-color: transparent;
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
:is(.sb-picker-menu-item, .sb-picker-menu-static) + :is(.sb-picker-menu-item, .sb-picker-menu-static) {
|
|
1568
|
+
box-shadow: inset 0 1px 0 var(--sb-color-separator);
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
.sb-picker-menu-item:active {
|
|
1572
|
+
background: var(--sb-color-fill);
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
.sb-picker-menu-check {
|
|
1576
|
+
display: inline-flex;
|
|
1577
|
+
align-items: center;
|
|
1578
|
+
justify-content: center;
|
|
1579
|
+
width: 20px;
|
|
1580
|
+
flex-shrink: 0;
|
|
1581
|
+
visibility: hidden;
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
.sb-picker-menu-item[aria-checked="true"] > .sb-picker-menu-check {
|
|
1585
|
+
visibility: visible;
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
.sb-picker-menu-check > svg {
|
|
1589
|
+
width: 1em;
|
|
1590
|
+
height: 1em;
|
|
1591
|
+
stroke: currentColor;
|
|
1592
|
+
stroke-width: 2.5;
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
.sb-picker-menu-title {
|
|
1596
|
+
flex: 1 1 auto;
|
|
1597
|
+
overflow: hidden;
|
|
1598
|
+
text-overflow: ellipsis;
|
|
1599
|
+
white-space: nowrap;
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
@keyframes sb-menu-in {
|
|
1603
|
+
from {
|
|
1604
|
+
opacity: 0;
|
|
1605
|
+
transform: scale(0.9);
|
|
1606
|
+
}
|
|
1607
|
+
to {
|
|
1608
|
+
opacity: 1;
|
|
1609
|
+
transform: scale(1);
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
/* GeometryReader: a plain box the engine sizes to its proposal. */
|
|
1614
|
+
.sb-geometry {
|
|
1615
|
+
display: block;
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
/* ---------------------------------------------------------------------------
|
|
1619
|
+
Phase 6: layout containers (docs/phase6/layout-containers.md). Grids, the
|
|
1620
|
+
custom `layout` element and `decorated` are plain boxes whose children the
|
|
1621
|
+
engine positions; the gauge draws its bar as chrome.
|
|
1622
|
+
------------------------------------------------------------------------- */
|
|
1623
|
+
|
|
1624
|
+
.sb-layout,
|
|
1625
|
+
.sb-grid,
|
|
1626
|
+
.sb-gridrow,
|
|
1627
|
+
.sb-lazyvgrid,
|
|
1628
|
+
.sb-decorated {
|
|
1629
|
+
display: block;
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
/* `.background { }`: the decoration (the last child) draws under the content.
|
|
1633
|
+
The box is its own stacking context so the content's z-index stays local. */
|
|
1634
|
+
.sb-decorated {
|
|
1635
|
+
isolation: isolate;
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
.sb-decorated[data-sb-role="background"] > [data-sb-id]:first-child {
|
|
1639
|
+
z-index: 1;
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
.sb-gauge {
|
|
1643
|
+
display: block;
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
.sb-gauge-track,
|
|
1647
|
+
.sb-gauge-fill {
|
|
1648
|
+
position: absolute;
|
|
1649
|
+
border-radius: 2px;
|
|
1650
|
+
pointer-events: none;
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
.sb-gauge-track {
|
|
1654
|
+
background: var(--sb-color-fill);
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
.sb-gauge-fill {
|
|
1658
|
+
background: var(--sb-tint, var(--sb-color-accent));
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1662
|
+
.sb-picker-menu,
|
|
1663
|
+
.sb-segment::before {
|
|
1664
|
+
animation-duration: 0.01s;
|
|
1665
|
+
transition-duration: 0.01s;
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
/* ---------------------------------------------------------------------------
|
|
1670
|
+
Phase 6 (docs/phase6/chrome.md): menus, inline pickers, navigation bar
|
|
1671
|
+
toolbars, search fields, tap gestures and list row badges.
|
|
1672
|
+
------------------------------------------------------------------------- */
|
|
1673
|
+
|
|
1674
|
+
/* Menu: the label (first child) is the tappable control, the item children
|
|
1675
|
+
are listed in the popover and never shown inline. */
|
|
1676
|
+
.sb-menu {
|
|
1677
|
+
display: block;
|
|
1678
|
+
color: var(--sb-color-accent);
|
|
1679
|
+
cursor: pointer;
|
|
1680
|
+
touch-action: manipulation;
|
|
1681
|
+
-webkit-tap-highlight-color: transparent;
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
.sb-menu > [data-sb-id]:not(:first-child),
|
|
1685
|
+
.sb-menu > [data-sb-id][hidden] {
|
|
1686
|
+
display: none;
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
.sb-menu:active {
|
|
1690
|
+
opacity: 0.3;
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
.sb-menu:focus-visible {
|
|
1694
|
+
outline: 2px solid var(--sb-color-accent);
|
|
1695
|
+
outline-offset: 4px;
|
|
1696
|
+
border-radius: 6px;
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
/* Popover rows for menu items: a tap row shows its symbol trailing; a
|
|
1700
|
+
destructive role is red; a disabled item dims; section headers are 32px
|
|
1701
|
+
captions; `text` items are static 44px rows; groups are split by 8px bands. */
|
|
1702
|
+
.sb-picker-menu-symbol {
|
|
1703
|
+
display: inline-flex;
|
|
1704
|
+
align-items: center;
|
|
1705
|
+
justify-content: center;
|
|
1706
|
+
width: 22px;
|
|
1707
|
+
margin-left: auto;
|
|
1708
|
+
flex-shrink: 0;
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
.sb-picker-menu-symbol > svg {
|
|
1712
|
+
width: 1.15em;
|
|
1713
|
+
height: 1.15em;
|
|
1714
|
+
stroke: currentColor;
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
.sb-picker-menu-item[data-sb-role="destructive"] {
|
|
1718
|
+
color: var(--sb-color-red);
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
.sb-picker-menu-item:disabled {
|
|
1722
|
+
color: var(--sb-color-tertiary-label);
|
|
1723
|
+
cursor: default;
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
.sb-picker-menu-caption {
|
|
1727
|
+
display: flex;
|
|
1728
|
+
align-items: center;
|
|
1729
|
+
height: 32px;
|
|
1730
|
+
padding: 0 16px;
|
|
1731
|
+
color: var(--sb-color-secondary);
|
|
1732
|
+
font: 400 13px/16px var(--sb-font-family);
|
|
1733
|
+
white-space: nowrap;
|
|
1734
|
+
overflow: hidden;
|
|
1735
|
+
text-overflow: ellipsis;
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
.sb-picker-menu-static {
|
|
1739
|
+
display: flex;
|
|
1740
|
+
align-items: center;
|
|
1741
|
+
height: 44px;
|
|
1742
|
+
padding: 0 16px;
|
|
1743
|
+
color: var(--sb-color-secondary);
|
|
1744
|
+
font: 400 var(--sb-font-size-body) / 1.3 var(--sb-font-family);
|
|
1745
|
+
white-space: nowrap;
|
|
1746
|
+
overflow: hidden;
|
|
1747
|
+
text-overflow: ellipsis;
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
.sb-picker-menu-separator {
|
|
1751
|
+
height: 8px;
|
|
1752
|
+
background: var(--sb-color-fill);
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
/* Inline picker: a dim label row (when the label is non-empty) and 44px
|
|
1756
|
+
option rows at the engine's `label` / `option<i>` rects; inside a list the
|
|
1757
|
+
rows span the list row, with the usual 16px content inset and hairlines. */
|
|
1758
|
+
.sb-picker[data-sb-style="inline"] {
|
|
1759
|
+
display: block;
|
|
1760
|
+
color: var(--sb-color-primary);
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
.sb-picker-inline-label {
|
|
1764
|
+
position: absolute;
|
|
1765
|
+
display: flex;
|
|
1766
|
+
align-items: center;
|
|
1767
|
+
color: var(--sb-color-secondary);
|
|
1768
|
+
white-space: nowrap;
|
|
1769
|
+
overflow: hidden;
|
|
1770
|
+
text-overflow: ellipsis;
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
.sb-picker-option {
|
|
1774
|
+
appearance: none;
|
|
1775
|
+
position: absolute;
|
|
1776
|
+
display: flex;
|
|
1777
|
+
align-items: center;
|
|
1778
|
+
gap: 8px;
|
|
1779
|
+
margin: 0;
|
|
1780
|
+
padding: 0;
|
|
1781
|
+
border: 0;
|
|
1782
|
+
background: none;
|
|
1783
|
+
color: var(--sb-color-primary);
|
|
1784
|
+
font: inherit;
|
|
1785
|
+
text-align: left;
|
|
1786
|
+
cursor: pointer;
|
|
1787
|
+
touch-action: manipulation;
|
|
1788
|
+
-webkit-tap-highlight-color: transparent;
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
.sb-picker-option:active {
|
|
1792
|
+
background: var(--sb-color-fill);
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
.sb-picker-option:focus-visible {
|
|
1796
|
+
outline: 2px solid var(--sb-color-accent);
|
|
1797
|
+
outline-offset: -2px;
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
.sb-picker-option-title {
|
|
1801
|
+
flex: 1 1 auto;
|
|
1802
|
+
overflow: hidden;
|
|
1803
|
+
text-overflow: ellipsis;
|
|
1804
|
+
white-space: nowrap;
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
.sb-picker-option-check {
|
|
1808
|
+
display: inline-flex;
|
|
1809
|
+
align-items: center;
|
|
1810
|
+
justify-content: center;
|
|
1811
|
+
width: 20px;
|
|
1812
|
+
flex-shrink: 0;
|
|
1813
|
+
color: var(--sb-color-accent);
|
|
1814
|
+
visibility: hidden;
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
.sb-picker-option[aria-checked="true"] > .sb-picker-option-check {
|
|
1818
|
+
visibility: visible;
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
.sb-picker-option-check > svg {
|
|
1822
|
+
width: 1.1em;
|
|
1823
|
+
height: 1.1em;
|
|
1824
|
+
stroke: currentColor;
|
|
1825
|
+
stroke-width: 2.5;
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
/* Hairlines between the inline rows (the first row has the row above it). */
|
|
1829
|
+
.sb-picker-inline-label + .sb-picker-option::before,
|
|
1830
|
+
.sb-picker-option + .sb-picker-option::before {
|
|
1831
|
+
content: "";
|
|
1832
|
+
position: absolute;
|
|
1833
|
+
left: 0;
|
|
1834
|
+
right: 0;
|
|
1835
|
+
top: 0;
|
|
1836
|
+
height: 1px;
|
|
1837
|
+
background: var(--sb-color-separator);
|
|
1838
|
+
pointer-events: none;
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
.sb-section-rows > .sb-picker[data-sb-style="inline"] > .sb-picker-inline-label,
|
|
1842
|
+
.sb-list-content > .sb-picker[data-sb-style="inline"] > .sb-picker-inline-label,
|
|
1843
|
+
.sb-section-rows > .sb-picker[data-sb-style="inline"] > .sb-picker-option,
|
|
1844
|
+
.sb-list-content > .sb-picker[data-sb-style="inline"] > .sb-picker-option {
|
|
1845
|
+
padding: 0 16px;
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
.sb-section-rows > .sb-picker[data-sb-style="inline"] > .sb-picker-option::before,
|
|
1849
|
+
.sb-list-content > .sb-picker[data-sb-style="inline"] > .sb-picker-option::before {
|
|
1850
|
+
left: 16px;
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
/* Toolbars: the element's frame is its item group in the bar (items are
|
|
1854
|
+
ordinary children with frames). A bottom toolbar is the whole 49px + safe
|
|
1855
|
+
area bar, translucent with a hairline like the tab bar. */
|
|
1856
|
+
.sb-toolbar {
|
|
1857
|
+
display: block;
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
.sb-toolbar[data-sb-placement="bottom"] {
|
|
1861
|
+
z-index: 3;
|
|
1862
|
+
background: var(--sb-color-tabbar-background);
|
|
1863
|
+
backdrop-filter: blur(20px) saturate(180%);
|
|
1864
|
+
-webkit-backdrop-filter: blur(20px) saturate(180%);
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
.sb-toolbar[data-sb-placement="bottom"]::before {
|
|
1868
|
+
content: "";
|
|
1869
|
+
position: absolute;
|
|
1870
|
+
left: 0;
|
|
1871
|
+
right: 0;
|
|
1872
|
+
top: 0;
|
|
1873
|
+
height: 1px;
|
|
1874
|
+
background: var(--sb-color-separator);
|
|
1875
|
+
pointer-events: none;
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
/* Search field: a 36px rounded gray field (engine `searchField` rect) with a
|
|
1879
|
+
magnifier leading; the input sends `text` events like a textfield. */
|
|
1880
|
+
.sb-searchfield {
|
|
1881
|
+
display: flex;
|
|
1882
|
+
align-items: center;
|
|
1883
|
+
gap: 6px;
|
|
1884
|
+
padding: 0 8px;
|
|
1885
|
+
border-radius: 10px;
|
|
1886
|
+
background: var(--sb-color-fill);
|
|
1887
|
+
color: var(--sb-color-primary);
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
.sb-searchfield > svg {
|
|
1891
|
+
display: block;
|
|
1892
|
+
width: 17px;
|
|
1893
|
+
height: 17px;
|
|
1894
|
+
flex-shrink: 0;
|
|
1895
|
+
color: var(--sb-color-secondary);
|
|
1896
|
+
stroke: currentColor;
|
|
1897
|
+
stroke-width: 2.25;
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
.sb-searchfield-input {
|
|
1901
|
+
flex: 1 1 auto;
|
|
1902
|
+
min-width: 0;
|
|
1903
|
+
appearance: none;
|
|
1904
|
+
margin: 0;
|
|
1905
|
+
padding: 0;
|
|
1906
|
+
border: 0;
|
|
1907
|
+
background: transparent;
|
|
1908
|
+
outline: none;
|
|
1909
|
+
color: var(--sb-color-primary);
|
|
1910
|
+
font: inherit;
|
|
1911
|
+
font-size: var(--sb-font-size-body);
|
|
1912
|
+
line-height: 1.3;
|
|
1913
|
+
}
|
|
1914
|
+
|
|
1915
|
+
.sb-searchfield-input::placeholder {
|
|
1916
|
+
color: var(--sb-color-secondary);
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
/* Tap gesture: a plain box the size of its child; the renderer sends `tap`
|
|
1920
|
+
for clicks no inner control claimed. */
|
|
1921
|
+
.sb-tapgesture {
|
|
1922
|
+
display: block;
|
|
1923
|
+
cursor: pointer;
|
|
1924
|
+
touch-action: manipulation;
|
|
1925
|
+
-webkit-tap-highlight-color: transparent;
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
/* List row badge: trailing secondary text at the engine's `badge` rect. */
|
|
1929
|
+
.sb-badge {
|
|
1930
|
+
position: absolute;
|
|
1931
|
+
display: flex;
|
|
1932
|
+
align-items: center;
|
|
1933
|
+
justify-content: flex-end;
|
|
1934
|
+
color: var(--sb-color-secondary);
|
|
1935
|
+
white-space: pre;
|
|
1936
|
+
pointer-events: none;
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
/* ---------------------------------------------------------------------------
|
|
1940
|
+
Phase 7: split navigation, selected rows, ViewThatFits, hidden and masks
|
|
1941
|
+
(docs/ops-protocol.md, "Phase 7").
|
|
1942
|
+
------------------------------------------------------------------------- */
|
|
1943
|
+
|
|
1944
|
+
/* NavigationSplitView: two navstack columns (protocol children, positioned by
|
|
1945
|
+
the engine) with a hairline between them at the engine's `separator` rect. */
|
|
1946
|
+
.sb-navsplit {
|
|
1947
|
+
overflow: hidden;
|
|
1948
|
+
background: var(--sb-color-system-background);
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
.sb-navsplit-separator {
|
|
1952
|
+
position: absolute;
|
|
1953
|
+
background: var(--sb-color-separator);
|
|
1954
|
+
pointer-events: none;
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
/* A selected `navlink` row (`List(selection:)`): the sidebar-style rounded
|
|
1958
|
+
fill. The engine omits the chevron inside a navsplit sidebar; elsewhere
|
|
1959
|
+
the chevron stays. The list-row selectors outrank the per-style row
|
|
1960
|
+
background and `border-radius: 0` rules above. */
|
|
1961
|
+
.sb-navlink[data-sb-selected],
|
|
1962
|
+
.sb-list[data-sb-list-style] > .sb-list-content > .sb-navlink[data-sb-selected],
|
|
1963
|
+
.sb-list[data-sb-list-style] .sb-section-rows > .sb-navlink[data-sb-selected] {
|
|
1964
|
+
background: var(--sb-color-tertiary-fill);
|
|
1965
|
+
border-radius: 10px;
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
/* ViewThatFits: the candidates it did not choose have no frame and carry the
|
|
1969
|
+
`hidden` attribute (src/layoutDom.ts). */
|
|
1970
|
+
.sb-viewthatfits > [data-sb-id][hidden] {
|
|
1971
|
+
display: none;
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
/* `.hidden()`: the styled box keeps its layout but is invisible and inert
|
|
1975
|
+
(inline `visibility: hidden; pointer-events: none`, see applyStyle). */
|
|
1976
|
+
.sb-styled[data-sb-hidden] {
|
|
1977
|
+
visibility: hidden;
|
|
1978
|
+
pointer-events: none;
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
/* ---------------------------------------------------------------------------
|
|
1982
|
+
Phase 7: charts (docs/ops-protocol.md, "Phase 7: charts"). The engine
|
|
1983
|
+
frames the chart and its `chartitem` children; src/chartDom.ts draws the
|
|
1984
|
+
plot SVG, ticks, text labels and legend as chrome inside .sb-chart-chrome,
|
|
1985
|
+
which stays static so its absolutely positioned pieces resolve against
|
|
1986
|
+
the chart box. The slot comes after the chrome, so annotations draw above
|
|
1987
|
+
the marks.
|
|
1988
|
+
------------------------------------------------------------------------- */
|
|
1989
|
+
|
|
1990
|
+
.sb-chart,
|
|
1991
|
+
.sb-chartitem {
|
|
1992
|
+
display: block;
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
.sb-chart-chrome {
|
|
1996
|
+
position: static;
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
.sb-chart-plot {
|
|
2000
|
+
position: absolute;
|
|
2001
|
+
display: block;
|
|
2002
|
+
overflow: hidden;
|
|
2003
|
+
pointer-events: none;
|
|
2004
|
+
}
|
|
2005
|
+
|
|
2006
|
+
.sb-chart-gridline {
|
|
2007
|
+
stroke: var(--sb-color-separator);
|
|
2008
|
+
stroke-width: 1;
|
|
2009
|
+
shape-rendering: crispEdges;
|
|
2010
|
+
}
|
|
2011
|
+
|
|
2012
|
+
.sb-chart-tick {
|
|
2013
|
+
position: absolute;
|
|
2014
|
+
background: var(--sb-color-separator);
|
|
2015
|
+
pointer-events: none;
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
.sb-chart-label {
|
|
2019
|
+
position: absolute;
|
|
2020
|
+
color: var(--sb-color-secondary);
|
|
2021
|
+
white-space: pre;
|
|
2022
|
+
pointer-events: none;
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
.sb-chart-legend-item {
|
|
2026
|
+
position: absolute;
|
|
2027
|
+
display: flex;
|
|
2028
|
+
align-items: center;
|
|
2029
|
+
gap: 4px;
|
|
2030
|
+
color: var(--sb-color-secondary);
|
|
2031
|
+
white-space: pre;
|
|
2032
|
+
pointer-events: none;
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
.sb-chart-legend-glyph {
|
|
2036
|
+
display: block;
|
|
2037
|
+
flex: none;
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
.sb-chart-legend-label {
|
|
2041
|
+
flex: none;
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
/* 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). */
|
|
2046
|
+
.device,
|
|
2047
|
+
.device[data-theme="light"] {
|
|
2048
|
+
--sb-color-blue: #007aff;
|
|
2049
|
+
--sb-color-orange: #ff9500;
|
|
2050
|
+
--sb-color-yellow: #ffcc00;
|
|
2051
|
+
--sb-color-purple: #af52de;
|
|
2052
|
+
--sb-color-pink: #ff2d55;
|
|
2053
|
+
--sb-color-teal: #30b0c7;
|
|
2054
|
+
--sb-color-indigo: #5856d6;
|
|
2055
|
+
--sb-color-mint: #00c7be;
|
|
2056
|
+
--sb-color-cyan: #32ade6;
|
|
2057
|
+
--sb-color-brown: #a2845e;
|
|
2058
|
+
--sb-color-gray: #8e8e93;
|
|
2059
|
+
}
|
|
2060
|
+
|
|
2061
|
+
.device[data-theme="dark"] {
|
|
2062
|
+
--sb-color-blue: #0a84ff;
|
|
2063
|
+
--sb-color-orange: #ff9f0a;
|
|
2064
|
+
--sb-color-yellow: #ffd60a;
|
|
2065
|
+
--sb-color-purple: #bf5af2;
|
|
2066
|
+
--sb-color-pink: #ff375f;
|
|
2067
|
+
--sb-color-teal: #40c8e0;
|
|
2068
|
+
--sb-color-indigo: #5e5ce6;
|
|
2069
|
+
--sb-color-mint: #63e6e2;
|
|
2070
|
+
--sb-color-cyan: #64d2ff;
|
|
2071
|
+
--sb-color-brown: #ac8e68;
|
|
2072
|
+
--sb-color-gray: #8e8e93;
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
/* ---------------------------------------------------------------------------
|
|
2076
|
+
Phase 8: gestures, rows, navigation (docs/ops-protocol.md, "Phase 8:
|
|
2077
|
+
interaction"; src/interaction.ts drives the data attributes).
|
|
2078
|
+
------------------------------------------------------------------------- */
|
|
2079
|
+
|
|
2080
|
+
/* `gesture`: a drag gesture owns touches in both axes; long-press subtrees
|
|
2081
|
+
never show the browser's text selection or callout. */
|
|
2082
|
+
.sb-gesture,
|
|
2083
|
+
.sb-contextmenu {
|
|
2084
|
+
user-select: none;
|
|
2085
|
+
-webkit-user-select: none;
|
|
2086
|
+
-webkit-touch-callout: none;
|
|
2087
|
+
-webkit-tap-highlight-color: transparent;
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
.sb-gesture[data-sb-drag] {
|
|
2091
|
+
touch-action: none;
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
/* `contextmenu` / `swipeactions`: only the content (the first child) shows
|
|
2095
|
+
inline; the items are listed in the popover / drawn as action buttons
|
|
2096
|
+
(they are in `hidden` without frames). */
|
|
2097
|
+
.sb-contextmenu > [data-sb-id]:not(:first-child),
|
|
2098
|
+
.sb-contextmenu > [data-sb-id][hidden],
|
|
2099
|
+
.sb-swipeactions > .sb-swipe-content > [data-sb-id]:not(:first-child),
|
|
2100
|
+
.sb-swipeactions > .sb-swipe-content > [data-sb-id][hidden] {
|
|
2101
|
+
display: none;
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
/* `.allowsHitTesting(false)` (set inline by the renderer as well). */
|
|
2105
|
+
[data-sb-hit-testing="false"] {
|
|
2106
|
+
pointer-events: none;
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
/* Hit testing like SwiftUI's: a stack, spacer or plain modifier box is not
|
|
2110
|
+
hit in its empty space (what is under it is), only content is: text, images,
|
|
2111
|
+
shapes, controls, rows. A `styled` with a `background` paint is content too.
|
|
2112
|
+
Containers therefore drop pointer events and their children opt back in;
|
|
2113
|
+
subtrees that must stay inert (hidden, disabled, hit testing off, exit and
|
|
2114
|
+
pop animations) are blocked explicitly, since the opt-in breaks inheritance. */
|
|
2115
|
+
.sb-vstack,
|
|
2116
|
+
.sb-hstack,
|
|
2117
|
+
.sb-zstack,
|
|
2118
|
+
.sb-spacer,
|
|
2119
|
+
.sb-styled,
|
|
2120
|
+
.sb-geometry,
|
|
2121
|
+
.sb-layout,
|
|
2122
|
+
.sb-grid,
|
|
2123
|
+
.sb-gridrow,
|
|
2124
|
+
.sb-lazyvgrid,
|
|
2125
|
+
.sb-viewthatfits,
|
|
2126
|
+
.sb-decorated,
|
|
2127
|
+
.sb-gesture,
|
|
2128
|
+
.sb-tapgesture {
|
|
2129
|
+
pointer-events: none;
|
|
2130
|
+
}
|
|
2131
|
+
|
|
2132
|
+
:is(.sb-vstack, .sb-hstack, .sb-zstack, .sb-styled, .sb-geometry, .sb-layout, .sb-grid, .sb-gridrow, .sb-lazyvgrid, .sb-viewthatfits, .sb-decorated, .sb-gesture, .sb-tapgesture)
|
|
2133
|
+
> [data-sb-kind]:not(.sb-vstack, .sb-hstack, .sb-zstack, .sb-spacer, .sb-styled, .sb-geometry, .sb-layout, .sb-grid, .sb-gridrow, .sb-lazyvgrid, .sb-viewthatfits, .sb-decorated, .sb-gesture, .sb-tapgesture) {
|
|
2134
|
+
pointer-events: auto;
|
|
2135
|
+
}
|
|
2136
|
+
|
|
2137
|
+
.sb-styled[data-sb-background] {
|
|
2138
|
+
pointer-events: auto;
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
:is([data-sb-hidden], [data-sb-disabled], [data-sb-hit-testing="false"], .sb-navscreen--pop, .sb-exit-clone, .sb-sheet--out, .sb-alert--out, .sb-tab[data-sb-tab-hidden]) [data-sb-kind],
|
|
2142
|
+
:is([data-sb-hidden], [data-sb-disabled], [data-sb-hit-testing="false"], .sb-navscreen--pop, .sb-exit-clone, .sb-sheet--out, .sb-alert--out, .sb-tab[data-sb-tab-hidden]) .sb-navbar > * {
|
|
2143
|
+
pointer-events: none !important;
|
|
2144
|
+
}
|
|
2145
|
+
|
|
2146
|
+
/* While a pointer session runs (src/interaction.ts) nothing on the screen is
|
|
2147
|
+
selectable: a mouse drag is a gesture, not a text selection. */
|
|
2148
|
+
[data-sb-pointer-session],
|
|
2149
|
+
[data-sb-pointer-session] * {
|
|
2150
|
+
user-select: none;
|
|
2151
|
+
-webkit-user-select: none;
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2154
|
+
/* Swipe rows (`swipeactions`, deletable `listrow`): the content slot paints
|
|
2155
|
+
the row background and slides over the action buttons behind it. */
|
|
2156
|
+
.sb-swipeactions,
|
|
2157
|
+
.sb-listrow {
|
|
2158
|
+
overflow: hidden;
|
|
2159
|
+
touch-action: pan-y;
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
.sb-swipeactions > .sb-swipe-content,
|
|
2163
|
+
.sb-listrow > .sb-swipe-content {
|
|
2164
|
+
position: absolute;
|
|
2165
|
+
inset: 0;
|
|
2166
|
+
z-index: 1;
|
|
2167
|
+
background-color: inherit;
|
|
2168
|
+
border-radius: inherit;
|
|
2169
|
+
transition: transform 0.3s var(--sb-nav-easing);
|
|
2170
|
+
will-change: transform;
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
.sb-swipeactions[data-sb-swiping] > .sb-swipe-content,
|
|
2174
|
+
.sb-listrow[data-sb-swiping] > .sb-swipe-content {
|
|
2175
|
+
transition: none;
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
/* The hairline separator stays above the sliding content. */
|
|
2179
|
+
.sb-swipeactions::after,
|
|
2180
|
+
.sb-listrow::after {
|
|
2181
|
+
z-index: 2;
|
|
2182
|
+
}
|
|
2183
|
+
|
|
2184
|
+
.sb-swipe-actions {
|
|
2185
|
+
position: absolute;
|
|
2186
|
+
top: 0;
|
|
2187
|
+
bottom: 0;
|
|
2188
|
+
z-index: 0;
|
|
2189
|
+
display: flex;
|
|
2190
|
+
overflow: hidden;
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
.sb-swipe-actions[data-sb-edge="trailing"] {
|
|
2194
|
+
right: 0;
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
/* Leading: the first declared action is nearest the content, so the row is reversed. */
|
|
2198
|
+
.sb-swipe-actions[data-sb-edge="leading"] {
|
|
2199
|
+
left: 0;
|
|
2200
|
+
flex-direction: row-reverse;
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2203
|
+
.sb-swipe-action {
|
|
2204
|
+
appearance: none;
|
|
2205
|
+
flex: 1 1 74px;
|
|
2206
|
+
min-width: 0;
|
|
2207
|
+
display: flex;
|
|
2208
|
+
flex-direction: column;
|
|
2209
|
+
align-items: center;
|
|
2210
|
+
justify-content: center;
|
|
2211
|
+
gap: 3px;
|
|
2212
|
+
margin: 0;
|
|
2213
|
+
padding: 0 4px;
|
|
2214
|
+
border: 0;
|
|
2215
|
+
background: var(--sb-swipe-tint, var(--sb-color-gray));
|
|
2216
|
+
color: #fff;
|
|
2217
|
+
font: 500 var(--sb-ts-footnote-size, 13px) / 1.2 var(--sb-font-family);
|
|
2218
|
+
white-space: nowrap;
|
|
2219
|
+
overflow: hidden;
|
|
2220
|
+
cursor: pointer;
|
|
2221
|
+
touch-action: manipulation;
|
|
2222
|
+
-webkit-tap-highlight-color: transparent;
|
|
2223
|
+
}
|
|
2224
|
+
|
|
2225
|
+
.sb-swipe-action[data-sb-role="destructive"] {
|
|
2226
|
+
background: var(--sb-swipe-tint, var(--sb-color-red));
|
|
2227
|
+
}
|
|
2228
|
+
|
|
2229
|
+
.sb-swipe-action:active {
|
|
2230
|
+
filter: brightness(0.85);
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2233
|
+
/* Once every button has its 74px, only the one nearest the content grows. */
|
|
2234
|
+
.sb-swipe-actions[data-sb-stretch] > .sb-swipe-action {
|
|
2235
|
+
flex: 0 0 74px;
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
.sb-swipe-actions[data-sb-stretch] > .sb-swipe-action:first-child {
|
|
2239
|
+
flex: 1 1 74px;
|
|
2240
|
+
}
|
|
2241
|
+
|
|
2242
|
+
.sb-swipe-action-symbol {
|
|
2243
|
+
display: inline-flex;
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
.sb-swipe-action-symbol > svg {
|
|
2247
|
+
width: 22px;
|
|
2248
|
+
height: 22px;
|
|
2249
|
+
stroke-width: 2;
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
.sb-swipe-action-title {
|
|
2253
|
+
overflow: hidden;
|
|
2254
|
+
text-overflow: ellipsis;
|
|
2255
|
+
max-width: 100%;
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
/* `listrow`: the edit-mode control at the engine's `editControl` rect (hidden
|
|
2259
|
+
otherwise); a selected row reuses the Phase 7 selected-row fill. */
|
|
2260
|
+
.sb-listrow-control {
|
|
2261
|
+
appearance: none;
|
|
2262
|
+
position: absolute;
|
|
2263
|
+
z-index: 2;
|
|
2264
|
+
display: flex;
|
|
2265
|
+
align-items: center;
|
|
2266
|
+
justify-content: center;
|
|
2267
|
+
margin: 0;
|
|
2268
|
+
padding: 0;
|
|
2269
|
+
border: 0;
|
|
2270
|
+
background: none;
|
|
2271
|
+
cursor: pointer;
|
|
2272
|
+
touch-action: manipulation;
|
|
2273
|
+
-webkit-tap-highlight-color: transparent;
|
|
2274
|
+
animation: sb-fade-in 0.25s ease-out both;
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
.sb-listrow-control[hidden] {
|
|
2278
|
+
display: none;
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
.sb-listrow-circle {
|
|
2282
|
+
display: flex;
|
|
2283
|
+
align-items: center;
|
|
2284
|
+
justify-content: center;
|
|
2285
|
+
width: 22px;
|
|
2286
|
+
height: 22px;
|
|
2287
|
+
border-radius: 50%;
|
|
2288
|
+
box-sizing: border-box;
|
|
2289
|
+
border: 1.5px solid var(--sb-color-tertiary-label);
|
|
2290
|
+
color: #fff;
|
|
2291
|
+
transition:
|
|
2292
|
+
background-color 0.2s,
|
|
2293
|
+
border-color 0.2s;
|
|
2294
|
+
}
|
|
2295
|
+
|
|
2296
|
+
.sb-listrow-circle > svg {
|
|
2297
|
+
display: none;
|
|
2298
|
+
width: 14px;
|
|
2299
|
+
height: 14px;
|
|
2300
|
+
stroke: currentColor;
|
|
2301
|
+
stroke-width: 3;
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
.sb-listrow[data-sb-selectable][data-sb-selected] .sb-listrow-circle {
|
|
2305
|
+
background: var(--sb-color-accent);
|
|
2306
|
+
border-color: var(--sb-color-accent);
|
|
2307
|
+
}
|
|
2308
|
+
|
|
2309
|
+
.sb-listrow[data-sb-selectable][data-sb-selected] .sb-listrow-circle > .sb-listrow-check {
|
|
2310
|
+
display: block;
|
|
2311
|
+
}
|
|
2312
|
+
|
|
2313
|
+
/* Deletable only: the red minus. */
|
|
2314
|
+
.sb-listrow[data-sb-deletable]:not([data-sb-selectable]) .sb-listrow-circle {
|
|
2315
|
+
background: var(--sb-color-red);
|
|
2316
|
+
border-color: var(--sb-color-red);
|
|
2317
|
+
}
|
|
2318
|
+
|
|
2319
|
+
.sb-listrow[data-sb-deletable]:not([data-sb-selectable]) .sb-listrow-circle > .sb-listrow-minus {
|
|
2320
|
+
display: block;
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2323
|
+
.sb-listrow[data-sb-selectable] {
|
|
2324
|
+
cursor: pointer;
|
|
2325
|
+
}
|
|
2326
|
+
|
|
2327
|
+
.sb-listrow[data-sb-selected],
|
|
2328
|
+
.sb-list[data-sb-list-style] > .sb-list-content > .sb-listrow[data-sb-selected],
|
|
2329
|
+
.sb-list[data-sb-list-style] .sb-section-rows > .sb-listrow[data-sb-selected] {
|
|
2330
|
+
background: var(--sb-color-tertiary-fill);
|
|
2331
|
+
border-radius: 10px;
|
|
2332
|
+
}
|
|
2333
|
+
|
|
2334
|
+
@keyframes sb-fade-in {
|
|
2335
|
+
from {
|
|
2336
|
+
opacity: 0;
|
|
2337
|
+
}
|
|
2338
|
+
to {
|
|
2339
|
+
opacity: 1;
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
|
|
2343
|
+
/* Pull to refresh: a 40px spinner row above the content while `refreshing`
|
|
2344
|
+
(the content moves down by 40 like an iOS content inset); during a pull the
|
|
2345
|
+
controller fades it in by `--sb-pull` (0...1). */
|
|
2346
|
+
.sb-refresh {
|
|
2347
|
+
position: absolute;
|
|
2348
|
+
left: 0;
|
|
2349
|
+
right: 0;
|
|
2350
|
+
top: 0;
|
|
2351
|
+
height: 40px;
|
|
2352
|
+
display: flex;
|
|
2353
|
+
align-items: center;
|
|
2354
|
+
justify-content: center;
|
|
2355
|
+
pointer-events: none;
|
|
2356
|
+
opacity: var(--sb-pull, 0);
|
|
2357
|
+
transition: opacity 0.2s;
|
|
2358
|
+
}
|
|
2359
|
+
|
|
2360
|
+
.sb-refresh-spinner {
|
|
2361
|
+
width: 20px;
|
|
2362
|
+
height: 20px;
|
|
2363
|
+
border-radius: 50%;
|
|
2364
|
+
border: 2.5px solid var(--sb-color-secondary);
|
|
2365
|
+
border-top-color: transparent;
|
|
2366
|
+
transform: rotate(calc(var(--sb-pull, 0) * 360deg));
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2369
|
+
[data-sb-refreshing] > .sb-refresh {
|
|
2370
|
+
opacity: 1;
|
|
2371
|
+
}
|
|
2372
|
+
|
|
2373
|
+
[data-sb-refreshing] > .sb-refresh > .sb-refresh-spinner {
|
|
2374
|
+
animation: sb-spin 0.9s linear infinite;
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
.sb-list[data-sb-refreshable] > .sb-list-content,
|
|
2378
|
+
.sb-scrollview[data-sb-refreshable] > .sb-scroll-content {
|
|
2379
|
+
transition: transform 0.3s var(--sb-nav-easing);
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
[data-sb-pulling] > .sb-list-content,
|
|
2383
|
+
[data-sb-pulling] > .sb-scroll-content {
|
|
2384
|
+
transition: none;
|
|
2385
|
+
}
|
|
2386
|
+
|
|
2387
|
+
[data-sb-refreshing] > .sb-list-content,
|
|
2388
|
+
[data-sb-refreshing] > .sb-scroll-content {
|
|
2389
|
+
transform: translateY(40px);
|
|
2390
|
+
}
|
|
2391
|
+
|
|
2392
|
+
/* Swipe back: the strip along a pushed screen's leading edge where a
|
|
2393
|
+
rightward pan starts. Only touch needs it (to keep the browser from
|
|
2394
|
+
scrolling): for the mouse the controller reads the pointer position. */
|
|
2395
|
+
.sb-navscreen-edge {
|
|
2396
|
+
position: absolute;
|
|
2397
|
+
left: 0;
|
|
2398
|
+
top: 0;
|
|
2399
|
+
bottom: 0;
|
|
2400
|
+
width: 24px;
|
|
2401
|
+
z-index: 3;
|
|
2402
|
+
display: none;
|
|
2403
|
+
pointer-events: none;
|
|
2404
|
+
touch-action: none;
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
.sb-navscreen[data-sb-nav-position="top"]:not([data-sb-depth="0"]) > .sb-navscreen-edge {
|
|
2408
|
+
display: block;
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2411
|
+
@media (pointer: coarse) {
|
|
2412
|
+
.sb-navscreen[data-sb-nav-position="top"]:not([data-sb-depth="0"]) > .sb-navscreen-edge {
|
|
2413
|
+
pointer-events: auto;
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2417
|
+
/* The screen below dims by `--sb-nav-dim` while it sits behind the top one
|
|
2418
|
+
(0.12 at rest; the swipe back fades it out as the top screen uncovers it). */
|
|
2419
|
+
.sb-navscreen::after {
|
|
2420
|
+
content: "";
|
|
2421
|
+
position: absolute;
|
|
2422
|
+
inset: 0;
|
|
2423
|
+
z-index: 4;
|
|
2424
|
+
background-color: rgba(0, 0, 0, var(--sb-nav-dim, 0));
|
|
2425
|
+
pointer-events: none;
|
|
2426
|
+
transition: background-color 0.4s var(--sb-nav-easing);
|
|
2427
|
+
}
|
|
2428
|
+
|
|
2429
|
+
.sb-navscreen[data-sb-nav-position="below"] {
|
|
2430
|
+
--sb-nav-dim: 0.12;
|
|
2431
|
+
}
|
|
2432
|
+
|
|
2433
|
+
/* While dragging, the screens follow the pointer (inline transforms); the
|
|
2434
|
+
dragged screen casts the push shadow. */
|
|
2435
|
+
.sb-navscreen[data-sb-swipe-dragging],
|
|
2436
|
+
.sb-navscreen[data-sb-swipe-pop] {
|
|
2437
|
+
z-index: 2;
|
|
2438
|
+
box-shadow: -8px 0 24px rgba(0, 0, 0, 0.12);
|
|
2439
|
+
}
|
|
2440
|
+
|
|
2441
|
+
/* The pop clone starts where a swipe back left the screen (overrides the
|
|
2442
|
+
Phase 2 keyframes of the same name). */
|
|
2443
|
+
@keyframes sb-nav-pop {
|
|
2444
|
+
from {
|
|
2445
|
+
transform: translateX(var(--sb-nav-from, 0px));
|
|
2446
|
+
}
|
|
2447
|
+
to {
|
|
2448
|
+
transform: translateX(100%);
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
|
|
2452
|
+
|
|
2453
|
+
/* Phase 8: alerts, controls, effects */
|
|
2454
|
+
|
|
2455
|
+
/* ---------------------------------------------------------------------------
|
|
2456
|
+
Alerts and confirmation dialogs: a root-level layer above sheets (z 6 > 5).
|
|
2457
|
+
The scrim fades in over 0.25s; the layer (cards, texts, buttons, OK) fades
|
|
2458
|
+
and, for the `alert` style, scales from 1.1 about the card's center (the
|
|
2459
|
+
renderer sets the layer's transform-origin from the engine's `card` rect).
|
|
2460
|
+
A dismissed alert leaves a ghost clone that fades out.
|
|
2461
|
+
------------------------------------------------------------------------- */
|
|
2462
|
+
.screen > .sb-alert,
|
|
2463
|
+
.sb-alert {
|
|
2464
|
+
position: absolute;
|
|
2465
|
+
inset: 0;
|
|
2466
|
+
width: auto;
|
|
2467
|
+
height: auto;
|
|
2468
|
+
z-index: 6;
|
|
2469
|
+
display: block;
|
|
2470
|
+
}
|
|
2471
|
+
|
|
2472
|
+
/* Above a `large` sheet the presenting tree scales down; an alert never does. */
|
|
2473
|
+
.screen[data-sb-sheet] > .sb-alert,
|
|
2474
|
+
.screen > .sb-alert {
|
|
2475
|
+
transform: none;
|
|
2476
|
+
transition: none;
|
|
2477
|
+
}
|
|
2478
|
+
|
|
2479
|
+
.sb-alert-scrim {
|
|
2480
|
+
position: absolute;
|
|
2481
|
+
inset: 0;
|
|
2482
|
+
background: rgba(0, 0, 0, 0.3);
|
|
2483
|
+
animation: sb-alert-fade-in 0.25s ease-out both;
|
|
2484
|
+
}
|
|
2485
|
+
|
|
2486
|
+
/* Chrome the engine gave no rect gets the `hidden` attribute (layoutDom);
|
|
2487
|
+
the display rules below must not override it. */
|
|
2488
|
+
.sb-alert-layer > [hidden],
|
|
2489
|
+
.sb-datepicker > [hidden],
|
|
2490
|
+
.sb-stepper > [hidden],
|
|
2491
|
+
.sb-slider > [hidden] {
|
|
2492
|
+
display: none;
|
|
2493
|
+
}
|
|
2494
|
+
|
|
2495
|
+
/* The layer spans the screen only to be animated as one; taps between its
|
|
2496
|
+
parts fall through to the scrim. */
|
|
2497
|
+
.sb-alert-layer {
|
|
2498
|
+
position: absolute;
|
|
2499
|
+
inset: 0;
|
|
2500
|
+
pointer-events: none;
|
|
2501
|
+
}
|
|
2502
|
+
|
|
2503
|
+
.sb-alert-layer > * {
|
|
2504
|
+
pointer-events: auto;
|
|
2505
|
+
}
|
|
2506
|
+
|
|
2507
|
+
.sb-alert--in > .sb-alert-layer {
|
|
2508
|
+
animation: sb-alert-fade-in 0.25s ease-out both;
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
.sb-alert[data-sb-style="alert"].sb-alert--in > .sb-alert-layer {
|
|
2512
|
+
animation: sb-alert-in 0.25s cubic-bezier(0.2, 0.8, 0.2, 1) both;
|
|
2513
|
+
}
|
|
2514
|
+
|
|
2515
|
+
.sb-alert--out {
|
|
2516
|
+
pointer-events: none;
|
|
2517
|
+
animation: sb-alert-fade-out 0.25s ease-out both;
|
|
2518
|
+
}
|
|
2519
|
+
|
|
2520
|
+
.sb-alert--out > .sb-alert-scrim,
|
|
2521
|
+
.sb-alert--out > .sb-alert-layer {
|
|
2522
|
+
animation: none;
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2525
|
+
.sb-alert-card,
|
|
2526
|
+
.sb-alert-cancel-card {
|
|
2527
|
+
position: absolute;
|
|
2528
|
+
box-sizing: border-box;
|
|
2529
|
+
border-radius: 14px;
|
|
2530
|
+
background: color-mix(in srgb, var(--sb-color-secondary-system-background) 85%, transparent);
|
|
2531
|
+
-webkit-backdrop-filter: blur(24px) saturate(180%);
|
|
2532
|
+
backdrop-filter: blur(24px) saturate(180%);
|
|
2533
|
+
overflow: hidden;
|
|
2534
|
+
}
|
|
2535
|
+
|
|
2536
|
+
.sb-alert-cancel-card {
|
|
2537
|
+
background: var(--sb-color-sheet-background);
|
|
2538
|
+
}
|
|
2539
|
+
|
|
2540
|
+
.sb-alert-title,
|
|
2541
|
+
.sb-alert-message {
|
|
2542
|
+
position: absolute;
|
|
2543
|
+
display: block;
|
|
2544
|
+
white-space: pre;
|
|
2545
|
+
text-align: center;
|
|
2546
|
+
color: var(--sb-color-primary);
|
|
2547
|
+
overflow: hidden;
|
|
2548
|
+
}
|
|
2549
|
+
|
|
2550
|
+
/* The action sheet's title and message are a dim caption. */
|
|
2551
|
+
.sb-alert[data-sb-style="dialog"] .sb-alert-title,
|
|
2552
|
+
.sb-alert[data-sb-style="dialog"] .sb-alert-message {
|
|
2553
|
+
color: var(--sb-color-secondary);
|
|
2554
|
+
}
|
|
2555
|
+
|
|
2556
|
+
/* Button children: the engine gives each its row and centers its label;
|
|
2557
|
+
every row has a hairline above it. Two side-by-side buttons (`row` layout)
|
|
2558
|
+
also get a hairline between them. */
|
|
2559
|
+
.sb-alert-buttons > .sb-button,
|
|
2560
|
+
.sb-alert-ok {
|
|
2561
|
+
color: var(--sb-tint, var(--sb-color-accent));
|
|
2562
|
+
font-size: var(--sb-font-size-body);
|
|
2563
|
+
text-align: center;
|
|
2564
|
+
overflow: hidden;
|
|
2565
|
+
}
|
|
2566
|
+
|
|
2567
|
+
.sb-alert-buttons > .sb-button::before,
|
|
2568
|
+
.sb-alert-ok::before {
|
|
2569
|
+
content: "";
|
|
2570
|
+
position: absolute;
|
|
2571
|
+
left: 0;
|
|
2572
|
+
right: 0;
|
|
2573
|
+
top: 0;
|
|
2574
|
+
height: 1px;
|
|
2575
|
+
background: var(--sb-color-separator);
|
|
2576
|
+
pointer-events: none;
|
|
2577
|
+
}
|
|
2578
|
+
|
|
2579
|
+
.sb-alert[data-sb-layout="row"] .sb-alert-buttons > .sb-button:nth-child(2)::after {
|
|
2580
|
+
content: "";
|
|
2581
|
+
position: absolute;
|
|
2582
|
+
left: 0;
|
|
2583
|
+
top: 0;
|
|
2584
|
+
bottom: 0;
|
|
2585
|
+
width: 1px;
|
|
2586
|
+
background: var(--sb-color-separator);
|
|
2587
|
+
pointer-events: none;
|
|
2588
|
+
}
|
|
2589
|
+
|
|
2590
|
+
.sb-alert-buttons > .sb-button[data-sb-role="cancel"] {
|
|
2591
|
+
font-weight: 600;
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2594
|
+
.sb-alert-buttons > .sb-button[data-sb-role="destructive"] {
|
|
2595
|
+
color: var(--sb-color-red);
|
|
2596
|
+
}
|
|
2597
|
+
|
|
2598
|
+
.sb-alert-buttons > .sb-button:active,
|
|
2599
|
+
.sb-alert-ok:active {
|
|
2600
|
+
opacity: 1;
|
|
2601
|
+
background: var(--sb-color-fill);
|
|
2602
|
+
}
|
|
2603
|
+
|
|
2604
|
+
.sb-alert-ok {
|
|
2605
|
+
appearance: none;
|
|
2606
|
+
position: absolute;
|
|
2607
|
+
margin: 0;
|
|
2608
|
+
padding: 0;
|
|
2609
|
+
border: 0;
|
|
2610
|
+
background: none;
|
|
2611
|
+
font: inherit;
|
|
2612
|
+
font-size: var(--sb-font-size-body);
|
|
2613
|
+
cursor: pointer;
|
|
2614
|
+
touch-action: manipulation;
|
|
2615
|
+
-webkit-tap-highlight-color: transparent;
|
|
2616
|
+
}
|
|
2617
|
+
|
|
2618
|
+
/* Action sheet rows are taller and 20pt; its cancel button sits in its own card (no hairline). */
|
|
2619
|
+
.sb-alert[data-sb-style="dialog"] .sb-alert-buttons > .sb-button,
|
|
2620
|
+
.sb-alert[data-sb-style="dialog"] .sb-alert-ok {
|
|
2621
|
+
font-size: 20px;
|
|
2622
|
+
}
|
|
2623
|
+
|
|
2624
|
+
.sb-alert[data-sb-style="dialog"] .sb-alert-buttons > .sb-button[data-sb-role="cancel"]::before {
|
|
2625
|
+
display: none;
|
|
2626
|
+
}
|
|
2627
|
+
|
|
2628
|
+
/* Without a title or message the first row is flush with the card's top: no hairline. */
|
|
2629
|
+
.sb-alert[data-sb-style="dialog"]:not([data-sb-header]) .sb-alert-buttons > .sb-button:first-child::before,
|
|
2630
|
+
.sb-alert[data-sb-style="dialog"]:not([data-sb-header]) .sb-alert-ok::before {
|
|
2631
|
+
display: none;
|
|
2632
|
+
}
|
|
2633
|
+
|
|
2634
|
+
@keyframes sb-alert-fade-in {
|
|
2635
|
+
from {
|
|
2636
|
+
opacity: 0;
|
|
2637
|
+
}
|
|
2638
|
+
to {
|
|
2639
|
+
opacity: 1;
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
|
|
2643
|
+
@keyframes sb-alert-in {
|
|
2644
|
+
from {
|
|
2645
|
+
opacity: 0;
|
|
2646
|
+
transform: scale(1.1);
|
|
2647
|
+
}
|
|
2648
|
+
to {
|
|
2649
|
+
opacity: 1;
|
|
2650
|
+
transform: scale(1);
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
|
|
2654
|
+
@keyframes sb-alert-fade-out {
|
|
2655
|
+
from {
|
|
2656
|
+
opacity: 1;
|
|
2657
|
+
}
|
|
2658
|
+
to {
|
|
2659
|
+
opacity: 0;
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
|
|
2663
|
+
/* ---------------------------------------------------------------------------
|
|
2664
|
+
Full-screen cover: a sheet with `detents: ["full"]` has no radius, grabber
|
|
2665
|
+
(the engine gives it none) or drag to dismiss.
|
|
2666
|
+
------------------------------------------------------------------------- */
|
|
2667
|
+
.sb-sheet[data-sb-detent="full"] > .sb-sheet-card {
|
|
2668
|
+
border-radius: 0;
|
|
2669
|
+
box-shadow: none;
|
|
2670
|
+
touch-action: auto;
|
|
2671
|
+
}
|
|
2672
|
+
|
|
2673
|
+
/* ---------------------------------------------------------------------------
|
|
2674
|
+
Stepper: the label children are positioned by their frames; the 94×32
|
|
2675
|
+
segmented control sits at the engine's `control` rect, its halves divided
|
|
2676
|
+
by a hairline. A dimmed half (`aria-disabled`) swallows taps.
|
|
2677
|
+
------------------------------------------------------------------------- */
|
|
2678
|
+
.sb-stepper-control {
|
|
2679
|
+
position: absolute;
|
|
2680
|
+
display: flex;
|
|
2681
|
+
box-sizing: border-box;
|
|
2682
|
+
border-radius: 8px;
|
|
2683
|
+
background: var(--sb-color-tertiary-fill);
|
|
2684
|
+
overflow: hidden;
|
|
2685
|
+
}
|
|
2686
|
+
|
|
2687
|
+
.sb-stepper-button {
|
|
2688
|
+
appearance: none;
|
|
2689
|
+
flex: 1 1 0;
|
|
2690
|
+
display: flex;
|
|
2691
|
+
align-items: center;
|
|
2692
|
+
justify-content: center;
|
|
2693
|
+
margin: 0;
|
|
2694
|
+
padding: 0;
|
|
2695
|
+
border: 0;
|
|
2696
|
+
background: none;
|
|
2697
|
+
color: var(--sb-color-primary);
|
|
2698
|
+
font: inherit;
|
|
2699
|
+
font-size: var(--sb-font-size-body);
|
|
2700
|
+
cursor: pointer;
|
|
2701
|
+
touch-action: manipulation;
|
|
2702
|
+
-webkit-tap-highlight-color: transparent;
|
|
2703
|
+
}
|
|
2704
|
+
|
|
2705
|
+
.sb-stepper-button + .sb-stepper-button {
|
|
2706
|
+
box-shadow: -1px 0 0 var(--sb-color-separator);
|
|
2707
|
+
}
|
|
2708
|
+
|
|
2709
|
+
.sb-stepper-button > svg {
|
|
2710
|
+
display: block;
|
|
2711
|
+
width: 16px;
|
|
2712
|
+
height: 16px;
|
|
2713
|
+
stroke: currentColor;
|
|
2714
|
+
stroke-width: 2.5;
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2717
|
+
.sb-stepper-button:active:not([aria-disabled="true"]) {
|
|
2718
|
+
background: var(--sb-color-fill);
|
|
2719
|
+
}
|
|
2720
|
+
|
|
2721
|
+
.sb-stepper-button[aria-disabled="true"] {
|
|
2722
|
+
opacity: 0.35;
|
|
2723
|
+
cursor: default;
|
|
2724
|
+
}
|
|
2725
|
+
|
|
2726
|
+
/* ---------------------------------------------------------------------------
|
|
2727
|
+
Slider: the label children are positioned by their frames; the 4pt track,
|
|
2728
|
+
the accent fill and the 27pt thumb are chrome at the engine's rects. A
|
|
2729
|
+
pointer anywhere on the slider moves the thumb (touch-action none).
|
|
2730
|
+
------------------------------------------------------------------------- */
|
|
2731
|
+
.sb-slider {
|
|
2732
|
+
touch-action: none;
|
|
2733
|
+
cursor: pointer;
|
|
2734
|
+
-webkit-tap-highlight-color: transparent;
|
|
2735
|
+
}
|
|
2736
|
+
|
|
2737
|
+
.sb-slider-track {
|
|
2738
|
+
position: absolute;
|
|
2739
|
+
border-radius: 2px;
|
|
2740
|
+
background: var(--sb-color-tertiary-fill);
|
|
2741
|
+
}
|
|
2742
|
+
|
|
2743
|
+
.sb-slider-fill {
|
|
2744
|
+
position: absolute;
|
|
2745
|
+
border-radius: 2px;
|
|
2746
|
+
background: var(--sb-tint, var(--sb-color-accent));
|
|
2747
|
+
}
|
|
2748
|
+
|
|
2749
|
+
.sb-slider-thumb {
|
|
2750
|
+
position: absolute;
|
|
2751
|
+
box-sizing: border-box;
|
|
2752
|
+
border-radius: 50%;
|
|
2753
|
+
background: #ffffff;
|
|
2754
|
+
box-shadow:
|
|
2755
|
+
0 3px 8px rgba(0, 0, 0, 0.15),
|
|
2756
|
+
0 3px 1px rgba(0, 0, 0, 0.06),
|
|
2757
|
+
0 0 0 0.5px rgba(0, 0, 0, 0.04);
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2760
|
+
/* ---------------------------------------------------------------------------
|
|
2761
|
+
Date picker (compact): the label children are positioned by their frames;
|
|
2762
|
+
the date / time capsules are chrome at the engine's rects, each with an
|
|
2763
|
+
invisible native input stretched over it that opens the browser's picker.
|
|
2764
|
+
------------------------------------------------------------------------- */
|
|
2765
|
+
.sb-datepicker-capsule {
|
|
2766
|
+
position: absolute;
|
|
2767
|
+
display: flex;
|
|
2768
|
+
align-items: center;
|
|
2769
|
+
justify-content: center;
|
|
2770
|
+
box-sizing: border-box;
|
|
2771
|
+
border-radius: 8px;
|
|
2772
|
+
background: var(--sb-color-tertiary-fill);
|
|
2773
|
+
color: var(--sb-color-primary);
|
|
2774
|
+
font-size: var(--sb-font-size-body);
|
|
2775
|
+
line-height: 1.3;
|
|
2776
|
+
white-space: nowrap;
|
|
2777
|
+
overflow: hidden;
|
|
2778
|
+
}
|
|
2779
|
+
|
|
2780
|
+
.sb-datepicker-text {
|
|
2781
|
+
display: block;
|
|
2782
|
+
overflow: hidden;
|
|
2783
|
+
text-overflow: ellipsis;
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2786
|
+
.sb-datepicker-input {
|
|
2787
|
+
position: absolute;
|
|
2788
|
+
inset: 0;
|
|
2789
|
+
width: 100%;
|
|
2790
|
+
height: 100%;
|
|
2791
|
+
margin: 0;
|
|
2792
|
+
padding: 0;
|
|
2793
|
+
border: 0;
|
|
2794
|
+
opacity: 0;
|
|
2795
|
+
cursor: pointer;
|
|
2796
|
+
color-scheme: light dark;
|
|
2797
|
+
}
|
|
2798
|
+
|
|
2799
|
+
.sb-datepicker-input::-webkit-calendar-picker-indicator {
|
|
2800
|
+
position: absolute;
|
|
2801
|
+
inset: 0;
|
|
2802
|
+
width: 100%;
|
|
2803
|
+
height: 100%;
|
|
2804
|
+
cursor: pointer;
|
|
2805
|
+
}
|
|
2806
|
+
|
|
2807
|
+
/* ---------------------------------------------------------------------------
|
|
2808
|
+
Text editor: a borderless textarea filling its frame in the body font (its
|
|
2809
|
+
parent provides any frame or background).
|
|
2810
|
+
------------------------------------------------------------------------- */
|
|
2811
|
+
.sb-texteditor {
|
|
2812
|
+
appearance: none;
|
|
2813
|
+
display: block;
|
|
2814
|
+
margin: 0;
|
|
2815
|
+
padding: 0;
|
|
2816
|
+
border: 0;
|
|
2817
|
+
outline: none;
|
|
2818
|
+
resize: none;
|
|
2819
|
+
overflow: auto;
|
|
2820
|
+
background: transparent;
|
|
2821
|
+
color: var(--sb-color-primary);
|
|
2822
|
+
font: inherit;
|
|
2823
|
+
font-size: var(--sb-font-size-body);
|
|
2824
|
+
line-height: 1.3;
|
|
2825
|
+
}
|
|
2826
|
+
|
|
2827
|
+
@media (prefers-reduced-motion: reduce) {
|
|
2828
|
+
.sb-alert-scrim,
|
|
2829
|
+
.sb-alert--in > .sb-alert-layer,
|
|
2830
|
+
.sb-alert[data-sb-style="alert"].sb-alert--in > .sb-alert-layer,
|
|
2831
|
+
.sb-alert--out {
|
|
2832
|
+
animation-duration: 0.01s;
|
|
2833
|
+
}
|
|
2834
|
+
}
|