@rdlabo/ionic-theme-ios27 0.1.0-0 → 0.1.0-1

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 (85) hide show
  1. package/README.md +12 -10
  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-content.css +1 -1
  7. package/dist/css/components/ion-datetime.css +1 -1
  8. package/dist/css/components/ion-fab.css +1 -1
  9. package/dist/css/components/ion-inputs.css +1 -1
  10. package/dist/css/components/ion-list.css +1 -1
  11. package/dist/css/components/ion-loading.css +1 -1
  12. package/dist/css/components/ion-menu.css +1 -1
  13. package/dist/css/components/ion-modal.css +1 -1
  14. package/dist/css/components/ion-popover.css +1 -1
  15. package/dist/css/components/ion-range.css +1 -1
  16. package/dist/css/components/ion-searchbar.css +1 -1
  17. package/dist/css/components/ion-segment.css +1 -1
  18. package/dist/css/components/ion-tabs.css +1 -1
  19. package/dist/css/components/ion-toast.css +1 -1
  20. package/dist/css/components/ion-toggle.css +1 -1
  21. package/dist/css/components/ion-toolbar.css +1 -1
  22. package/dist/css/ionic-theme-ios27-dark-always.css +1 -0
  23. package/dist/css/ionic-theme-ios27-dark-class.css +1 -0
  24. package/dist/css/ionic-theme-ios27-dark-system.css +1 -0
  25. package/dist/css/ionic-theme-ios27.css +1 -0
  26. package/dist/css/md-remove-ios-class-effect.css +1 -1
  27. package/dist/css/utils/translucent.css +1 -1
  28. package/dist/popover/animations/ios.enter.js +2 -2
  29. package/dist/popover/animations/ios.leave.js +2 -2
  30. package/dist/sheets-of-glass/index.js +2 -2
  31. package/dist/transition/ios.transition.d.ts.map +1 -1
  32. package/dist/transition/ios.transition.js +10 -9
  33. package/docs/eslint.md +1 -1
  34. package/docs/feedback.md +2 -2
  35. package/docs/ios-adaptive.md +90 -0
  36. package/docs/migration.md +6 -83
  37. package/docs/special-markup.md +5 -3
  38. package/docs/using-ion-item-group.md +2 -2
  39. package/package.json +2 -2
  40. package/src/popover/animations/ios.enter.ts +2 -2
  41. package/src/popover/animations/ios.leave.ts +2 -2
  42. package/src/sheets-of-glass/index.ts +2 -2
  43. package/src/styles/components/ion-action-sheet.scss +4 -4
  44. package/src/styles/components/ion-alert.scss +1 -1
  45. package/src/styles/components/ion-button.scss +25 -21
  46. package/src/styles/components/ion-card.scss +1 -1
  47. package/src/styles/components/ion-content.scss +10 -8
  48. package/src/styles/components/ion-datetime.scss +1 -1
  49. package/src/styles/components/ion-fab.scss +11 -9
  50. package/src/styles/components/ion-inputs.scss +2 -2
  51. package/src/styles/components/ion-list.scss +2 -2
  52. package/src/styles/components/ion-loading.scss +1 -1
  53. package/src/styles/components/ion-menu.scss +26 -21
  54. package/src/styles/components/ion-modal.scss +3 -3
  55. package/src/styles/components/ion-picker.scss +1 -1
  56. package/src/styles/components/ion-popover.scss +4 -3
  57. package/src/styles/components/ion-range.scss +2 -2
  58. package/src/styles/components/ion-searchbar.scss +6 -4
  59. package/src/styles/components/ion-segment.scss +8 -8
  60. package/src/styles/components/ion-tabs.scss +13 -13
  61. package/src/styles/components/ion-toast.scss +8 -7
  62. package/src/styles/components/ion-toggle.scss +4 -4
  63. package/src/styles/components/ion-toolbar.scss +13 -12
  64. package/src/styles/default-variables.scss +4 -3
  65. package/src/styles/md-remove-ios-class-effect.scss +2 -2
  66. package/src/styles/utils/api.scss +16 -16
  67. package/src/styles/utils/dark/ion-button.scss +8 -8
  68. package/src/styles/utils/dark/ion-fab.scss +1 -1
  69. package/src/styles/utils/dark/ion-segment.scss +3 -2
  70. package/src/styles/utils/dark/ion-tabs.scss +3 -2
  71. package/src/styles/utils/translucent.scss +21 -16
  72. package/src/transition/ios.transition.ts +12 -9
  73. package/dist/css/ionic-theme-ios26-dark-always.css +0 -1
  74. package/dist/css/ionic-theme-ios26-dark-class.css +0 -1
  75. package/dist/css/ionic-theme-ios26-dark-system.css +0 -1
  76. package/dist/css/ionic-theme-ios26.css +0 -1
  77. package/dist/docs.json +0 -234
  78. package/dist/focus-controller /index.d.ts +0 -6
  79. package/dist/focus-controller /index.d.ts.map +0 -1
  80. package/dist/focus-controller /index.js +0 -62
  81. package/docs/ios-18.md +0 -16
  82. /package/src/styles/{ionic-theme-ios26-dark-always.scss → ionic-theme-ios27-dark-always.scss} +0 -0
  83. /package/src/styles/{ionic-theme-ios26-dark-class.scss → ionic-theme-ios27-dark-class.scss} +0 -0
  84. /package/src/styles/{ionic-theme-ios26-dark-system.scss → ionic-theme-ios27-dark-system.scss} +0 -0
  85. /package/src/styles/{ionic-theme-ios26.scss → ionic-theme-ios27.scss} +0 -0
