@rdlabo/ionic-theme-ios27 0.1.0-beta.pr154.shac3e35365120e → 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.
Files changed (95) hide show
  1. package/README.md +3 -3
  2. package/dist/css/components/ion-action-sheet.css +1 -1
  3. package/dist/css/components/ion-alert.css +1 -1
  4. package/dist/css/components/ion-button.css +1 -1
  5. package/dist/css/components/ion-card.css +1 -1
  6. package/dist/css/components/ion-fab.css +1 -1
  7. package/dist/css/components/ion-list.css +1 -1
  8. package/dist/css/components/ion-menu.css +1 -1
  9. package/dist/css/components/ion-modal.css +1 -1
  10. package/dist/css/components/ion-popover.css +1 -1
  11. package/dist/css/components/ion-range.css +1 -1
  12. package/dist/css/components/ion-segment.css +1 -1
  13. package/dist/css/components/ion-tabs.css +1 -1
  14. package/dist/css/components/ion-toast.css +1 -1
  15. package/dist/css/components/ion-toolbar.css +1 -1
  16. package/dist/css/default-variables.css +1 -1
  17. package/dist/css/ionic-theme-ios27-dark-always.css +1 -1
  18. package/dist/css/ionic-theme-ios27-dark-class.css +1 -1
  19. package/dist/css/ionic-theme-ios27-dark-system.css +1 -1
  20. package/dist/css/ionic-theme-ios27.css +1 -1
  21. package/dist/css/md-ion-list-inset.css +1 -1
  22. package/dist/css/utils/dark/ion-list.css +1 -0
  23. package/dist/css/utils/group-heading.css +1 -0
  24. package/dist/native/components/searchable-tabs.d.ts.map +1 -1
  25. package/dist/native/components/searchable-tabs.js +9 -1
  26. package/dist/native/definitions.d.ts +4 -0
  27. package/dist/native/definitions.d.ts.map +1 -1
  28. package/dist/native/runtime.d.ts.map +1 -1
  29. package/dist/native/runtime.js +51 -27
  30. package/dist/native/shared/candidate.d.ts.map +1 -1
  31. package/dist/native/shared/candidate.js +17 -0
  32. package/dist/native/shared/crossfade.d.ts +8 -0
  33. package/dist/native/shared/crossfade.d.ts.map +1 -0
  34. package/dist/native/shared/crossfade.js +46 -0
  35. package/dist/native/shared/dom.d.ts.map +1 -1
  36. package/dist/native/shared/dom.js +2 -1
  37. package/dist/popover/animations/ios.enter.d.ts.map +1 -1
  38. package/dist/popover/animations/ios.enter.js +105 -9
  39. package/dist/popover/animations/ios.leave.d.ts.map +1 -1
  40. package/dist/popover/animations/ios.leave.js +15 -1
  41. package/dist/popover/callout-surface.d.ts +2 -0
  42. package/dist/popover/callout-surface.d.ts.map +1 -0
  43. package/dist/popover/callout-surface.js +44 -0
  44. package/dist/sheets-of-glass/index.js +2 -1
  45. package/dist/transition/ios.transition.d.ts.map +1 -1
  46. package/dist/transition/ios.transition.js +0 -223
  47. package/docs/experimental-animation.md +3 -3
  48. package/docs/features.md +7 -2
  49. package/docs/native-ui-shell.md +9 -5
  50. package/docs/special-markup.md +17 -0
  51. package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellButton.swift +7 -2
  52. package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellSearchController.swift +10 -0
  53. package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellSegment.swift +47 -15
  54. package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellTabBar.swift +42 -7
  55. package/ios/Sources/IonicNativeUIShellPlugin/IonicNativeUIShellPlugin.swift +13 -6
  56. package/ios/Sources/IonicNativeUIShellPlugin/Shared/ShellCrossfade.swift +35 -0
  57. package/ios/Sources/IonicNativeUIShellPlugin/Shared/ShellSnapshot.swift +6 -1
  58. package/ios/Tests/IonicNativeUIShellPluginTests/ShellSnapshotTests.swift +59 -0
  59. package/package.json +1 -1
  60. package/src/native/components/searchable-tabs.ts +12 -1
  61. package/src/native/definitions.ts +4 -0
  62. package/src/native/runtime.ts +54 -31
  63. package/src/native/shared/candidate.ts +19 -0
  64. package/src/native/shared/crossfade.ts +49 -0
  65. package/src/native/shared/dom.ts +4 -1
  66. package/src/popover/animations/ios.enter.ts +123 -10
  67. package/src/popover/animations/ios.leave.ts +15 -1
  68. package/src/popover/callout-surface.ts +54 -0
  69. package/src/sheets-of-glass/index.ts +2 -2
  70. package/src/styles/components/ion-action-sheet.scss +35 -17
  71. package/src/styles/components/ion-alert.scss +51 -31
  72. package/src/styles/components/ion-button.scss +4 -7
  73. package/src/styles/components/ion-card.scss +64 -3
  74. package/src/styles/components/ion-fab.scss +3 -5
  75. package/src/styles/components/ion-list.scss +14 -1
  76. package/src/styles/components/ion-menu.scss +19 -7
  77. package/src/styles/components/ion-modal.scss +63 -14
  78. package/src/styles/components/ion-popover.scss +52 -9
  79. package/src/styles/components/ion-range.scss +49 -50
  80. package/src/styles/components/ion-segment.scss +15 -16
  81. package/src/styles/components/ion-tabs.scss +73 -13
  82. package/src/styles/components/ion-toast.scss +16 -25
  83. package/src/styles/components/ion-toolbar.scss +17 -1
  84. package/src/styles/default-variables.scss +4 -0
  85. package/src/styles/utils/_glass.scss +96 -0
  86. package/src/styles/utils/api.scss +46 -4
  87. package/src/styles/utils/dark/ion-button.scss +13 -0
  88. package/src/styles/utils/dark/ion-fab.scss +9 -0
  89. package/src/styles/utils/dark/ion-list.scss +7 -0
  90. package/src/styles/utils/dark/ion-segment.scss +8 -3
  91. package/src/styles/utils/dark/ion-tabs.scss +20 -0
  92. package/src/styles/utils/group-heading.scss +5 -0
  93. package/src/styles/utils/structured-list.scss +68 -15
  94. package/src/styles/utils/theme-dark.scss +23 -0
  95. package/src/transition/ios.transition.ts +0 -487
