@rdlabo/ionic-theme-ios26 0.3.3 → 0.3.5

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 (65) hide show
  1. package/README.md +29 -18
  2. package/dist/css/components/ion-segment.css +1 -1
  3. package/dist/css/components/ion-tabs.css +1 -1
  4. package/dist/css/ionic-theme-ios26.css +1 -1
  5. package/dist/css/md-remove-ios-class-effect.css +1 -1
  6. package/dist/gestures/gestures.d.ts +3 -0
  7. package/dist/gestures/gestures.d.ts.map +1 -0
  8. package/dist/gestures/gestures.js +240 -0
  9. package/dist/gestures/index.d.ts +3 -0
  10. package/dist/gestures/index.d.ts.map +1 -0
  11. package/dist/gestures/index.js +240 -0
  12. package/dist/gestures/interfaces.d.ts +10 -0
  13. package/dist/gestures/interfaces.d.ts.map +1 -0
  14. package/dist/gestures/interfaces.js +1 -0
  15. package/dist/gestures/utils.d.ts +3 -0
  16. package/dist/gestures/utils.d.ts.map +1 -0
  17. package/dist/gestures/utils.js +24 -0
  18. package/dist/index.d.ts +5 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +18 -0
  21. package/dist/utils.d.ts +3 -0
  22. package/dist/utils.d.ts.map +1 -0
  23. package/dist/utils.js +24 -0
  24. package/package.json +17 -4
  25. package/src/gestures/index.ts +290 -0
  26. package/src/gestures/interfaces.ts +10 -0
  27. package/src/gestures/utils.ts +28 -0
  28. package/src/index.ts +21 -0
  29. package/src/styles/components/ion-segment.scss +106 -0
  30. package/src/styles/components/ion-tabs.scss +117 -0
  31. package/src/{md-remove-ios-class-effect.scss → styles/md-remove-ios-class-effect.scss} +2 -2
  32. package/src/components/ion-segment.scss +0 -64
  33. package/src/components/ion-tabs.scss +0 -86
  34. /package/src/{components → styles/components}/ion-action-sheet.scss +0 -0
  35. /package/src/{components → styles/components}/ion-alert.scss +0 -0
  36. /package/src/{components → styles/components}/ion-breadcrumbs.scss +0 -0
  37. /package/src/{components → styles/components}/ion-button.scss +0 -0
  38. /package/src/{components → styles/components}/ion-card.scss +0 -0
  39. /package/src/{components → styles/components}/ion-chip.scss +0 -0
  40. /package/src/{components → styles/components}/ion-content.scss +0 -0
  41. /package/src/{components → styles/components}/ion-datetime.scss +0 -0
  42. /package/src/{components → styles/components}/ion-fab.scss +0 -0
  43. /package/src/{components → styles/components}/ion-list.scss +0 -0
  44. /package/src/{components → styles/components}/ion-loading.scss +0 -0
  45. /package/src/{components → styles/components}/ion-modal.scss +0 -0
  46. /package/src/{components → styles/components}/ion-picker.scss +0 -0
  47. /package/src/{components → styles/components}/ion-popover.scss +0 -0
  48. /package/src/{components → styles/components}/ion-range.scss +0 -0
  49. /package/src/{components → styles/components}/ion-searchbar.scss +0 -0
  50. /package/src/{components → styles/components}/ion-toast.scss +0 -0
  51. /package/src/{components → styles/components}/ion-toggle.scss +0 -0
  52. /package/src/{components → styles/components}/ion-toolbar.scss +0 -0
  53. /package/src/{default-variables.scss → styles/default-variables.scss} +0 -0
  54. /package/src/{ionic-theme-ios26-dark-always.scss → styles/ionic-theme-ios26-dark-always.scss} +0 -0
  55. /package/src/{ionic-theme-ios26-dark-class.scss → styles/ionic-theme-ios26-dark-class.scss} +0 -0
  56. /package/src/{ionic-theme-ios26-dark-system.scss → styles/ionic-theme-ios26-dark-system.scss} +0 -0
  57. /package/src/{ionic-theme-ios26.scss → styles/ionic-theme-ios26.scss} +0 -0
  58. /package/src/{md-ion-list-inset.scss → styles/md-ion-list-inset.scss} +0 -0
  59. /package/src/{utils → styles/utils}/api.scss +0 -0
  60. /package/src/{utils → styles/utils}/dark/ion-button.scss +0 -0
  61. /package/src/{utils → styles/utils}/dark/ion-fab.scss +0 -0
  62. /package/src/{utils → styles/utils}/dark/ion-tabs.scss +0 -0
  63. /package/src/{utils → styles/utils}/theme-dark.scss +0 -0
  64. /package/src/{utils → styles/utils}/theme-list-inset.scss +0 -0
  65. /package/src/{utils → styles/utils}/translucent.scss +0 -0