package/docs/eslint.md CHANGED
@@ -2,7 +2,7 @@
2
2
  title: Keep lists consistent with ESLint
3
3
  ---
4
4
 
5
- Catch missing list groups before they reach the screen. In Ionic Angular apps, `@rdlabo/eslint-plugin-rules` checks the markup used by iOS 26.
5
+ Catch missing list groups before they reach the screen. In Ionic Angular apps, `@rdlabo/eslint-plugin-rules` checks the markup used by iOS 27.
6
6
 
7
7
  ## Enable the list check
8
8
 
package/docs/feedback.md CHANGED
@@ -31,7 +31,7 @@ export interface IonicConfig {
31
31
 
32
32
  ### native-inner(or item-inner) part to ion-item
33
33
 
34
- The styling for `ion-item[lines=inset]` is applied to `.item-inner`, which cannot be styled directly. This limitation means that for iOS 26 styling, I can only modify the border-bottom style through `::part(native)` with padding-right, preventing me from utilizing the full right side of `ion-item`. Adding `::part(native-inner)` would increase styling flexibility.
34
+ The styling for `ion-item[lines=inset]` is applied to `.item-inner`, which cannot be styled directly. This limitation means that for iOS 27 styling, I can only modify the border-bottom style through `::part(native)` with padding-right, preventing me from utilizing the full right side of `ion-item`. Adding `::part(native-inner)` would increase styling flexibility.
35
35
 
36
36
  ```diff
37
37
  <ion-item>
@@ -54,7 +54,7 @@ Resolved.
54
54
 
55
55
  ### should disable ion-back-button Animation
56
56
 
57
- Resolved: by created https://github.com/rdlabo-dev/ionic-theme-ios26/tree/main/src/transition
57
+ Resolved: by created https://github.com/rdlabo-dev/ionic-theme-ios27/tree/main/src/transition
58
58
 
59
59
  ## feat(): ion-content[fullscreen=true] will have .content-fullscreen class
60
60
 
@@ -0,0 +1,90 @@
1
+ ---
2
+ title: Adaptive iOS themes
3
+ ---
4
+
5
+ # Adaptive iOS themes
6
+
7
+ Use CSS feature queries to choose a theme while keeping Ionic's default iOS styles on older browsers. Safari 27 adds `:heading` support; see the [WebKit announcement](https://webkit.org/blog/17967/news-from-wwdc26-webkit-in-safari-27-beta/).
8
+
9
+ These checks detect browser capabilities, not the operating system version. Other browsers may support the same features. Keep Ionic mode and platform-specific animation configuration consistent with your application.
10
+
11
+ ## Load only the iOS 27 theme
12
+
13
+ In your global Sass stylesheet, use `meta.load-css` inside the feature query. Unlike `@use`, it can emit the theme inside `@supports`.
14
+
15
+ ```scss
16
+ @use 'sass:meta';
17
+
18
+ @supports selector(:heading) {
19
+ @include meta.load-css('@rdlabo/ionic-theme-ios27/src/styles/default-variables');
20
+ @include meta.load-css('@rdlabo/ionic-theme-ios27/src/styles/ionic-theme-ios27');
21
+ @include meta.load-css('@rdlabo/ionic-theme-ios27/src/styles/ionic-theme-ios27-dark-class');
22
+ @include meta.load-css('@rdlabo/ionic-theme-ios27/src/styles/md-remove-ios-class-effect');
23
+ }
24
+ ```
25
+
26
+ ## Use the iOS 26 and iOS 27 themes together
27
+
28
+ Install both packages:
29
+
30
+ ```bash
31
+ npm install @rdlabo/ionic-theme-ios26 @rdlabo/ionic-theme-ios27
32
+ ```
33
+
34
+ Use this global Sass configuration instead of unconditional theme imports:
35
+
36
+ ```scss
37
+ @use 'sass:meta';
38
+
39
+ @supports selector(:heading) {
40
+ @include meta.load-css('@rdlabo/ionic-theme-ios27/src/styles/default-variables');
41
+ @include meta.load-css('@rdlabo/ionic-theme-ios27/src/styles/ionic-theme-ios27');
42
+ @include meta.load-css('@rdlabo/ionic-theme-ios27/src/styles/ionic-theme-ios27-dark-class');
43
+ @include meta.load-css('@rdlabo/ionic-theme-ios27/src/styles/md-remove-ios-class-effect');
44
+ }
45
+
46
+ @supports (text-wrap: pretty) and (not selector(:heading)) {
47
+ @include meta.load-css('@rdlabo/ionic-theme-ios26/src/styles/default-variables');
48
+ @include meta.load-css('@rdlabo/ionic-theme-ios26/src/styles/ionic-theme-ios26');
49
+ @include meta.load-css('@rdlabo/ionic-theme-ios26/src/styles/ionic-theme-ios26-dark-class');
50
+ @include meta.load-css('@rdlabo/ionic-theme-ios26/src/styles/md-remove-ios-class-effect');
51
+ }
52
+ ```
53
+
54
+ The branches are mutually exclusive. Browsers without either feature retain Ionic's default styling. The `text-wrap: pretty` check preserves the earlier theme's capability-based fallback for older Safari versions.
55
+
56
+ Both examples use class-based dark mode. Load Ionic's matching dark palette in your application; for system or always-dark mode, replace the `-dark-class` imports with the matching variant. If you also use `md-ion-list-inset`, load the corresponding package's stylesheet inside each branch.
57
+
58
+ ## Select matching JavaScript animations
59
+
60
+ Use the same feature checks for dynamic imports before initializing Ionic. Call this loader for the iOS platform; leave other platforms on their existing animation configuration.
61
+
62
+ ```ts
63
+ async function loadIOSAnimations() {
64
+ if (typeof CSS === 'undefined') {
65
+ return {};
66
+ }
67
+
68
+ const theme = CSS.supports('selector(:heading)')
69
+ ? await import('@rdlabo/ionic-theme-ios27')
70
+ : CSS.supports('text-wrap: pretty')
71
+ ? await import('@rdlabo/ionic-theme-ios26')
72
+ : undefined;
73
+
74
+ if (!theme) {
75
+ return {};
76
+ }
77
+
78
+ return {
79
+ navAnimation: theme.iosTransitionAnimation,
80
+ popoverEnter: theme.popoverEnterAnimation,
81
+ popoverLeave: theme.popoverLeaveAnimation,
82
+ };
83
+ }
84
+
85
+ // In your browser bootstrap, before initializing Ionic:
86
+ const animations = isPlatform('ios') ? await loadIOSAnimations() : {};
87
+ provideIonicAngular({ ...animations });
88
+ ```
89
+
90
+ Import `isPlatform` and `provideIonicAngular` from your Ionic Angular entry point as shown in the README. Pass the same options to `setupIonicReact` or `IonicVue` when using those frameworks. The imports are selected at runtime; the bundler may still emit chunks for both packages. In server-rendered apps, run the selection during browser initialization.
package/docs/migration.md CHANGED
@@ -4,91 +4,14 @@ title: Migration
4
4
 
5
5
  # Migration
6
6
 
7
- Review every section newer than the version currently installed, in ascending order. For example, when upgrading from 1.x to 9.0.0, complete the 2.0.0 and 3.0.0 migration steps before reviewing 9.0.0.
7
+ ## iOS 27 naming
8
8
 
9
- Each section lists only the changes that require application code or configuration updates.
9
+ Use `@rdlabo/ionic-theme-ios27` for the current theme. Update stylesheet imports to `ionic-theme-ios27.scss` or `ionic-theme-ios27.css`, including the `-dark-always`, `-dark-system`, and `-dark-class` variants.
10
10
 
11
- ## Migrating to 9.0.0
11
+ Use the version-independent `--ios-theme-*` CSS variables. The corresponding `--ios26-*` variables remain supported as deprecated fallbacks. When both are set, the new name takes precedence. For example, use `--ios-theme-content-box-shadow-rgb` instead of `--ios26-content-box-shadow-rgb`.
12
12
 
13
- Version 9 aligns the theme's major version with Ionic Framework 9. It does not introduce additional breaking changes beyond those documented in the earlier migration sections.
13
+ For opting out of the theme, use the version-independent `ios-theme-disabled` class. The `ios26-disabled` class remains supported as a deprecated alias; migrate existing markup when convenient.
14
14
 
15
- Both Ionic 8 and Ionic 9 remain supported. Version 9 requires `@ionic/core >=8.8.1 <10`.
15
+ See [Special markup and classes](./special-markup.md) and [Default variables](../src/styles/default-variables.scss) for the current names.
16
16
 
17
- ## Migrating to 3.0.0
18
-
19
- ### Rename `.header-item-group` to `.item-group-header`
20
-
21
- The class for an `ion-item-group` used as a section header has been renamed for consistency with the element it modifies. Replace every occurrence of `.header-item-group` in application templates and styles.
22
-
23
- ```diff
24
- - <ion-item-group class="header-item-group">
25
- + <ion-item-group class="item-group-header">
26
- ...
27
- </ion-item-group>
28
- ```
29
-
30
- The old class is no longer styled by the theme. This rename applies to markup shared with `@rdlabo/ionic-theme-md3` as well.
31
-
32
- ## Migrating to 2.0.0
33
-
34
- ### Configure `iosTransitionAnimation`
35
-
36
- Version 2 requires the package navigation transition. It follows Ionic's default iOS transition without the obsolete `animateBackButton()` behavior that animated a Large Title into the back-button label.
37
-
38
- ```ts
39
- import { isPlatform } from '@ionic/core'; // or @ionic/angular/standalone, @ionic/react, @ionic/vue
40
- import { iosTransitionAnimation } from '@rdlabo/ionic-theme-ios26';
41
-
42
- // Angular
43
- provideIonicAngular({
44
- // ...
45
- navAnimation: isPlatform('ios') ? iosTransitionAnimation : undefined,
46
- });
47
-
48
- // React
49
- setupIonicReact({
50
- // ...
51
- navAnimation: isPlatform('ios') ? iosTransitionAnimation : undefined,
52
- });
53
-
54
- // Vue
55
- createApp(App).use(IonicVue, {
56
- // ...
57
- navAnimation: isPlatform('ios') ? iosTransitionAnimation : undefined,
58
- });
59
- ```
60
-
61
- With this transition configured, `<ion-buttons><ion-back-button></ion-back-button></ion-buttons>` can be used without the unwanted transition side effects caused by the old animation.
62
-
63
- ## Migrating to 1.0.0
64
-
65
- ### Update SCSS import paths
66
-
67
- The source files moved under `src/styles` when JavaScript files were added to the package.
68
-
69
- ```diff
70
- - @import '@rdlabo/ionic-theme-ios26/src/default-variables.scss';
71
- + @import '@rdlabo/ionic-theme-ios26/src/styles/default-variables.scss';
72
- ```
73
-
74
- Generated CSS paths under `dist` did not change.
75
-
76
- ### Rename `--ios26-color-background-rgb`
77
-
78
- ```diff
79
- :root {
80
- - --ios26-color-background-rgb: 255, 255, 255;
81
- + --ios26-content-box-shadow-rgb: 255, 255, 255;
82
- }
83
- ```
84
-
85
- ### Rename brightness variables
86
-
87
- Replace each `--ion-color-*-brightness-rgb` variable with `--ion-color-*-brightness` and use a color value instead of an RGB channel list.
88
-
89
- ```diff
90
- :root {
91
- - --ion-color-primary-brightness-rgb: 130, 255, 255;
92
- + --ion-color-primary-brightness: #96feff;
93
- }
94
- ```
17
+ Earlier migration notes are preserved on the [previous theme branch](https://github.com/rdlabo-dev/ionic-theme-ios27/blob/ios26/docs/migration.md).
@@ -79,7 +79,7 @@ Add `.segment-expand` when segment buttons should divide the available width eve
79
79
 
80
80
  ## Classic search bar in a condense header
81
81
 
82
- The theme gives iOS search bars the iOS 26 appearance by default. Add `.searchbar-classic` to the search field shown beneath a large title in an `ion-header` with `collapse="condense"`. It uses the conventional filled iOS appearance and collapses with the large title instead of remaining in the fixed header.
82
+ The theme gives iOS search bars the iOS 27 appearance by default. Add `.searchbar-classic` to the search field shown beneath a large title in an `ion-header` with `collapse="condense"`. It uses the conventional filled iOS appearance and collapses with the large title instead of remaining in the fixed header.
83
83
 
84
84
  Place it in a toolbar with a color, such as `color="light"`; the classic background is derived from that color's contrast value.
85
85
 
@@ -136,10 +136,12 @@ Add `.toolbar-searchbar` when an `ion-toolbar` combines a search bar with start
136
136
 
137
137
  ## Opting out
138
138
 
139
- Add `.ios26-disabled` to an individual Ionic component when it must retain Ionic's standard iOS styling.
139
+ Add `.ios-theme-disabled` to an individual Ionic component when it must retain Ionic's standard iOS styling.
140
+
141
+ `.ios26-disabled` is deprecated but remains supported as an alias with the same behavior. Use `.ios-theme-disabled` for new code.
140
142
 
141
143
  ```html preview
142
- <ion-button>iOS 26 theme</ion-button> <ion-button class="ios26-disabled">Standard Ionic button</ion-button>
144
+ <ion-button>iOS 27 theme</ion-button> <ion-button class="ios-theme-disabled">Standard Ionic button</ion-button>
143
145
  ```
144
146
 
145
147
  For the background model behind inset lists, see [Using `ion-item-group`](./using-ion-item-group.md).
@@ -24,9 +24,9 @@ No wrapper is required for lists that do not use `inset="true"`.
24
24
 
25
25
  ## Why the wrapper is required
26
26
 
27
- Ionic normally gives `ion-list` its background, which makes `ion-list-header` appear inside the same surface as the items. The iOS 26 layout treats the header and item surface separately.
27
+ Ionic normally gives `ion-list` its background, which makes `ion-list-header` appear inside the same surface as the items. The iOS 27 layout treats the header and item surface separately.
28
28
 
29
- ![Inset list background comparison showing why ion-item-group is required](https://raw.githubusercontent.com/rdlabo-dev/ionic-theme-ios26/v3.0.0/screenshots/why-ion-list-inset.png)
29
+ ![Inset list background comparison showing why ion-item-group is required](https://raw.githubusercontent.com/rdlabo-dev/ionic-theme-ios27/v3.0.0/screenshots/why-ion-list-inset.png)
30
30
 
31
31
  The theme therefore:
32
32
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rdlabo/ionic-theme-ios27",
3
3
  "private": false,
4
- "version": "0.1.0-0",
4
+ "version": "0.1.0-1",
5
5
  "description": "iOS27 Theme for Ionic Framework",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.js",
@@ -46,7 +46,7 @@
46
46
  "bugs": {
47
47
  "url": "https://github.com/rdlabo-dev/ionic-theme-ios27/issues"
48
48
  },
49
- "homepage": "https://docs.rdlabo.dev/projects/ionic-theme-ios26",
49
+ "homepage": "https://docs.rdlabo.dev/projects/ionic-theme-ios27",
50
50
  "devDependencies": {
51
51
  "@ionic/angular": "^9.0.0",
52
52
  "husky": "^9.1.7",
@@ -27,7 +27,7 @@ export const iosEnterAnimation = (baseEl: HTMLElement, opts?: any): Animation =>
27
27
  if (!['ion-button', 'ion-buttons'].includes(referenceSizeEl.localName)) {
28
28
  return false;
29
29
  }
30
- if (referenceSizeEl.classList.contains('ios26-disabled')) {
30
+ if (referenceSizeEl.matches('.ios-theme-disabled, .ios26-disabled')) {
31
31
  return false;
32
32
  }
33
33
  return true;
@@ -102,7 +102,7 @@ export const iosEnterAnimation = (baseEl: HTMLElement, opts?: any): Animation =>
102
102
  .duration(200)
103
103
  .addElement(referenceSizeEl)
104
104
  .beforeStyles({ 'transform-origin': `${originY} ${originX}` })
105
- .beforeAddClass('ios26-replace-element')
105
+ .beforeAddClass('ios27-replace-element')
106
106
  .fromTo('transform', 'scale(1)', 'scale(1.05)')
107
107
  .fromTo('opacity', 1, 0);
108
108
  }
@@ -17,7 +17,7 @@ export const iosLeaveAnimation = (baseEl: HTMLElement): Animation => {
17
17
  const targetAnimation = createAnimation();
18
18
 
19
19
  const doc = baseEl.ownerDocument as any;
20
- const replaceElement = doc.querySelector('.ios26-replace-element') as HTMLElement | null;
20
+ const replaceElement = doc.querySelector('.ios27-replace-element') as HTMLElement | null;
21
21
 
22
22
  if (replaceElement) {
23
23
  const ratio = contentEl.getBoundingClientRect().width / contentEl.getBoundingClientRect().height;
@@ -27,7 +27,7 @@ export const iosLeaveAnimation = (baseEl: HTMLElement): Animation => {
27
27
  .addElement(replaceElement)
28
28
  .delay(100)
29
29
  .duration(300)
30
- .afterRemoveClass('ios26-replace-element')
30
+ .afterRemoveClass('ios27-replace-element')
31
31
  .fromTo('transform', `scale(${scale})`, 'scale(1)')
32
32
  .fromTo('opacity', 0, 0.9);
33
33
  }
@@ -4,8 +4,8 @@ import type { Animation, Gesture, GestureDetail } from '@ionic/core';
4
4
  import { changeSelectedElement, cloneElement, getStep } from '../utils';
5
5
  import { createMoveAnimation, createPreMoveAnimation, getMoveAnimationKeyframe, getScaleAnimation } from './animations';
6
6
 
7
- const GESTURE_NAME = 'ios26-enable-gesture';
8
- const ANIMATED_NAME = 'ios26-animated';
7
+ const GESTURE_NAME = 'ios27-enable-gesture';
8
+ const ANIMATED_NAME = 'ios27-animated';
9
9
 
10
10
  export const registerEffect = (
11
11
  targetElement: HTMLElement,
@@ -1,12 +1,12 @@
1
1
  @use '../utils/api';
2
2
 
3
- ion-action-sheet.ios:not(.ios26-disabled) {
3
+ ion-action-sheet.ios:not(.ios-theme-disabled, .ios26-disabled) {
4
4
  --backdrop-opacity: 0.2;
5
5
  --color: var(--ion-color-step-600, var(--ion-text-color-step-400, #666666));
6
6
 
7
7
  .action-sheet-wrapper {
8
8
  padding-bottom: 0;
9
- bottom: var(--ios26-floating-safe-area-bottom);
9
+ bottom: var(--ios-theme-floating-safe-area-bottom, var(--ios26-floating-safe-area-bottom));
10
10
  }
11
11
 
12
12
  .action-sheet-container {
@@ -71,10 +71,10 @@ ion-action-sheet.ios:not(.ios26-disabled) {
71
71
 
72
72
  /**
73
73
  * For ion-select[interface=action-sheet].
74
- * ios26, action-sheet should not has 'cancel'.
74
+ * ios27, action-sheet should not has 'cancel'.
75
75
  * It is recommend to use Popover.
76
76
  */
77
- ion-action-sheet.ios.select-action-sheet:not(.ios26-disabled) {
77
+ ion-action-sheet.ios.select-action-sheet:not(.ios-theme-disabled, .ios26-disabled) {
78
78
  .action-sheet-container {
79
79
  .action-sheet-group {
80
80
  button.action-sheet-cancel {
@@ -1,6 +1,6 @@
1
1
  @use '../utils/api';
2
2
 
3
- ion-alert.ios:not(.ios26-disabled) {
3
+ ion-alert.ios:not(.ios-theme-disabled, .ios26-disabled) {
4
4
  --min-width: 280px;
5
5
  --backdrop-opacity: 0.2;
6
6
 
@@ -23,7 +23,7 @@ $scaleup-large-icon-only: api.$button-scaleup-large-icon-only;
23
23
  /**
24
24
  * compare .header-collapse-main ion-toolbar.in-toolbar ion-title, .header-collapse-main ion-toolbar.in-toolbar ion-buttons
25
25
  */
26
- transition: transform var(--ios26-activated-transition-duration) ease-out;
26
+ transition: transform var(--ios-theme-activated-transition-duration, var(--ios26-activated-transition-duration)) ease-out;
27
27
 
28
28
  z-index: 0;
29
29
 
@@ -59,11 +59,11 @@ $scaleup-large-icon-only: api.$button-scaleup-large-icon-only;
59
59
  }
60
60
  }
61
61
 
62
- &:has(ion-button:not(.ios26-disabled).button-clear.button-large) {
62
+ &:has(ion-button:not(.ios-theme-disabled, .ios26-disabled).button-clear.button-large) {
63
63
  border-radius: 30px;
64
64
  }
65
65
 
66
- ion-button:not(.ios26-disabled).button-clear {
66
+ ion-button:not(.ios-theme-disabled, .ios26-disabled).button-clear {
67
67
  --background: transparent;
68
68
  --border-style: none;
69
69
  --border-width: 0;
@@ -121,11 +121,11 @@ $scaleup-large-icon-only: api.$button-scaleup-large-icon-only;
121
121
  }
122
122
  }
123
123
 
124
- ion-button:not(.ios26-disabled) {
124
+ ion-button:not(.ios-theme-disabled, .ios26-disabled) {
125
125
  margin: 0;
126
126
  }
127
127
 
128
- ion-menu-button:not(.ios26-disabled) {
128
+ ion-menu-button:not(.ios-theme-disabled, .ios26-disabled) {
129
129
  width: 44px;
130
130
  height: 44px;
131
131
  padding: 2px;
@@ -140,7 +140,7 @@ $scaleup-large-icon-only: api.$button-scaleup-large-icon-only;
140
140
  }
141
141
 
142
142
  transition:
143
- transform var(--ios26-activated-transition-duration) ease-out,
143
+ transform var(--ios-theme-activated-transition-duration, var(--ios26-activated-transition-duration)) ease-out,
144
144
  color 50ms ease;
145
145
 
146
146
  &.in-toolbar:not(.ion-color):not(.in-toolbar-color) {
@@ -153,13 +153,13 @@ $scaleup-large-icon-only: api.$button-scaleup-large-icon-only;
153
153
  }
154
154
 
155
155
  // will-change is optimized to only apply when needed for performance
156
- &:not(.ios26-disabled):hover,
157
- &:not(.ios26-disabled):active,
158
- &:not(.ios26-disabled).ion-activated {
156
+ &:not(.ios-theme-disabled, .ios26-disabled):hover,
157
+ &:not(.ios-theme-disabled, .ios26-disabled):active,
158
+ &:not(.ios-theme-disabled, .ios26-disabled).ion-activated {
159
159
  will-change: transform;
160
160
  }
161
161
 
162
- &:not(.ios26-disabled):not(:hover):not(:active):not(.ion-activated) {
162
+ &:not(.ios-theme-disabled, .ios26-disabled):not(:hover):not(:active):not(.ion-activated) {
163
163
  will-change: auto;
164
164
  }
165
165
 
@@ -248,8 +248,8 @@ $scaleup-large-icon-only: api.$button-scaleup-large-icon-only;
248
248
  }
249
249
  } @else {
250
250
  --box-shadow:
251
- inset 0 0 8px 0 rgba(var(--ios26-glass-box-shadow-color-rgb), 0.2),
252
- 0 0 8px -4px rgba(var(--ios26-glass-box-shadow-color-rgb), 0.92);
251
+ inset 0 0 8px 0 rgba(var(--ios-theme-glass-box-shadow-color-rgb, var(--ios26-glass-box-shadow-color-rgb)), 0.2),
252
+ 0 0 8px -4px rgba(var(--ios-theme-glass-box-shadow-color-rgb, var(--ios26-glass-box-shadow-color-rgb)), 0.92);
253
253
  }
254
254
  &::part(native)::before {
255
255
  content: '';
@@ -313,13 +313,13 @@ $scaleup-large-icon-only: api.$button-scaleup-large-icon-only;
313
313
 
314
314
  // fill:default only
315
315
  &:not(.button-solid):not(.button-outline):not(.button-clear) {
316
- --background: rgba(var(--ios26-glass-background-rgb), 0.72);
316
+ --background: rgba(var(--ios-theme-glass-background-rgb, var(--ios26-glass-background-rgb)), 0.72);
317
317
  --background-hover: transparent;
318
318
  --background-activated: transparent;
319
319
  @if not $is-back-button {
320
320
  --box-shadow:
321
- inset 0 0 8px 0 rgba(var(--ios26-glass-box-shadow-color-rgb), 0.2),
322
- 0 0.5px 1px 0 rgba(var(--ios26-glass-box-shadow-color-rgb), 0.08);
321
+ inset 0 0 8px 0 rgba(var(--ios-theme-glass-box-shadow-color-rgb, var(--ios26-glass-box-shadow-color-rgb)), 0.2),
322
+ 0 0.5px 1px 0 rgba(var(--ios-theme-glass-box-shadow-color-rgb, var(--ios26-glass-box-shadow-color-rgb)), 0.08);
323
323
  }
324
324
 
325
325
  &:not(.button-has-icon-only):not(.back-button-has-icon-only) {
@@ -409,18 +409,22 @@ $scaleup-large-icon-only: api.$button-scaleup-large-icon-only;
409
409
  }
410
410
  }
411
411
 
412
- ion-buttons.ios:not(.ios26-disabled):not(:has(ion-back-button, ion-button:not(.button-clear), ion-menu-button.menu-button-hidden)) {
412
+ ion-buttons.ios:not(.ios-theme-disabled, .ios26-disabled):not(
413
+ :has(ion-back-button, ion-button:not(.button-clear), ion-menu-button.menu-button-hidden)
414
+ ) {
413
415
  @include theme-buttons;
414
416
  }
415
417
 
416
418
  // Ionic's collapsing-header transition has a more specific contextual selector.
417
419
  .header-collapse-main
418
420
  :where(ion-toolbar.in-toolbar)
419
- ion-buttons.ios:not(.ios26-disabled):not(:has(ion-back-button, ion-button:not(.button-clear), ion-menu-button.menu-button-hidden)) {
420
- transition: transform var(--ios26-activated-transition-duration) ease-out;
421
+ ion-buttons.ios:not(.ios-theme-disabled, .ios26-disabled):not(
422
+ :has(ion-back-button, ion-button:not(.button-clear), ion-menu-button.menu-button-hidden)
423
+ ) {
424
+ transition: transform var(--ios-theme-activated-transition-duration, var(--ios26-activated-transition-duration)) ease-out;
421
425
  }
422
426
 
423
- ion-buttons.ios:not(.ios26-disabled):not(:has(ion-back-button, ion-button.button-clear)) {
427
+ ion-buttons.ios:not(.ios-theme-disabled, .ios26-disabled):not(:has(ion-back-button, ion-button.button-clear)) {
424
428
  ion-button {
425
429
  margin: 0 6px;
426
430
  &:first-child {
@@ -435,9 +439,9 @@ ion-buttons.ios:not(.ios26-disabled):not(:has(ion-back-button, ion-button.button
435
439
  }
436
440
  }
437
441
 
438
- ion-button.ios:not(.ios26-disabled) {
442
+ ion-button.ios:not(.ios-theme-disabled, .ios26-disabled) {
439
443
  @include theme-button;
440
444
  }
441
- ion-back-button.ios:not(.ios26-disabled) {
445
+ ion-back-button.ios:not(.ios-theme-disabled, .ios26-disabled) {
442
446
  @include theme-button($is-back-button: true);
443
447
  }
@@ -1,4 +1,4 @@
1
- ion-card.ios:not(.ios26-disabled) {
1
+ ion-card.ios:not(.ios-theme-disabled, .ios26-disabled) {
2
2
  border-radius: 24px;
3
3
 
4
4
  ion-list ion-item {
@@ -1,10 +1,10 @@
1
1
  /**
2
- * Since iOS26, content is now displayed to the full extent of the top and bottom safe areas,
2
+ * Since iOS27, content is now displayed to the full extent of the top and bottom safe areas,
3
3
  * so we adjust the margin and padding of the content.
4
4
  * Also supports virtual scrolling using .ion-content-scroll-host.
5
5
  */
6
- .ion-page.ios:not(.ios26-disabled):has(ion-header.header-translucent)
7
- ion-content.ios:not(.ios26-disabled).content-fullscreen:has(.ion-content-scroll-host) {
6
+ .ion-page.ios:not(.ios-theme-disabled, .ios26-disabled):has(ion-header.header-translucent)
7
+ ion-content.ios:not(.ios-theme-disabled, .ios26-disabled).content-fullscreen:has(.ion-content-scroll-host) {
8
8
  --padding-top: calc(var(--offset-top) * -1);
9
9
  > *:not(.ion-content-scroll-host, ion-fab) {
10
10
  transform: translateY(var(--offset-top));
@@ -13,10 +13,12 @@
13
13
  padding-top: var(--offset-top);
14
14
  }
15
15
  }
16
- .ion-page.ios:not(.ios26-disabled):not(:has(ion-footer)) ion-content.ios:not(.ios26-disabled).content-fullscreen .ion-content-scroll-host {
17
- margin-bottom: calc(calc(60px + var(--ios26-floating-safe-area-bottom)) * -1);
18
- padding-bottom: calc(60px + var(--ios26-floating-safe-area-bottom));
16
+ .ion-page.ios:not(.ios-theme-disabled, .ios26-disabled):not(:has(ion-footer))
17
+ ion-content.ios:not(.ios-theme-disabled, .ios26-disabled).content-fullscreen
18
+ .ion-content-scroll-host {
19
+ margin-bottom: calc(calc(60px + var(--ios-theme-floating-safe-area-bottom, var(--ios26-floating-safe-area-bottom))) * -1);
20
+ padding-bottom: calc(60px + var(--ios-theme-floating-safe-area-bottom, var(--ios26-floating-safe-area-bottom)));
19
21
  }
20
- ion-content.ios:not(.ios26-disabled).content-fullscreen:not(:has(.ion-content-scroll-host)) {
21
- --padding-bottom: calc(60px + var(--ios26-floating-safe-area-bottom));
22
+ ion-content.ios:not(.ios-theme-disabled, .ios26-disabled).content-fullscreen:not(:has(.ion-content-scroll-host)) {
23
+ --padding-bottom: calc(60px + var(--ios-theme-floating-safe-area-bottom, var(--ios26-floating-safe-area-bottom)));
22
24
  }
@@ -1,6 +1,6 @@
1
1
  @use '../utils/api';
2
2
 
3
- ion-datetime.ios:not(.ios26-disabled) {
3
+ ion-datetime.ios:not(.ios-theme-disabled, .ios26-disabled) {
4
4
  @include api.glass-background-overlay;
5
5
  border-radius: 24px;
6
6
 
@@ -4,19 +4,21 @@ $scaleup-small-icon-only: api.$button-scaleup-small-icon-only;
4
4
  $scaleup-default-icon-only: api.$button-scaleup-default-icon-only;
5
5
  $scaleup-large-icon-only: api.$button-scaleup-large-icon-only;
6
6
 
7
- ion-fab.ios:not(.ios26-disabled) {
7
+ ion-fab.ios:not(.ios-theme-disabled, .ios26-disabled) {
8
8
  --transform-value: 2.5px;
9
9
  &:has(> ion-fab-button.fab-button-small) {
10
10
  --transform-value: 4px;
11
11
  }
12
12
 
13
13
  ion-fab-button {
14
- --background: rgba(var(--ios26-glass-background-rgb), 0.72);
14
+ --background: rgba(var(--ios-theme-glass-background-rgb, var(--ios26-glass-background-rgb)), 0.72);
15
15
  --box-shadow:
16
- inset 0 0 8px 0 rgba(var(--ios26-glass-box-shadow-color-rgb), 0.2), 0 0.5px 1px 0 rgba(var(--ios26-glass-box-shadow-color-rgb), 0.08);
16
+ inset 0 0 8px 0 rgba(var(--ios-theme-glass-box-shadow-color-rgb, var(--ios26-glass-box-shadow-color-rgb)), 0.2),
17
+ 0 0.5px 1px 0 rgba(var(--ios-theme-glass-box-shadow-color-rgb, var(--ios26-glass-box-shadow-color-rgb)), 0.08);
17
18
  --transition:
18
- var(--ios26-activated-transition-duration) transform cubic-bezier(0.25, 1.11, 0.78, 1.59),
19
- var(--ios26-activated-transition-duration) color ease;
19
+ var(--ios-theme-activated-transition-duration, var(--ios26-activated-transition-duration)) transform
20
+ cubic-bezier(0.25, 1.11, 0.78, 1.59),
21
+ var(--ios-theme-activated-transition-duration, var(--ios26-activated-transition-duration)) color ease;
20
22
  --background-activated-opacity: 0;
21
23
  --background-focused-opacity: 0;
22
24
  --background-hover-opacity: 0;
@@ -77,15 +79,15 @@ ion-fab.ios:not(.ios26-disabled) {
77
79
  }
78
80
 
79
81
  &.fab-vertical-top {
80
- top: var(--ios26-floating-safe-area-top);
82
+ top: var(--ios-theme-floating-safe-area-top, var(--ios26-floating-safe-area-top));
81
83
  }
82
84
 
83
85
  &.fab-vertical-bottom {
84
- bottom: var(--ios26-floating-safe-area-bottom);
86
+ bottom: var(--ios-theme-floating-safe-area-bottom, var(--ios26-floating-safe-area-bottom));
85
87
  }
86
88
 
87
89
  &.fab-horizontal-start {
88
- left: calc(16px + var(--ios26-menu-width, var(--ion-safe-area-left), 0px));
90
+ left: calc(16px + var(--ios-theme-menu-width, var(--ios26-menu-width, var(--ion-safe-area-left), 0px)));
89
91
  }
90
92
 
91
93
  &.fab-horizontal-end {
@@ -94,7 +96,7 @@ ion-fab.ios:not(.ios26-disabled) {
94
96
 
95
97
  &:has(.fab-button-small) {
96
98
  &.fab-horizontal-start {
97
- left: calc(10px + var(--ios26-menu-width, var(--ion-safe-area-left), 0px));
99
+ left: calc(10px + var(--ios-theme-menu-width, var(--ios26-menu-width, var(--ion-safe-area-left), 0px)));
98
100
  }
99
101
 
100
102
  &.fab-horizontal-end {
@@ -54,7 +54,7 @@
54
54
  }
55
55
  }
56
56
 
57
- ion-input.ios:not(.ios26-disabled) {
57
+ ion-input.ios:not(.ios-theme-disabled, .ios26-disabled) {
58
58
  &.input-fill-outline {
59
59
  @include outline-field('input');
60
60
 
@@ -65,7 +65,7 @@ ion-input.ios:not(.ios26-disabled) {
65
65
  }
66
66
  }
67
67
 
68
- ion-textarea.ios:not(.ios26-disabled) {
68
+ ion-textarea.ios:not(.ios-theme-disabled, .ios26-disabled) {
69
69
  &.textarea-fill-outline {
70
70
  @include outline-field('textarea');
71
71
 
@@ -1,6 +1,6 @@
1
1
  @use '../utils/structured-list';
2
2
 
3
- ion-list.ios:not(.ios26-disabled) {
3
+ ion-list.ios:not(.ios-theme-disabled, .ios26-disabled) {
4
4
  @include structured-list.support-text;
5
5
  ion-item {
6
6
  --background-hover-opacity: 0;
@@ -8,7 +8,7 @@ ion-list.ios:not(.ios26-disabled) {
8
8
  }
9
9
  }
10
10
 
11
- ion-list.list-inset.ios:not(.ios26-disabled) {
11
+ ion-list.list-inset.ios:not(.ios-theme-disabled, .ios26-disabled) {
12
12
  @include structured-list.layout(20px, 24px);
13
13
 
14
14
  ion-radio-group,
@@ -1,6 +1,6 @@
1
1
  @use '../utils/api';
2
2
 
3
- ion-loading.ios:not(.ios26-disabled) {
3
+ ion-loading.ios:not(.ios-theme-disabled, .ios26-disabled) {
4
4
  --backdrop-opacity: 0.2;
5
5
  .loading-wrapper {
6
6
  @include api.glass-background;