@@ -1,6 +1,8 @@
1
1
  @use '../utils/structured-list';
2
2
 
3
3
  ion-list.ios:not(.ios-theme-disabled, .ios26-disabled) {
4
+ --ion-border-color: var(--ion-color-step-100, var(--ion-background-color-step-100, #e8e8e8));
5
+
4
6
  @include structured-list.support-text;
5
7
  ion-item {
6
8
  --background-hover-opacity: 0;
@@ -9,7 +11,7 @@ ion-list.ios:not(.ios-theme-disabled, .ios26-disabled) {
9
11
  }
10
12
 
11
13
  ion-list.list-inset.ios:not(.ios-theme-disabled, .ios26-disabled) {
12
- @include structured-list.layout(20px, 24px);
14
+ @include structured-list.layout(20px, 28px);
13
15
 
14
16
  ion-radio-group,
15
17
  ion-reorder-group {
@@ -31,6 +33,13 @@ ion-list.list-inset.ios:not(.ios-theme-disabled, .ios26-disabled) {
31
33
  }
32
34
 
33
35
  --min-height: 52px;
36
+ font-size: 1rem;
37
+ line-height: 1.29rem;
38
+
39
+ > ion-label:not([slot]) {
40
+ font-size: inherit;
41
+ line-height: inherit;
42
+ }
34
43
 
35
44
  & > ion-input[labelplacement='floating'] {
36
45
  transition: transform 200ms ease;
@@ -42,3 +51,7 @@ ion-list.list-inset.ios:not(.ios-theme-disabled, .ios26-disabled) {
42
51
  }
43
52
  }
44
53
  }
54
+
55
+ ion-list.ios:not(.ios-theme-disabled, .ios26-disabled) {
56
+ @include structured-list.colors(0.75, 0.38);
57
+ }
@@ -1,5 +1,3 @@
1
- @use '../utils/api';
2
-
3
1
  ion-split-pane.ios:not(.ios-theme-disabled, .ios26-disabled) {
4
2
  --side-max-width: var(--ios-theme-menu-width, var(--ios26-menu-width));
5
3
  }
@@ -82,7 +80,8 @@ ion-split-pane.ios:not(.ios-theme-disabled, .ios26-disabled).split-pane-visible:
82
80
 
83
81
  ion-menu.ios:not(.ios-theme-disabled, .ios26-disabled) {
84
82
  --border: none;
85
- --background: transparent;
83
+ // Keep the surface nearly opaque while page transitions remain visible behind it.
84
+ --background: rgba(var(--ios-theme-menu-background-rgb), var(--ios-theme-menu-background-opacity, 0.96));
86
85
  --max-width: var(--ios-theme-menu-width, var(--ios26-menu-width));
87
86
  position: absolute;
88
87
  top: 0;
@@ -99,17 +98,30 @@ ion-menu.ios:not(.ios-theme-disabled, .ios26-disabled) {
99
98
  display: none;
100
99
  }
101
100
 
101
+ &::part(container) {
102
+ border-radius: 0;
103
+ box-shadow: none;
104
+ -webkit-backdrop-filter: blur(3px);
105
+ backdrop-filter: blur(3px);
106
+ }
107
+
108
+ ion-list:not(.ios-theme-disabled, .ios26-disabled, .ion-color) {
109
+ background: transparent;
110
+ }
111
+
112
+ ion-item:not(.ios-theme-disabled, .ios26-disabled, .ion-color) {
113
+ --background: transparent;
114
+ }
115
+
102
116
  ion-content {
103
117
  --background: transparent;
104
118
  --ios26-content-box-shadow-rgb: transparent;
105
119
  --ios-theme-content-box-shadow-rgb: var(--ios26-content-box-shadow-rgb);
106
120
 
107
121
  &::part(scroll) {
108
- @include api.glass-background;
109
122
  margin: calc(var(--ios-theme-floating-safe-area-top, var(--ios26-floating-safe-area-top)) + 6px)
110
- calc(16px + var(--ion-safe-area-right)) var(--ios-theme-floating-safe-area-bottom, var(--ios26-floating-safe-area-bottom))
111
- calc(12px + var(--ion-safe-area-left));
112
- border-radius: 24px;
123
+ calc(16px + var(--ion-safe-area-right, 0px)) var(--ios-theme-floating-safe-area-bottom, var(--ios26-floating-safe-area-bottom))
124
+ calc(12px + var(--ion-safe-area-left, 0px));
113
125
  }
114
126
  }
115
127
  }
@@ -1,10 +1,21 @@
1
1
  @use '../utils/api';
2
2
 
3
+ @mixin sheet-glass {
4
+ &::part(content) {
5
+ @include api.glass-background($opacity: 1, $include-background: false);
6
+ box-sizing: border-box;
7
+ }
8
+ }
9
+
3
10
  ion-modal.ios:not(.ios-theme-disabled, .ios26-disabled) {
4
11
  --backdrop-opacity: 0.2;
5
12
 
6
13
  &::part(handle) {
7
- width: 80px;
14
+ top: 6px;
15
+ width: 60px;
16
+ height: 4px;
17
+ border-radius: 2px;
18
+ background: var(--ion-background-color-step-250, var(--ion-color-step-250, #c8c7cc));
8
19
  }
9
20
 
10
21
  ion-toolbar {
@@ -12,19 +23,33 @@ ion-modal.ios:not(.ios-theme-disabled, .ios26-disabled) {
12
23
  padding-left: var(--ion-safe-area-left);
13
24
  }
14
25
 
26
+ &.modal-card,
15
27
  &.modal-sheet {
16
- ion-toolbar {
17
- padding-right: calc(var(--ion-safe-area-right) + 4px);
18
- padding-left: calc(var(--ion-safe-area-left) + 4px);
19
- }
28
+ --border-radius: 32px;
20
29
  }
21
30
 
22
- &.modal-card {
23
- /**
24
- * IonicでCardModalのborder-radiusはハードコーディングされています。
25
- * また、iOS27のドキュメントで見つからないため、サポートは行いません。
26
- * https://github.com/ionic-team/ionic-framework/blob/3b80473f2fd5ad4da5a9f5d66f783a69909c8965/core/src/components/modal/animations/ios.enter.ts#L82-L83
27
- */
31
+ // Match the iPad form and page presentations without changing phone fullscreen modals.
32
+ @media (min-width: 768px) and (min-height: 600px) {
33
+ --border-radius: 32px;
34
+ --width: 580px;
35
+ --height: min(660px, calc(100% - 64px));
36
+ --box-shadow: 0 24px 64px color-mix(in srgb, var(--ion-box-shadow-color, #000) 18%, transparent);
37
+
38
+ &:not(.modal-card, .modal-sheet)::part(content) {
39
+ // Keep translucent headers from sampling the shadow outside the rounded surface.
40
+ clip-path: inset(0 round var(--border-radius));
41
+ }
42
+
43
+ &.modal-card {
44
+ --width: min(936px, calc(100% - 40px));
45
+ --max-width: 936px;
46
+ --height: calc(100% - max(32px, var(--ion-safe-area-top, 0px)) * 2);
47
+ }
48
+
49
+ &.modal-sheet {
50
+ --width: min(936px, calc(100vw - 40px));
51
+ --height: calc(100% - max(32px, var(--ion-modal-offset-top, 0px)));
52
+ }
28
53
  }
29
54
 
30
55
  &:has(.ion-delegate-host):has(ion-datetime) {
@@ -33,10 +58,34 @@ ion-modal.ios:not(.ios-theme-disabled, .ios26-disabled) {
33
58
  }
34
59
 
35
60
  &.modal-sheet {
36
- --border-radius: 30px;
37
61
  --background: rgba(var(--ios-theme-glass-background-rgb, var(--ios26-glass-background-rgb)), 1);
38
- &::part(content) {
39
- @include api.glass-background($opacity: 1, $include-background: false);
62
+ @media (max-width: 767px), (max-height: 599px) {
63
+ @include sheet-glass;
64
+ }
65
+ }
66
+
67
+ @media (min-width: 768px) and (min-height: 600px) {
68
+ &.modal-sheet:not(.modal-no-expand-scroll) {
69
+ @include sheet-glass;
70
+ }
71
+
72
+ // Ionic sizes the page at each breakpoint when expandToScroll is false.
73
+ &.modal-sheet.modal-no-expand-scroll {
74
+ --background: transparent;
75
+ --box-shadow: none;
76
+ --border-width: 0;
77
+ --overflow: visible;
78
+
79
+ &::part(content) {
80
+ bottom: 20px;
81
+ }
82
+
83
+ > .ion-page {
84
+ border-radius: var(--border-radius);
85
+ overflow: hidden;
86
+ background: var(--ion-background-color, #fff);
87
+ box-shadow: 0 24px 64px color-mix(in srgb, var(--ion-box-shadow-color, #000) 18%, transparent);
88
+ }
40
89
  }
41
90
  }
42
91
  }
@@ -1,22 +1,65 @@
1
1
  @use '../utils/api';
2
2
 
3
3
  ion-popover.ios:not(.ios-theme-disabled, .ios26-disabled) {
4
- --backdrop-opacity: 0.2;
5
- --background: rgba(var(--ios-theme-glass-background-rgb, var(--ios26-glass-background-rgb)), 0.72);
4
+ @include api.glass-overlay-variables;
5
+ --background: rgba(var(--ios-theme-glass-background-rgb, var(--ios26-glass-background-rgb)), 0.42);
6
+ --backdrop-opacity: 0;
6
7
  --box-shadow:
7
- inset 0 0 8px 0 rgba(var(--ios-theme-glass-box-shadow-color-rgb, var(--ios26-glass-box-shadow-color-rgb)), 0.2),
8
- 0 0 10px 0 rgba(var(--ios-theme-glass-box-shadow-color-rgb, var(--ios26-glass-box-shadow-color-rgb)), 0.82);
8
+ inset 0 0.5px 1px rgba(var(--ios-theme-glass-border-color-rgb, var(--ios26-glass-border-color-rgb)), 0.6),
9
+ 0 4px 16px color-mix(in srgb, var(--ion-box-shadow-color, #000) 12%, transparent);
10
+
9
11
  &::part(arrow) {
10
- display: none;
11
- ::after {
12
- //@include api.glass-background;
12
+ width: 34px;
13
+ height: 14px;
14
+
15
+ &::after {
16
+ width: 100%;
17
+ height: 100%;
18
+ top: 0;
19
+ inset-inline-start: 0;
20
+ border-radius: 0;
21
+ transform: none;
22
+ // Rounded tip and concave shoulders join the popover's straight edge.
23
+ clip-path: path('M 0 14 L 0 13 C 3 13 4.5 12 6.5 10 L 14.5 1.5 C 16 -0.5 18 -0.5 19.5 1.5 L 27.5 10 C 29.5 12 31 13 34 13 L 34 14 Z');
24
+ backdrop-filter: blur(12px) saturate(115%) brightness(1.35);
25
+ -webkit-backdrop-filter: blur(12px) saturate(115%) brightness(1.35);
13
26
  }
14
27
  }
28
+
15
29
  &::part(content) {
16
- @include api.glass-background($include-background: false, $include-box-shadow: false);
17
- border-radius: 24px;
30
+ @include api.glass-overlay-surface;
31
+ backdrop-filter: blur(12px) saturate(115%) brightness(1.35);
32
+ -webkit-backdrop-filter: blur(12px) saturate(115%) brightness(1.35);
33
+ border-radius: 34px;
18
34
  padding: 0;
19
35
  }
36
+
37
+ &.ios-theme-callout::part(content) {
38
+ background: transparent;
39
+ border-color: transparent;
40
+ box-shadow: none;
41
+ backdrop-filter: none;
42
+ -webkit-backdrop-filter: none;
43
+ }
44
+
45
+ &.ios-theme-callout::part(arrow)::after {
46
+ display: none;
47
+ }
48
+
49
+ &::part(callout-glass) {
50
+ background: var(--background);
51
+ backdrop-filter: blur(12px) saturate(115%) brightness(1.35);
52
+ -webkit-backdrop-filter: blur(12px) saturate(115%) brightness(1.35);
53
+ }
54
+
55
+ &::part(callout-border) {
56
+ background: color-mix(in srgb, var(--border-color) 60%, transparent);
57
+ }
58
+
59
+ &::part(callout-shadow) {
60
+ background: color-mix(in srgb, var(--ion-box-shadow-color, #000) 8%, transparent);
61
+ }
62
+
20
63
  ion-select-popover {
21
64
  ion-list {
22
65
  background: transparent;
@@ -1,14 +1,29 @@
1
- @use '../utils/api';
2
-
3
- @mixin scaled-transform($translate-x) {
4
- transform: scale(1.56, 1.47) translateX(calc(#{$translate-x} * -0.1)) translateZ(0);
5
- -webkit-transform: scale(1.56, 1.47) translateX(calc(#{$translate-x} * -0.1)) translateZ(0);
1
+ @mixin pressed-knob($offset: 0px) {
2
+ // iPadOS 27: 38 × 24 at rest, approximately 58 × 38 while tracking.
3
+ transform: translateX($offset) scale(1.53, 1.58);
4
+ background: linear-gradient(
5
+ 180deg,
6
+ rgba(var(--ion-text-color-rgb, 0, 0, 0), 0.04),
7
+ rgba(var(--ion-text-color-rgb, 0, 0, 0), 0.08) 35%,
8
+ rgba(255, 255, 255, 0.12) 65%,
9
+ rgba(255, 255, 255, 0.24)
10
+ );
11
+ box-shadow:
12
+ inset 0 0 0 0.65px rgba(var(--ion-text-color-rgb, 0, 0, 0), 0.28),
13
+ inset 0 1px 1px rgba(255, 255, 255, 0.8),
14
+ 0 4px 6px rgba(0, 0, 0, 0.14);
6
15
  }
7
16
 
8
17
  ion-range.ios:not(.ios-theme-disabled, .ios26-disabled) {
9
- --knob-size: 20px;
18
+ --knob-size: 24px;
10
19
  --knob-width: 38px;
11
20
  --knob-border-radius: 24px;
21
+ --bar-height: 6px;
22
+ --bar-border-radius: 3px;
23
+
24
+ &.ion-color {
25
+ --bar-background-active: var(--ion-color-base);
26
+ }
12
27
 
13
28
  &.range-label-placement-start::part(label) {
14
29
  margin-inline: 0 24px;
@@ -27,64 +42,48 @@ ion-range.ios:not(.ios-theme-disabled, .ios26-disabled) {
27
42
  }
28
43
 
29
44
  &::part(knob) {
45
+ overflow: hidden;
30
46
  width: var(--knob-width);
31
- margin-inline-start: -8px;
47
+ margin-inline-start: calc((var(--knob-size) - var(--knob-width)) / 2);
32
48
  transition:
33
- transform 300ms ease,
34
- margin 300ms ease,
49
+ transform 200ms ease,
35
50
  box-shadow 200ms ease;
36
51
  }
37
52
 
38
- &:not(.range-dual-knobs).range-pressed {
39
- --knob-background: rgba(var(--ios-theme-glass-background-rgb, var(--ios26-glass-background-rgb)), 0.1);
40
- --knob-box-shadow: 0 0.5px 4px rgba(0, 0, 0, 0.12), 0 6px 4px rgba(0, 0, 0, 0.05), inset 0.4px 0.4px 1px 0 var(--bar-background-active);
41
-
42
- &::part(knob) {
43
- @include api.glass-background(0.1, 0, 120%, $include-background: false, $include-box-shadow: false, $include-border: false);
44
- @include scaled-transform(0px);
45
- }
53
+ &:not(.range-dual-knobs).range-pressed::part(knob) {
54
+ @include pressed-knob;
55
+ }
46
56
 
47
- &.range-value-min {
48
- --knob-box-shadow: 0 0.5px 4px rgba(0, 0, 0, 0.12), 0 6px 4px rgba(0, 0, 0, 0.05);
57
+ // Ionic does not expose each dual thumb's endpoint state through parts.
58
+ // Keep its width fixed so either thumb can be pressed at coincident endpoints.
59
+ &.range-dual-knobs.range-pressed-a::part(knob-a),
60
+ &.range-dual-knobs.range-pressed-b::part(knob-b) {
61
+ @include pressed-knob;
62
+ transform: scaleY(1.58);
63
+ }
49
64
 
50
- &::part(knob) {
51
- @include scaled-transform(calc(var(--knob-width) * -2));
52
- }
53
- }
54
- &.range-value-max {
55
- --knob-box-shadow:
56
- 0 0.5px 4px rgba(0, 0, 0, 0.12), 0 6px 4px rgba(0, 0, 0, 0.05), inset 0.4px 0.4px 1px 0.2px var(--bar-background-active);
65
+ // Keep the enlarged thumb inside the same outer edge at either endpoint.
66
+ &:not(.range-dual-knobs).range-pressed.range-value-min::part(knob) {
67
+ @include pressed-knob(calc(var(--knob-width) * 0.53 / 2));
68
+ }
57
69
 
58
- &::part(knob) {
59
- @include scaled-transform(calc(var(--knob-width) * 2));
60
- }
61
- }
70
+ &:not(.range-dual-knobs).range-pressed.range-value-max::part(knob) {
71
+ @include pressed-knob(calc(var(--knob-width) * -0.53 / 2));
62
72
  }
63
73
 
64
- &.range-dual-knobs.range-pressed {
65
- &.range-pressed-a::part(knob-a),
66
- &.range-pressed-b::part(knob-b) {
67
- @include api.glass-background(0.1, 0, 120%, $include-border: false);
68
- @include scaled-transform(0px);
69
- box-shadow:
70
- 0 0.5px 4px rgba(0, 0, 0, 0.12),
71
- 0 6px 4px rgba(0, 0, 0, 0.05),
72
- inset 0.4px 0.4px 1px 0 var(--bar-background-active);
74
+ &:dir(rtl) {
75
+ &:not(.range-dual-knobs).range-pressed.range-value-min::part(knob) {
76
+ @include pressed-knob(calc(var(--knob-width) * -0.53 / 2));
73
77
  }
74
78
 
75
- &.range-value-min.range-pressed-lower::part(knob-lower) {
76
- @include scaled-transform(calc(var(--knob-width) * -2));
77
- box-shadow:
78
- 0 0.5px 4px rgba(0, 0, 0, 0.12),
79
- 0 6px 4px rgba(0, 0, 0, 0.05);
79
+ &:not(.range-dual-knobs).range-pressed.range-value-max::part(knob) {
80
+ @include pressed-knob(calc(var(--knob-width) * 0.53 / 2));
80
81
  }
82
+ }
81
83
 
82
- &.range-value-max.range-pressed-upper::part(knob-upper) {
83
- @include scaled-transform(calc(var(--knob-width) * 2));
84
- box-shadow:
85
- 0 0.5px 4px rgba(0, 0, 0, 0.12),
86
- 0 6px 4px rgba(0, 0, 0, 0.05),
87
- inset 0.4px 0.4px 1px 0.2px var(--bar-background-active);
84
+ @media (prefers-reduced-motion: reduce) {
85
+ &::part(knob) {
86
+ transition: none;
88
87
  }
89
88
  }
90
89
  }
@@ -14,18 +14,8 @@ ion-segment.ios:not(.ios-theme-disabled, .ios26-disabled) {
14
14
 
15
15
  // UIKit uses a larger control in navigation bars, but not in content.
16
16
  &.in-toolbar:not(.segment-expand) {
17
- @include api.glass-background($include-border: false);
18
17
  --border-radius: 24px;
19
18
  min-height: 48px;
20
- // Draw the shared reflective rim without adding to the control's measured bounds.
21
- &::before {
22
- @include api.glass-background($include-background: false, $include-box-shadow: false, $blur: 0, $saturate: 100%);
23
- content: '';
24
- position: absolute;
25
- inset: 0;
26
- border-radius: inherit;
27
- pointer-events: none;
28
- }
29
19
  // UIKit's container spring: grow on press, then briefly shrink below rest on release.
30
20
  transition: transform 517ms
31
21
  linear(
@@ -67,9 +57,9 @@ ion-segment.ios:not(.ios-theme-disabled, .ios26-disabled) {
67
57
  }
68
58
 
69
59
  ion-segment-button {
70
- min-height: 40px;
71
- margin: 4px 2px;
72
- font-size: 15px;
60
+ min-height: 44px;
61
+ margin: 2px 0;
62
+ font-size: 0.94rem;
73
63
  }
74
64
  }
75
65
 
@@ -161,9 +151,14 @@ ion-segment-button.ios:not(.ios-theme-disabled, .ios26-disabled) {
161
151
  min-width: 65px;
162
152
  min-height: 28px;
163
153
  margin: 2px 0;
164
- font-size: 13px;
154
+ font-size: 0.81rem;
155
+ font-weight: 500;
165
156
  line-height: normal;
166
157
 
158
+ &.segment-button-disabled {
159
+ opacity: 0.2;
160
+ }
161
+
167
162
  &::part(indicator-background) {
168
163
  transition:
169
164
  var(--indicator-transition),
@@ -180,8 +175,12 @@ ion-segment-button.ios:not(.ios-theme-disabled, .ios26-disabled) {
180
175
  }
181
176
 
182
177
  // Public customization properties remain overridable by ordinary app selectors.
183
- :where(ion-segment.ios:not(.ios-theme-disabled, .ios26-disabled).in-toolbar:not(.segment-expand) ion-segment-button) {
184
- --indicator-color: rgba(var(--ion-text-color-rgb, 0, 0, 0), 0.06);
178
+ // The resting track is a flat system fill; only the moving lens has a glass rim.
179
+ :where(ion-segment.ios:not(.ios-theme-disabled, .ios26-disabled)) {
180
+ --background: rgba(var(--ion-color-medium-rgb, 99, 100, 105), 0.11);
181
+ }
182
+ :where(ion-segment.ios:not(.ios-theme-disabled, .ios26-disabled) ion-segment-button) {
183
+ --indicator-color: var(--ion-background-color, #fff);
185
184
  }
186
185
  :where(ion-segment.ios:not(.ios-theme-disabled, .ios26-disabled).ion-color ion-segment-button.in-segment-color) {
187
186
  --indicator-color: var(--ion-color-base);
@@ -1,11 +1,30 @@
1
1
  @use '../utils/api';
2
2
 
3
3
  ion-tab-bar.ios:not(.ios-theme-disabled, .ios26-disabled) {
4
- @include api.glass-background;
4
+ // Ionic forces content-box on the host. Keep the Glass border in a
5
+ // separate layer so its physical-pixel rounding cannot resize the bar.
6
+ border: 0;
7
+ background: transparent;
8
+ contain: size layout style;
5
9
  z-index: 2;
10
+ --background: rgba(var(--ios-theme-glass-background-rgb, var(--ios26-glass-background-rgb)), 0.53);
6
11
 
7
- border-radius: 40px;
8
- --color: rgb(var(--ion-text-color-rgb, 0, 0, 0));
12
+ &::before {
13
+ @include api.glass-control-background($include-background: false);
14
+ background-color: var(--background);
15
+ backdrop-filter: blur(6px) saturate(180%) brightness(1.05);
16
+ content: '';
17
+ position: absolute;
18
+ inset: 0;
19
+ box-sizing: border-box;
20
+ border-radius: inherit;
21
+ pointer-events: none;
22
+ z-index: -1;
23
+ }
24
+
25
+ border-radius: 31px;
26
+ --ios-theme-tab-overlap: 20px;
27
+ --color: var(--ion-text-color-step-50, #0d0d0d);
9
28
 
10
29
  &[slot='top'] {
11
30
  position: absolute;
@@ -42,17 +61,41 @@ ion-tab-bar.ios:not(.ios-theme-disabled, .ios26-disabled) {
42
61
  /**
43
62
  * 100% - margin-left - margin-right - right-fab - margin-left-fab
44
63
  */
45
- width: calc(100% - calc(18px + var(--ion-safe-area-left, 0px)) - calc(18px + var(--ion-safe-area-right, 0px)) - 60px - 12px);
46
- max-width: 474px;
47
- min-height: 56px;
64
+ width: calc(100% - calc(18px + var(--ion-safe-area-left, 0px)) - calc(18px + var(--ion-safe-area-right, 0px)) - 75px);
65
+ max-width: 471px;
66
+ height: 54px;
67
+ min-height: 54px;
48
68
  &:has(> ion-tab-button:nth-child(2):last-child) {
49
- width: 184px; // 190px including padding and border
69
+ width: 180px; // 188px including the 4px inset on each side.
50
70
  }
51
71
  &:has(:nth-child(5)) {
52
- width: calc(100% - calc(18px + var(--ion-safe-area-left, 0px)) - calc(18px + var(--ion-safe-area-right, 0px)));
53
- max-width: 546px;
72
+ width: calc(100% - calc(18px + var(--ion-safe-area-left, 0px)) - calc(18px + var(--ion-safe-area-right, 0px)) - 3px);
73
+ max-width: 543px;
74
+ }
75
+ padding: 4px;
76
+
77
+ > ion-tab-button:not(.ion-cloned-element) ~ ion-tab-button:not(.ion-cloned-element) {
78
+ margin-inline-start: calc(-1 * var(--ios-theme-tab-overlap));
79
+ }
80
+
81
+ &:not(:has(> ion-tab-button:nth-child(4 of ion-tab-button:not(.ion-cloned-element)))) {
82
+ --ios-theme-tab-overlap: 8px;
83
+ }
84
+
85
+ // iPad's stacked UIKit tabs use a compact platter instead of filling the
86
+ // available width. Keep the phone sizing and constrain only roomy layouts.
87
+ @media (min-width: 768px) {
88
+ --ios-theme-tab-overlap: 16px;
89
+ @each $count, $width in (2: 188px, 3: 274px, 4: 336px, 5: 414px) {
90
+ &:has(
91
+ > ion-tab-button:nth-child(#{$count} of ion-tab-button:not(.ion-cloned-element)):nth-last-child(
92
+ 1 of ion-tab-button:not(.ion-cloned-element)
93
+ )
94
+ ) {
95
+ max-width: $width - 8px;
96
+ }
97
+ }
54
98
  }
55
- padding: 2px 2px;
56
99
 
57
100
  transition: transform var(--ios-theme-activated-transition-duration, var(--ios26-activated-transition-duration)) ease-out;
58
101
 
@@ -118,10 +161,28 @@ ion-tab-bar.ios:not(.ios-theme-disabled, .ios26-disabled) {
118
161
 
119
162
  ion-tab-button.ios:not(.ios-theme-disabled, .ios26-disabled) {
120
163
  transform-origin: center 65%;
164
+ font-size: 0.59rem;
165
+ border-radius: 27px;
121
166
 
122
167
  ion-icon {
123
168
  font-size: 26px;
124
169
  }
170
+ &.tab-layout-icon-top.tab-has-icon.tab-has-label {
171
+ --padding-top: 7px;
172
+ --padding-bottom: 7px;
173
+ justify-content: flex-start;
174
+
175
+ ion-icon {
176
+ flex-shrink: 0;
177
+ margin-block: 0 2px;
178
+ }
179
+ ion-label {
180
+ margin: 0;
181
+ font-size: inherit;
182
+ line-height: 1.2;
183
+ min-height: 0;
184
+ }
185
+ }
125
186
  ion-label,
126
187
  ion-icon {
127
188
  transition:
@@ -129,12 +190,11 @@ ion-tab-button.ios:not(.ios-theme-disabled, .ios26-disabled) {
129
190
  color var(--ios-theme-activated-transition-duration, var(--ios26-activated-transition-duration)) ease;
130
191
  }
131
192
  background: rgba(var(--ios-theme-glass-background-rgb, var(--ios26-glass-background-rgb)), 0);
132
- height: auto;
193
+ height: 54px;
133
194
  transition: transform var(--ios-theme-activated-transition-duration, var(--ios26-activated-transition-duration)) ease;
134
195
  &::part(native) {
135
196
  overflow: visible;
136
- min-height: 56px;
137
- border-radius: 32px;
197
+ min-height: 54px;
138
198
  }
139
199
 
140
200
  &.ion-activated {
@@ -1,36 +1,27 @@
1
1
  @use '../utils/api';
2
2
 
3
3
  ion-toast.ios:not(.ios-theme-disabled, .ios26-disabled) {
4
- --background: rgba(var(--ios-theme-glass-background-rgb, var(--ios26-glass-background-rgb)), 0.72);
5
- --border-radius: 24px;
6
- --border-style: solid;
7
- --border-width: 0.5px;
8
- --box-shadow:
9
- inset 0 0 8px 0 rgba(var(--ios-theme-glass-box-shadow-color-rgb, var(--ios26-glass-box-shadow-color-rgb)), 0.2),
10
- 0 0 10px 0 rgba(var(--ios-theme-glass-box-shadow-color-rgb, var(--ios26-glass-box-shadow-color-rgb)), 0.82);
4
+ @include api.glass-overlay-variables($shadow-opacity: 0.06);
5
+ --border-radius: 28px;
6
+ --max-width: 560px;
11
7
  --start: 16px;
12
8
  --end: 16px;
9
+ font-size: 0.94rem;
13
10
 
14
11
  &::part(wrapper) {
15
- @include api.glass-background($include-background: false, $include-box-shadow: false, $include-border: false);
16
- border-top-color: rgba(var(--ios-theme-glass-border-color-rgb, var(--ios26-glass-border-color-rgb)), 1);
17
- border-right-color: rgba(var(--ios-theme-glass-border-color-rgb, var(--ios26-glass-border-color-rgb)), 0.8);
18
- border-bottom-color: rgba(var(--ios-theme-glass-border-color-rgb, var(--ios26-glass-border-color-rgb)), 1);
19
- border-left-color: rgba(var(--ios-theme-glass-border-color-rgb, var(--ios26-glass-border-color-rgb)), 0.6);
12
+ @include api.glass-overlay-surface($include-background: false, $include-frame: false);
20
13
  }
21
14
 
22
- &.ion-color {
23
- &::part(wrapper) {
24
- @include api.glass-background(
25
- $background-rgb: var(--ion-color-base-rgb),
26
- $border-color-rgb: var(--ion-color-base-rgb),
27
- $include-box-shadow: false,
28
- $include-border: false
29
- );
30
- border-top-color: rgba(var(--ion-color-base-rgb), 1);
31
- border-right-color: rgba(var(--ion-color-base-rgb), 0.8);
32
- border-bottom-color: rgba(var(--ion-color-base-rgb), 1);
33
- border-left-color: rgba(var(--ion-color-base-rgb), 0.6);
34
- }
15
+ &::part(header) {
16
+ font-size: 0.94rem;
17
+ font-weight: 600;
18
+ }
19
+
20
+ &::part(message) {
21
+ line-height: 1.33;
22
+ }
23
+
24
+ &.ion-color::part(wrapper) {
25
+ background: rgba(var(--ion-color-base-rgb), 0.87);
35
26
  }
36
27
  }
@@ -13,7 +13,23 @@ ion-header.ios:not(.ios-theme-disabled, .ios26-disabled) ion-toolbar:not(.ios-th
13
13
  ion-modal.ios:not(.ios-theme-disabled, .ios26-disabled).modal-card,
14
14
  ion-modal.ios:not(.ios-theme-disabled, .ios26-disabled).modal-sheet {
15
15
  ion-header.ios:not(.ios-theme-disabled, .ios26-disabled) ion-toolbar.ios:not(.ios-theme-disabled, .ios26-disabled):first-of-type {
16
- padding-top: 6px;
16
+ padding-top: 0;
17
+ padding-bottom: 0;
18
+ --min-height: 64px;
19
+ --padding-start: 3px;
20
+ --padding-end: 3px;
21
+ }
22
+ }
23
+
24
+ @media (min-width: 768px) and (min-height: 600px) {
25
+ ion-modal.ios:not(.ios-theme-disabled, .ios26-disabled) {
26
+ ion-header.ios:not(.ios-theme-disabled, .ios26-disabled) ion-toolbar.ios:not(.ios-theme-disabled, .ios26-disabled):first-of-type {
27
+ padding-top: 0;
28
+ padding-bottom: 0;
29
+ --min-height: 64px;
30
+ --padding-start: 3px;
31
+ --padding-end: 3px;
32
+ }
17
33
  }
18
34
  }
19
35