@@ -0,0 +1,290 @@
1
+ import { EffectScales, registeredEffect } from './interfaces';
2
+ import { createGesture, GestureDetail, createAnimation } from '@ionic/core';
3
+ import type { Animation } from '@ionic/core/dist/types/utils/animation/animation-interface';
4
+ import { Gesture } from '@ionic/core/dist/types/utils/gesture';
5
+ import { cloneElement, getTransform } from './utils';
6
+
7
+ const GESTURE_NAME = 'ios26-enable-gesture';
8
+ const ANIMATED_NAME = 'ios26-animated';
9
+
10
+ export const registerEffect = (
11
+ targetElement: HTMLElement,
12
+ effectTagName: string,
13
+ selectedClassName: string,
14
+ scales: EffectScales,
15
+ ): registeredEffect | undefined => {
16
+ if (!targetElement.classList.contains('ios')) {
17
+ return undefined;
18
+ }
19
+
20
+ let gesture!: Gesture;
21
+ let moveAnimation: Animation | undefined;
22
+ let currentTouchedElement: HTMLElement | undefined;
23
+ let animationLatestX: number | undefined;
24
+ let effectElementPositionY: number | undefined;
25
+
26
+ let enterAnimationPromise: Promise<void> | undefined;
27
+ let moveAnimationPromise: Promise<void> | undefined;
28
+ let clearActivatedTimer: ReturnType<typeof setTimeout> | undefined;
29
+
30
+ const effectElement = cloneElement(effectTagName);
31
+
32
+ /**
33
+ * These event listeners fix a bug where gestures don't complete properly.
34
+ * They terminate the gesture using native events as a fallback.
35
+ */
36
+ const onPointerDown = () => {
37
+ clearActivated();
38
+ currentTouchedElement?.classList.remove('ion-activated');
39
+ gesture.destroy();
40
+ createAnimationGesture();
41
+ };
42
+ const onPointerUp = (event: PointerEvent) => {
43
+ clearActivatedTimer = setTimeout(() => {
44
+ onEndGesture();
45
+ currentTouchedElement?.classList.remove('ion-activated');
46
+ gesture.destroy();
47
+ createAnimationGesture();
48
+ });
49
+ };
50
+
51
+ targetElement.addEventListener('pointerdown', onPointerDown);
52
+ targetElement.addEventListener('pointerup', onPointerUp);
53
+
54
+ const createAnimationGesture = () => {
55
+ targetElement.classList.add(GESTURE_NAME);
56
+ gesture = createGesture({
57
+ el: targetElement,
58
+ threshold: 0,
59
+ gestureName: `${GESTURE_NAME}_${effectTagName}_${crypto.randomUUID()}`,
60
+ onStart: (event) => onStartGesture(event),
61
+ onMove: (event) => onMoveGesture(event),
62
+ onEnd: (event) => onEndGesture(),
63
+ });
64
+ gesture.enable(true);
65
+ };
66
+ createAnimationGesture();
67
+
68
+ const clearActivated = () => {
69
+ if (!currentTouchedElement) {
70
+ return;
71
+ }
72
+ requestAnimationFrame(() => {
73
+ effectElement.style.display = 'none';
74
+ effectElement.innerHTML = '';
75
+ effectElement.style.transform = 'none';
76
+ });
77
+
78
+ targetElement.classList.remove(ANIMATED_NAME);
79
+ currentTouchedElement = undefined;
80
+ moveAnimation = undefined; // 次回のために破棄
81
+ moveAnimationPromise = undefined;
82
+ enterAnimationPromise = undefined; // 次回のためにリセット
83
+ };
84
+
85
+ const onStartGesture = (detail: GestureDetail): boolean | undefined => {
86
+ enterAnimationPromise = undefined;
87
+ currentTouchedElement = ((detail.event.target as HTMLElement).closest(effectTagName) as HTMLElement) || undefined;
88
+ const tabSelectedElement = targetElement.querySelector(`${effectTagName}.${selectedClassName}`);
89
+ if (currentTouchedElement === undefined || tabSelectedElement === null) {
90
+ return false;
91
+ }
92
+ effectElementPositionY = tabSelectedElement.getBoundingClientRect().top;
93
+
94
+ const startTransform = getTransform(
95
+ tabSelectedElement.getBoundingClientRect().left + tabSelectedElement.clientWidth / 2,
96
+ effectElementPositionY,
97
+ tabSelectedElement,
98
+ );
99
+ const middleTransform = getTransform(
100
+ (tabSelectedElement.getBoundingClientRect().left + tabSelectedElement.clientWidth / 2 + detail.currentX) / 2,
101
+ effectElementPositionY,
102
+ currentTouchedElement,
103
+ );
104
+ const endTransform = getTransform(detail.currentX, effectElementPositionY, currentTouchedElement);
105
+ const enterAnimation = createAnimation();
106
+ enterAnimation
107
+ .addElement(effectElement)
108
+ .delay(70)
109
+ .beforeStyles({
110
+ width: `${tabSelectedElement.clientWidth}px`,
111
+ height: `${tabSelectedElement.clientHeight}px`,
112
+ display: 'block',
113
+ })
114
+ .beforeAddWrite(() => {
115
+ tabSelectedElement.childNodes.forEach((node) => {
116
+ effectElement.appendChild(node.cloneNode(true));
117
+ });
118
+ targetElement.classList.add(ANIMATED_NAME);
119
+ currentTouchedElement!.classList.add('ion-activated');
120
+ currentTouchedElement!.click();
121
+ });
122
+
123
+ if (currentTouchedElement === tabSelectedElement) {
124
+ enterAnimation
125
+ .keyframes([
126
+ {
127
+ transform: `${startTransform} ${scales.small}`,
128
+ opacity: 1,
129
+ offset: 0,
130
+ },
131
+ {
132
+ transform: `${middleTransform} ${scales.large}`,
133
+ opacity: 1,
134
+ offset: 0.6,
135
+ },
136
+ {
137
+ transform: `${endTransform} ${scales.medium}`,
138
+ opacity: 1,
139
+ offset: 1,
140
+ },
141
+ ])
142
+ .duration(160);
143
+ } else {
144
+ enterAnimation
145
+ .keyframes([
146
+ {
147
+ transform: `${startTransform} ${scales.small}`,
148
+ opacity: 1,
149
+ offset: 0,
150
+ },
151
+ {
152
+ transform: `${middleTransform} ${scales.large}`,
153
+ opacity: 1,
154
+ offset: 0.65,
155
+ },
156
+ {
157
+ transform: `${endTransform} ${scales.medium}`,
158
+ opacity: 1,
159
+ offset: 1,
160
+ },
161
+ ])
162
+ .duration(280);
163
+ }
164
+ animationLatestX = detail.currentX;
165
+ enterAnimationPromise = enterAnimation.play().then(() => {
166
+ enterAnimationPromise = undefined;
167
+ });
168
+ return true;
169
+ };
170
+
171
+ const onMoveGesture = (detail: GestureDetail): boolean | undefined => {
172
+ if (currentTouchedElement === undefined || enterAnimationPromise || moveAnimationPromise) {
173
+ return true; // Skip Animation
174
+ }
175
+
176
+ const startTransform = getTransform(animationLatestX!, effectElementPositionY!, currentTouchedElement);
177
+ const endTransform = getTransform(detail.currentX, effectElementPositionY!, currentTouchedElement);
178
+
179
+ // Move用のアニメーションオブジェクトを初回のみ作成し、再利用する
180
+ if (!moveAnimation) {
181
+ moveAnimation = createAnimation();
182
+ moveAnimation
183
+ .addElement(effectElement)
184
+ .duration(800)
185
+ .easing('ease-in-out')
186
+ .keyframes([
187
+ {
188
+ transform: `${startTransform} ${scales.medium}`,
189
+ opacity: 1,
190
+ offset: 0,
191
+ },
192
+ {
193
+ transform: `${startTransform} ${scales.xlarge}`,
194
+ opacity: 1,
195
+ offset: 0.2,
196
+ },
197
+ {
198
+ transform: `${endTransform} ${scales.medium}`,
199
+ opacity: 1,
200
+ offset: 1,
201
+ },
202
+ ]);
203
+ } else {
204
+ moveAnimation.duration(0).keyframes([
205
+ {
206
+ transform: `${endTransform} ${scales.medium}`,
207
+ opacity: 1,
208
+ offset: 1,
209
+ },
210
+ {
211
+ transform: `${endTransform} ${scales.medium}`,
212
+ opacity: 1,
213
+ offset: 1,
214
+ },
215
+ ]);
216
+ }
217
+ animationLatestX = detail.currentX;
218
+ moveAnimationPromise = moveAnimation.play().then(() => {
219
+ moveAnimationPromise = undefined;
220
+ });
221
+ return true;
222
+ };
223
+
224
+ const onEndGesture = (): boolean | undefined => {
225
+ // タイマーをクリア(正常にonEndGestureが実行された場合)
226
+ if (clearActivatedTimer !== undefined) {
227
+ clearTimeout(clearActivatedTimer);
228
+ clearActivatedTimer = undefined;
229
+ }
230
+
231
+ if (currentTouchedElement === undefined) {
232
+ return false;
233
+ }
234
+
235
+ const transform = getTransform(animationLatestX!, effectElementPositionY!, currentTouchedElement);
236
+
237
+ const leaveAnimation = createAnimation();
238
+ leaveAnimation.addElement(effectElement);
239
+ leaveAnimation
240
+ .onFinish(() => clearActivated())
241
+ .easing('ease-in')
242
+ .duration(80)
243
+ .keyframes([
244
+ {
245
+ transform: `${transform} ${scales.medium}`,
246
+ opacity: 1,
247
+ },
248
+ {
249
+ transform: `${transform} ${scales.small}`,
250
+ opacity: 0,
251
+ },
252
+ ]);
253
+ (async () => {
254
+ // Wait for enter animation to complete before playing leave animation
255
+ if (enterAnimationPromise) {
256
+ setTimeout(() => currentTouchedElement!.classList.remove('ion-activated'), 50);
257
+ await enterAnimationPromise;
258
+ } else {
259
+ currentTouchedElement!.classList.remove('ion-activated');
260
+ }
261
+ leaveAnimation.play();
262
+ })();
263
+ return true;
264
+ };
265
+
266
+ return {
267
+ destroy: () => {
268
+ // Remove event listeners
269
+ targetElement.removeEventListener('pointerdown', onPointerDown);
270
+ targetElement.removeEventListener('pointerup', onPointerUp);
271
+
272
+ // Clear any pending timer
273
+ if (clearActivatedTimer !== undefined) {
274
+ clearTimeout(clearActivatedTimer);
275
+ clearActivatedTimer = undefined;
276
+ }
277
+
278
+ // Clear activated state
279
+ clearActivated();
280
+
281
+ // Destroy gesture
282
+ if (gesture) {
283
+ gesture.destroy();
284
+ }
285
+
286
+ // Remove gesture class
287
+ targetElement.classList.remove(GESTURE_NAME);
288
+ },
289
+ };
290
+ };
@@ -0,0 +1,10 @@
1
+ export interface EffectScales {
2
+ small: string;
3
+ medium: string;
4
+ large: string;
5
+ xlarge: string;
6
+ }
7
+
8
+ export interface registeredEffect {
9
+ destroy: () => void;
10
+ }
@@ -0,0 +1,28 @@
1
+ export const cloneElement = (tagName: string): HTMLElement => {
2
+ const getCachedEl = document.querySelector(`${tagName}.ion-cloned-element`);
3
+ if (getCachedEl !== null) {
4
+ return getCachedEl as HTMLElement;
5
+ }
6
+
7
+ const clonedEl = document.createElement(tagName) as HTMLElement;
8
+ clonedEl.classList.add('ion-cloned-element');
9
+ clonedEl.style.setProperty('display', 'none');
10
+ document.body.appendChild(clonedEl);
11
+
12
+ return clonedEl;
13
+ };
14
+
15
+ export const getTransform = (detailCurrentX: number, tabEffectElY: number, tabSelectedActual: Element): string => {
16
+ const diff = -2;
17
+ const currentX = detailCurrentX - tabSelectedActual.clientWidth / 2;
18
+ const maxLeft = tabSelectedActual.getBoundingClientRect().left + diff;
19
+ const maxRight = tabSelectedActual.getBoundingClientRect().right - diff - tabSelectedActual.clientWidth;
20
+
21
+ if (maxLeft < currentX && currentX < maxRight) {
22
+ return `translate3d(${currentX}px, ${tabEffectElY}px, 0)`;
23
+ }
24
+ if (maxLeft > currentX) {
25
+ return `translate3d(${maxLeft}px, ${tabEffectElY}px, 0)`;
26
+ }
27
+ return `translate3d(${maxRight}px, ${tabEffectElY}px, 0)`;
28
+ };
package/src/index.ts ADDED
@@ -0,0 +1,21 @@
1
+ import { registeredEffect } from './gestures/interfaces';
2
+ import { registerEffect } from './gestures';
3
+ export * from './gestures/interfaces';
4
+
5
+ export const registerTabBarEffect = (targetElement: HTMLElement): registeredEffect | undefined => {
6
+ return registerEffect(targetElement, 'ion-tab-button', 'tab-selected', {
7
+ small: 'scale(1.1)',
8
+ medium: 'scale(1.2)',
9
+ large: 'scale(1.3)',
10
+ xlarge: 'scale(1.3, 1.5)',
11
+ });
12
+ };
13
+
14
+ export const registerSegmentEffect = (targetElement: HTMLElement): registeredEffect | undefined => {
15
+ return registerEffect(targetElement, 'ion-segment-button', 'segment-button-checked', {
16
+ small: 'scale(1.35)',
17
+ medium: 'scale(1.45)',
18
+ large: 'scale(1.55)',
19
+ xlarge: 'scale(1.55, 1.65)',
20
+ });
21
+ };
@@ -0,0 +1,106 @@
1
+ @use '../utils/api';
2
+
3
+ ion-segment.ios:not(.ios26-disabled) {
4
+ @include api.glass-background;
5
+ min-height: 48px;
6
+ border-radius: 25px;
7
+
8
+ &.segment-expand {
9
+ min-height: 24px;
10
+ width: calc(100% - var(--ion-safe-area-left, 0) - var(--ion-safe-area-left, 0) - 24px);
11
+
12
+ &.segment-activated {
13
+ transform: scale(1);
14
+ }
15
+
16
+ ion-segment-button {
17
+ min-height: 24px;
18
+ }
19
+ }
20
+
21
+ transition: transform var(--ios26-activated-transition-duration) ease-out;
22
+ will-change: transform;
23
+
24
+ &.in-toolbar-color:not(.in-segment-color) {
25
+ ion-segment-button:not(.segment-button-checked)::part(native) {
26
+ color: rgba(var(--ion-text-color-rgb, 0, 0, 0), 1) !important;
27
+ }
28
+ }
29
+
30
+ &:not(.ios26-enable-gesture).segment-activated {
31
+ transform: scale(1.1);
32
+ ion-segment-button {
33
+ transition: transform 100ms ease-out;
34
+ &.segment-button-checked::part(native) {
35
+ transform: scale(1.08);
36
+ }
37
+ &::part(indicator-background) {
38
+ position: relative;
39
+ z-index: 1;
40
+ background: rgba(var(--ion-text-color-rgb, 0, 0, 0), 0);
41
+ transform: scale(1.1);
42
+ transform-origin: center center;
43
+ }
44
+ }
45
+ }
46
+
47
+ &.ios26-enable-gesture {
48
+ &:has(ion-segment-button.ion-activated) {
49
+ transform: scale(1.1);
50
+ ion-segment-button {
51
+ transition: transform 100ms ease-out;
52
+ &.segment-button-checked::part(native) {
53
+ transform: scale(1.08);
54
+ }
55
+ &::part(indicator-background) {
56
+ position: relative;
57
+ z-index: 1;
58
+ transform: scale(1.1);
59
+ transform-origin: center center;
60
+ }
61
+ }
62
+ }
63
+ &.ios26-animated {
64
+ ion-segment-button {
65
+ &::part(indicator-background) {
66
+ background: rgba(var(--ion-text-color-rgb, 0, 0, 0), 0);
67
+ }
68
+ }
69
+ }
70
+ }
71
+ }
72
+
73
+ ion-segment-button.ios:not(.ios26-disabled) {
74
+ --border-width: 0;
75
+ --ion-color-base: var(--ion-text-color, #000);
76
+ --padding-start: 8px;
77
+ --padding-end: 8px;
78
+ min-width: 60px;
79
+ margin: 3px 2px;
80
+ font-size: 14.5px;
81
+
82
+ &::part(indicator-background) {
83
+ border-radius: 25px;
84
+ box-shadow: none;
85
+ transition: background 0.2s ease;
86
+ background: rgba(var(--ion-text-color-rgb, 0, 0, 0), 0.06);
87
+ }
88
+
89
+ &.ion-cloned-element {
90
+ &::part(native) {
91
+ border-radius: 25px;
92
+ @include api.glass-background(1, 0, 104%);
93
+ background: transparent;
94
+ height: 100%;
95
+ }
96
+ color: var(--color-selected, var(--ion-color-primary, #0054e9));
97
+ pointer-events: none;
98
+ position: absolute;
99
+ left: 0;
100
+ top: -2.5px;
101
+ transform-origin: center;
102
+ & > * {
103
+ visibility: hidden;
104
+ }
105
+ }
106
+ }
@@ -0,0 +1,117 @@
1
+ @use '../utils/api';
2
+
3
+ ion-tab-bar.ios:not(.ios26-disabled) {
4
+ @include api.glass-background;
5
+ z-index: 2;
6
+
7
+ border-radius: 40px;
8
+ --color: rgb(var(--ion-text-color-rgb, 0, 0, 0));
9
+
10
+ &[slot='top'] {
11
+ position: absolute;
12
+ top: var(--ios26-floating-safe-area-top);
13
+ right: calc(16px + var(--ion-safe-area-right, 0px));
14
+ }
15
+
16
+ &[slot='bottom'] {
17
+ position: absolute;
18
+ bottom: var(--ios26-floating-safe-area-bottom);
19
+ left: calc(16px + var(--ion-safe-area-left, 0px));
20
+ }
21
+
22
+ /**
23
+ * 100% - margin-left - margin-right - right-fab - margin-left-fab
24
+ */
25
+ width: calc(100% - calc(18px + var(--ion-safe-area-left, 0px)) - calc(18px + var(--ion-safe-area-left, 0px)) - 60px - 12px);
26
+ max-width: 474px;
27
+ min-height: 56px;
28
+ &:has(:nth-child(5)) {
29
+ width: calc(100% - calc(18px + var(--ion-safe-area-left, 0px)) - calc(18px + var(--ion-safe-area-left, 0px)));
30
+ max-width: 546px;
31
+ }
32
+ padding: 2px 2px;
33
+
34
+ transition: transform var(--ios26-activated-transition-duration) ease-out;
35
+ will-change: transform;
36
+ &:has(ion-tab-button.ion-activated) {
37
+ transform: scale(1.038);
38
+ }
39
+
40
+ &:has(ion-tab-button.ion-activated) {
41
+ ion-tab-button.tab-selected:not(.ion-activated) {
42
+ --color-selected: rgb(var(--ion-text-color-rgb, 0, 0, 0));
43
+ }
44
+ }
45
+
46
+ /**
47
+ * effectが有効な場合、effectでスタイリングするため不要
48
+ */
49
+ &.ios26-enable-gesture.ios26-animated {
50
+ ion-tab-button.tab-selected::part(native) {
51
+ background: rgba(var(--ios26-button-color-selected-rgb), 0);
52
+ }
53
+ ion-tab-button.ion-activated {
54
+ ion-label,
55
+ ion-icon {
56
+ filter: brightness(100%);
57
+ }
58
+ }
59
+ }
60
+ }
61
+
62
+ ion-tab-button.ios:not(.ios26-disabled) {
63
+ ion-icon {
64
+ font-size: 26px;
65
+ }
66
+ ion-label,
67
+ ion-icon {
68
+ transition:
69
+ filter var(--ios26-activated-transition-duration) ease,
70
+ color var(--ios26-activated-transition-duration) ease;
71
+ }
72
+ background: rgba(var(--ios26-glass-background-rgb), 0);
73
+ height: auto;
74
+ transition: transform var(--ios26-activated-transition-duration) ease;
75
+ &::part(native) {
76
+ overflow: visible;
77
+ min-height: 56px;
78
+ border-radius: 32px;
79
+ }
80
+
81
+ &.ion-activated {
82
+ transform: scale(1.1);
83
+ position: relative;
84
+ color: var(--color-selected);
85
+ ion-label,
86
+ ion-icon {
87
+ filter: brightness(104%);
88
+ }
89
+ }
90
+
91
+ &.tab-selected {
92
+ &::part(native) {
93
+ background: rgba(var(--ios26-button-color-selected-rgb), 0.095);
94
+ transition: background var(--ios26-activated-transition-duration) ease;
95
+ }
96
+
97
+ &.ion-activated::part(native) {
98
+ background: rgba(var(--ios26-glass-box-shadow-color-rgb), 0.02);
99
+ }
100
+ }
101
+
102
+ &.ion-cloned-element {
103
+ &::part(native) {
104
+ @include api.glass-background(1, 0, 104%);
105
+ background: transparent;
106
+ }
107
+ color: var(--color-selected, var(--ion-color-primary, #0054e9));
108
+ pointer-events: none;
109
+ position: absolute;
110
+ left: 0;
111
+ top: 0;
112
+ transform-origin: center;
113
+ & > * {
114
+ visibility: hidden;
115
+ }
116
+ }
117
+ }
@@ -13,12 +13,12 @@ ion-toolbar.md:not(.ios26-disabled) {
13
13
  }
14
14
  }
15
15
 
16
- ion-buttons.md.ios26-disabled {
16
+ ion-buttons.md {
17
17
  /**
18
18
  * ion-buttons.ios26-disabled > ion-button.button-default is for ios26 structure.
19
19
  * for .md, remove button-default styles. Will be button-clear.
20
20
  */
21
- ion-button:not(.ios26-disabled).button-default {
21
+ ion-button.md:not(.ios26-disabled).button-default {
22
22
  // copy .button-clear
23
23
  --color: initial;
24
24
  --background: transparent;
@@ -1,64 +0,0 @@
1
- @use '../utils/api';
2
-
3
- ion-segment.ios:not(.ios26-disabled) {
4
- @include api.glass-background;
5
- min-height: 48px;
6
- border-radius: 25px;
7
-
8
- &.segment-expand {
9
- min-height: 24px;
10
- width: calc(100% - var(--ion-safe-area-left, 0) - var(--ion-safe-area-left, 0) - 24px);
11
-
12
- &.segment-activated {
13
- transform: scale(1);
14
- }
15
-
16
- ion-segment-button {
17
- min-height: 24px;
18
- }
19
- }
20
-
21
- transition: transform var(--ios26-activated-transition-duration) ease-out;
22
- will-change: transform;
23
-
24
- &.in-toolbar-color:not(.in-segment-color) {
25
- ion-segment-button:not(.segment-button-checked)::part(native) {
26
- color: rgba(var(--ion-text-color-rgb, 0, 0, 0), 1) !important;
27
- }
28
- }
29
-
30
- ion-segment-button {
31
- --border-width: 0;
32
- --ion-color-base: var(--ion-text-color, #000);
33
- --padding-start: 8px;
34
- --padding-end: 8px;
35
- min-width: 60px;
36
- margin: 3px 2px;
37
- font-size: 14.5px;
38
-
39
- &::part(indicator-background) {
40
- border-radius: 25px;
41
- box-shadow: none;
42
- transition: background 0.2s ease;
43
- background: rgba(var(--ion-text-color-rgb, 0, 0, 0), 0.06);
44
- }
45
- }
46
-
47
- &.segment-activated {
48
- transform: scale(1.1);
49
-
50
- ion-segment-button {
51
- transition: transform 100ms ease-out;
52
- &.segment-button-checked::part(native) {
53
- transform: scale(1.08);
54
- }
55
- &::part(indicator-background) {
56
- position: relative;
57
- z-index: 1;
58
- background: rgba(var(--ion-text-color-rgb, 0, 0, 0), 0);
59
- transform: scale(1.1);
60
- transform-origin: center center;
61
- }
62
- }
63
- }
64
- }