@swiftbrowser/web 0.5.6 → 0.6.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/src/styles.css CHANGED
@@ -106,6 +106,8 @@
106
106
  rgba(255, 255, 255, 0.6) 360deg
107
107
  );
108
108
  --sb-glass-selected: rgba(120, 120, 128, 0.16);
109
+ /* The glass under a pressed button brightens (src/press.ts). */
110
+ --sb-glass-pressed: rgba(255, 255, 255, 0.92);
109
111
  --sb-color-segment-track: rgba(118, 118, 128, 0.12);
110
112
  --sb-color-segment-selected: #ffffff;
111
113
  /* Menus, floating sheets and alerts are Liquid Glass too: translucent
@@ -163,6 +165,7 @@
163
165
  rgba(255, 255, 255, 0.125) 360deg
164
166
  );
165
167
  --sb-glass-selected: rgba(120, 120, 128, 0.32);
168
+ --sb-glass-pressed: rgba(72, 72, 72, 0.92);
166
169
  /* Sheets float above the window and take the elevated system background. */
167
170
  --sb-color-sheet-background: #1c1c1e;
168
171
  --sb-color-grabber: rgba(235, 235, 245, 0.3);
@@ -480,6 +483,12 @@ html[data-sb-mode="device"] .device-screen {
480
483
  Phase 2: button styles, disabled, image, divider, scrollview
481
484
  ------------------------------------------------------------------------- */
482
485
 
486
+ /* `.buttonStyle(.plain)`: the label in the foreground it is in (the label
487
+ color unless a `.foregroundStyle` says otherwise), not the tint. */
488
+ .sb-button[data-sb-style="plain"] {
489
+ color: var(--sb-button-color, inherit);
490
+ }
491
+
483
492
  .sb-button[data-sb-style="bordered"],
484
493
  .sb-button[data-sb-style="borderedProminent"] {
485
494
  border-radius: 9999px;
@@ -849,14 +858,21 @@ svg[data-sb-symbol="progress.indicator"] > line:nth-child(8) { animation-delay:
849
858
  }
850
859
 
851
860
  /* Rows: every direct child of a section's card, and every non-section child
852
- of a list (loose rows form their own card). */
861
+ of a list (loose rows form their own card). `.listRowBackground(_:)` sets
862
+ `--sb-row-background` on its row (src/layoutDom.ts). */
853
863
  .sb-section-rows > [data-sb-id],
854
864
  .sb-section-rows > [data-sb-ghost],
855
865
  .sb-list-content > :not(.sb-section) {
856
- background: var(--sb-color-grouped-cell);
866
+ background: var(--sb-row-background, var(--sb-color-grouped-cell));
857
867
  text-align: left;
858
868
  }
859
869
 
870
+ /* A card with such a row leaves the cell paint to its rows, so a clear row
871
+ shows the list's background. */
872
+ .sb-section-rows:has(> [data-sb-row-background]) {
873
+ background: transparent;
874
+ }
875
+
860
876
  .sb-list-content > :not(.sb-section):first-child,
861
877
  .sb-list-content > .sb-section + :not(.sb-section) {
862
878
  border-top-left-radius: 26px;
@@ -1390,7 +1406,7 @@ svg[data-sb-symbol="progress.indicator"] > line:nth-child(8) { animation-delay:
1390
1406
 
1391
1407
  /* A sheet is a root-level layer covering the screen: a scrim over the
1392
1408
  presenting tree and the card at the engine's frame (large: screen height −
1393
- status bar − 10; medium: half), sliding up from the bottom. */
1409
+ status bar; medium: half), sliding up from the bottom. */
1394
1410
  .screen > .sb-sheet,
1395
1411
  .sb-sheet {
1396
1412
  position: absolute;
@@ -1401,17 +1417,19 @@ svg[data-sb-symbol="progress.indicator"] > line:nth-child(8) { animation-delay:
1401
1417
  display: block;
1402
1418
  }
1403
1419
 
1420
+ /* iOS 26: the dim behind a sheet is 20% black (measured). */
1404
1421
  .sb-sheet-scrim {
1405
1422
  position: absolute;
1406
1423
  inset: 0;
1407
- background: rgba(0, 0, 0, 0.4);
1424
+ background: rgba(0, 0, 0, 0.2);
1408
1425
  animation: sb-sheet-scrim-in 0.4s ease-out both;
1409
1426
  }
1410
1427
 
1411
1428
  .sb-sheet-card {
1412
1429
  position: absolute;
1413
1430
  box-sizing: border-box;
1414
- border-radius: 10px 10px 0 0;
1431
+ /* iOS 26: a large sheet's top corners follow the screen's (about 38). */
1432
+ border-radius: 38px 38px 0 0;
1415
1433
  background: var(--sb-color-sheet-background);
1416
1434
  box-shadow: 0 -2px 20px rgba(0, 0, 0, 0.15);
1417
1435
  overflow: hidden;
@@ -1502,15 +1520,8 @@ svg[data-sb-symbol="progress.indicator"] > line:nth-child(8) { animation-delay:
1502
1520
  transform-origin: 50% 0;
1503
1521
  }
1504
1522
 
1505
- /* Behind a `large` sheet the presenting tree scales to 0.92 and dims. */
1506
- .screen > :not(.sb-sheet) {
1507
- transform-origin: 50% 0;
1508
- transition: transform 0.4s cubic-bezier(0.2, 0.8, 0.2, 1);
1509
- }
1510
-
1511
- .screen[data-sb-sheet="large"]:where(:not(:has(> .sb-sheet[data-sb-sheet-position="top"][data-sb-form]))) > :not(.sb-sheet, .sb-exit-clone, .sb-picker-menu, .sb-picker-menu-exit) {
1512
- transform: scale(0.92);
1513
- }
1523
+ /* iOS 26: the presenting tree stays as it is behind a sheet (only dimmed);
1524
+ earlier versions scaled it to 0.92 behind a large one. */
1514
1525
 
1515
1526
  @keyframes sb-sheet-in {
1516
1527
  from {
@@ -1553,8 +1564,7 @@ svg[data-sb-symbol="progress.indicator"] > line:nth-child(8) { animation-delay:
1553
1564
  .sb-sheet-card,
1554
1565
  .sb-sheet--in > .sb-sheet-card,
1555
1566
  .sb-sheet--out > .sb-sheet-card,
1556
- .sb-sheet--out > .sb-sheet-scrim,
1557
- .screen > :not(.sb-sheet) {
1567
+ .sb-sheet--out > .sb-sheet-scrim {
1558
1568
  animation-duration: 0.01s;
1559
1569
  transition-duration: 0.01s;
1560
1570
  }
@@ -1705,6 +1715,25 @@ svg[data-sb-symbol="progress.indicator"] > line:nth-child(8) { animation-delay:
1705
1715
  stroke-width: 1.75;
1706
1716
  }
1707
1717
 
1718
+ /* A catalog icon: a custom symbol masks the item's color; a bitmap fits the slot. */
1719
+ .sb-tabbar-icon > .sb-tabbar-mask,
1720
+ .sb-tabbar-icon > img {
1721
+ display: block;
1722
+ width: 1em;
1723
+ height: 1em;
1724
+ object-fit: contain;
1725
+ }
1726
+
1727
+ .sb-tabbar-icon > .sb-tabbar-mask {
1728
+ background-color: currentColor;
1729
+ mask-size: contain;
1730
+ mask-repeat: no-repeat;
1731
+ mask-position: center;
1732
+ -webkit-mask-size: contain;
1733
+ -webkit-mask-repeat: no-repeat;
1734
+ -webkit-mask-position: center;
1735
+ }
1736
+
1708
1737
  .sb-tabbar-icon[data-sb-missing] {
1709
1738
  border: 1px dashed currentColor;
1710
1739
  border-radius: 4px;
@@ -2025,13 +2054,16 @@ svg[data-sb-symbol="progress.indicator"] > line:nth-child(8) { animation-delay:
2025
2054
  overflow: visible;
2026
2055
  }
2027
2056
 
2057
+ /* iOS 26: the value in the tint, else the primary color; the track in the
2058
+ same color at 35%. */
2028
2059
  .sb-gauge-ring-track {
2029
- stroke: var(--sb-color-fill);
2060
+ stroke: var(--sb-tint, var(--sb-color-primary));
2061
+ stroke-opacity: 0.35;
2030
2062
  stroke-linecap: round;
2031
2063
  }
2032
2064
 
2033
2065
  .sb-gauge-ring-value {
2034
- stroke: var(--sb-tint, var(--sb-color-accent));
2066
+ stroke: var(--sb-tint, var(--sb-color-primary));
2035
2067
  stroke-linecap: round;
2036
2068
  }
2037
2069
 
@@ -2337,6 +2369,84 @@ svg[data-sb-symbol="progress.indicator"] > line:nth-child(8) { animation-delay:
2337
2369
  pointer-events: none;
2338
2370
  }
2339
2371
 
2372
+ /* iOS 26 press feedback on Liquid Glass (src/press.ts): while a pointer is
2373
+ down on a bar button or a glass button, the glass under it (a bar group's
2374
+ capsule, a bottom bar capsule, the back and More circles, the sidebar
2375
+ toggle, a `.glass` button, a `.glassEffect` box) swells and brightens;
2376
+ when it lifts, the glass springs back (an overshooting ease-out). The
2377
+ pressed item in a group gets a highlight capsule of its own; buttons on
2378
+ glass do not dim as plain buttons do. The swell is the `scale` property,
2379
+ apart from `transform`, which the renderer sets (offsets, rotations) and
2380
+ the Animator tweens. */
2381
+ .sb-toolbar[data-sb-placement="leading"],
2382
+ .sb-toolbar[data-sb-placement="trailing"],
2383
+ .sb-bottom-glass,
2384
+ .sb-navbar-back,
2385
+ .sb-navbar-more,
2386
+ .sb-navsplit-toggle,
2387
+ .sb-button[data-sb-style="glass"],
2388
+ [data-sb-glass] {
2389
+ --sb-press-scale: 1.08;
2390
+ transition:
2391
+ scale 0.45s cubic-bezier(0.34, 1.56, 0.64, 1),
2392
+ background-color 0.3s ease,
2393
+ opacity 0.1s ease;
2394
+ }
2395
+
2396
+ /* A capsule of several items swells less: it is wider. */
2397
+ .sb-toolbar[data-sb-placement="leading"],
2398
+ .sb-toolbar[data-sb-placement="trailing"],
2399
+ .sb-bottom-glass {
2400
+ --sb-press-scale: 1.05;
2401
+ }
2402
+
2403
+ [data-sb-pressed] {
2404
+ scale: var(--sb-press-scale);
2405
+ background-color: var(--sb-glass-pressed);
2406
+ transition:
2407
+ scale 0.14s cubic-bezier(0.2, 0.8, 0.2, 1),
2408
+ background-color 0.14s ease;
2409
+ }
2410
+
2411
+ .sb-toolbar > [data-sb-pressed-item]::before {
2412
+ content: "";
2413
+ position: absolute;
2414
+ inset: -11px -10px;
2415
+ border-radius: 9999px;
2416
+ background: var(--sb-glass-selected);
2417
+ pointer-events: none;
2418
+ }
2419
+
2420
+ .sb-toolbar:is([data-sb-placement="leading"], [data-sb-placement="trailing"], [data-sb-placement="bottom"]) :is(.sb-button, .sb-menu):active,
2421
+ .sb-button[data-sb-style="glass"]:active,
2422
+ .sb-navbar-back:active,
2423
+ .sb-navbar-more:active,
2424
+ .sb-navsplit-toggle:active {
2425
+ opacity: 1;
2426
+ }
2427
+
2428
+ @media (prefers-reduced-motion: reduce) {
2429
+ [data-sb-pressed] {
2430
+ scale: none;
2431
+ }
2432
+ }
2433
+
2434
+ /* The exit clone of a bar item (src/animation.ts) is drawn as its toolbar
2435
+ drew it: the bar's symbol size and weight, the label color. */
2436
+ .sb-exit-clone[data-sb-bar-item] {
2437
+ --sb-color-accent: var(--sb-color-primary);
2438
+ }
2439
+
2440
+ .sb-exit-clone[data-sb-bar-item] .sb-image > svg {
2441
+ width: 1.57em;
2442
+ height: 1.57em;
2443
+ stroke-width: 1.85;
2444
+ }
2445
+
2446
+ .sb-exit-clone[data-sb-bar-item="bottom"] .sb-image > svg {
2447
+ stroke-width: 1.5;
2448
+ }
2449
+
2340
2450
  /* A ToolbarSpacer draws nothing (the engine also hides it). */
2341
2451
  .sb-toolbar[data-sb-spacer] {
2342
2452
  display: none;
@@ -2359,6 +2469,62 @@ svg[data-sb-symbol="progress.indicator"] > line:nth-child(8) { animation-delay:
2359
2469
  z-index: 2;
2360
2470
  }
2361
2471
 
2472
+ /* iOS 26 in a tab view: the field sits under the title (engine
2473
+ `searchTop`), drawn as the bar's: a gray capsule, no glass. When the
2474
+ screen's scroll view (the one carrying the large title) scrolls, the
2475
+ field moves with its content and fades under the bar: a scroll-driven
2476
+ animation on that scroll view's timeline, so it keeps step with the
2477
+ scrolling as the title (which is in the scroll view) does. */
2478
+ .sb-navscreen[data-sb-search-top] > .sb-navscreen-body > .sb-navscreen-content > .sb-searchfield {
2479
+ background: var(--sb-color-tertiary-fill);
2480
+ box-shadow: none;
2481
+ backdrop-filter: none;
2482
+ -webkit-backdrop-filter: none;
2483
+ padding: 0 12px;
2484
+ gap: 6px;
2485
+ z-index: 0;
2486
+ }
2487
+
2488
+ .sb-navscreen[data-sb-search-top] > .sb-navscreen-body > .sb-navscreen-content > .sb-searchfield::after {
2489
+ display: none;
2490
+ }
2491
+
2492
+ @supports (animation-timeline: scroll()) {
2493
+ .sb-navscreen[data-sb-search-top] {
2494
+ timeline-scope: --sb-screen-scroll;
2495
+ }
2496
+
2497
+ .sb-navscreen[data-sb-search-top] [data-sb-under-bar]:has(> .sb-navbar-large-title) {
2498
+ scroll-timeline: --sb-screen-scroll y;
2499
+ }
2500
+
2501
+ .sb-navscreen[data-sb-search-top] > .sb-navscreen-body > .sb-navscreen-content > .sb-searchfield {
2502
+ animation:
2503
+ sb-search-follow linear both,
2504
+ sb-search-fade linear both;
2505
+ animation-timeline: --sb-screen-scroll, --sb-screen-scroll;
2506
+ animation-range: 0px 2000px, 0px 44px;
2507
+ }
2508
+ }
2509
+
2510
+ @keyframes sb-search-follow {
2511
+ from {
2512
+ transform: translateY(0);
2513
+ }
2514
+ to {
2515
+ transform: translateY(-2000px);
2516
+ }
2517
+ }
2518
+
2519
+ @keyframes sb-search-fade {
2520
+ from {
2521
+ opacity: 1;
2522
+ }
2523
+ to {
2524
+ opacity: 0;
2525
+ }
2526
+ }
2527
+
2362
2528
  .sb-searchfield > svg {
2363
2529
  display: block;
2364
2530
  width: 17px;
@@ -2466,10 +2632,6 @@ svg[data-sb-symbol="progress.indicator"] > line:nth-child(8) { animation-delay:
2466
2632
  display: none;
2467
2633
  }
2468
2634
 
2469
- .sb-navsplit-toggle:active {
2470
- opacity: 0.3;
2471
- }
2472
-
2473
2635
  .sb-navsplit-toggle > svg {
2474
2636
  width: 22px;
2475
2637
  height: 22px;
@@ -3126,13 +3288,6 @@ svg[data-sb-symbol="progress.indicator"] > line:nth-child(8) { animation-delay:
3126
3288
  display: block;
3127
3289
  }
3128
3290
 
3129
- /* Above a `large` sheet the presenting tree scales down; an alert never does. */
3130
- .screen[data-sb-sheet] > .sb-alert,
3131
- .screen > .sb-alert {
3132
- transform: none;
3133
- transition: none;
3134
- }
3135
-
3136
3291
  .sb-alert-scrim {
3137
3292
  position: absolute;
3138
3293
  inset: 0;
package/src/styles.ts CHANGED
@@ -38,6 +38,9 @@ import {
38
38
 
39
39
  const SEMANTIC_COLOR_VAR: Record<string, string> = {
40
40
  primary: 'var(--sb-color-primary)',
41
+ // `.foreground` (ForegroundStyle): the color where it is drawn, as SwiftUI
42
+ // resolves it (white inside a prominent button, a `.foregroundStyle` above).
43
+ foreground: 'currentColor',
41
44
  secondary: 'var(--sb-color-secondary)',
42
45
  accent: 'var(--sb-color-accent)',
43
46
  systemBackground: 'var(--sb-color-system-background)',
package/src/symbols.ts CHANGED
@@ -366,6 +366,7 @@ import {
366
366
  Target,
367
367
  Tent,
368
368
  Terminal,
369
+ Cpu,
369
370
  TextAlignCenter,
370
371
  TextAlignEnd,
371
372
  TextAlignJustify,
@@ -887,7 +888,8 @@ export const SF_SYMBOLS: Readonly<Record<string, SymbolGlyph>> = {
887
888
  bluetooth: outline(Bluetooth),
888
889
  'battery.100': outline(Battery),
889
890
  power: outline(Power),
890
- 'cpu': outline(Terminal),
891
+ cpu: outline(Cpu),
892
+ 'cpu.fill': knockout(Cpu),
891
893
  'chevron.left.forwardslash.chevron.right': outline(Code),
892
894
  terminal: outline(Terminal),
893
895
  'ant': outline(Bug),
package/src/vite-env.d.ts CHANGED
@@ -24,6 +24,24 @@ interface SBLandingApp {
24
24
 
25
25
  /** The registered apps, in registry order. */
26
26
  declare const __SB_APPS__: readonly SBLandingApp[];
27
+ /** The app the app page runs without `?app=` (`SiteOptions.defaultApp`, node/config.ts). */
28
+ declare const __SB_DEFAULT_APP__: string;
29
+ /** What a wide screen shows: `auto` (the frame) or `device` (the full window, a built single-app site). See plugins/site-layout.ts. */
30
+ declare const __SB_PAGE_MODE__: 'auto' | 'device';
31
+
32
+ /** `window.__SB_SITE__`, what the app page's inline scripts read (`SiteInfo` in plugins/site-layout.ts). */
33
+ interface SBSiteInfo {
34
+ layout: 'app' | 'landing';
35
+ defaultApp: string;
36
+ displayName: string;
37
+ pageMode: 'auto' | 'device';
38
+ }
39
+
40
+ interface Window {
41
+ __SB_SITE__?: SBSiteInfo;
42
+ /** `name -> icon URL` for apps with their own icon (plugins/app-manifests.ts). */
43
+ __SB_APP_ICONS__?: Record<string, string>;
44
+ }
27
45
  /** Commit sha the site was built from (`GITHUB_SHA`, else `git rev-parse --short HEAD`, else "dev"). */
28
46
  declare const __SB_COMMIT__: string;
29
47
  /** Branch the site was built from (`GITHUB_REF_NAME`, else `git rev-parse --abbrev-ref HEAD`, else ""). */