Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion demo/src/app/docs/docs-content.generated.ts

Large diffs are not rendered by default.

26 changes: 26 additions & 0 deletions docs/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,32 @@ Review every section newer than the version currently installed, in ascending or

Each section lists only the changes that require application code or configuration updates.

## Migrating to 9.2.0

### Version-independent theme names

The theme opt-out class and CSS variables have been renamed to remove the OS version from their public names. The old names are deprecated; use the new names in new code and migrate existing customizations when convenient.

| Deprecated name | New name |
| --- | --- |
| `ios26-disabled` | `ios-theme-disabled` |
| `--ios26-content-box-shadow-rgb` | `--ios-theme-content-box-shadow-rgb` |
| `--ios26-*` (other theme variables) | `--ios-theme-*` (same suffix) |

```diff
- <ion-button class="ios26-disabled">Standard Ionic button</ion-button>
+ <ion-button class="ios-theme-disabled">Standard Ionic button</ion-button>
```

```diff
ion-content {
- --ios26-content-box-shadow-rgb: 255, 255, 255;
+ --ios-theme-content-box-shadow-rgb: 255, 255, 255;
}
```

`ios26-disabled` remains supported as an alias. Old CSS variables remain supported as fallbacks, and the new variable takes precedence when both are set. Existing applications can therefore keep using the deprecated names during migration. Package names and stylesheet paths are unchanged.

## Migrating to 9.0.0

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.
Expand Down
6 changes: 4 additions & 2 deletions docs/special-markup.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,12 @@ Add `.toolbar-searchbar` when an `ion-toolbar` combines a search bar with start

## Opting out

Add `.ios26-disabled` to an individual Ionic component when it must retain Ionic's standard iOS styling.
Add `.ios-theme-disabled` to an individual Ionic component when it must retain Ionic's standard iOS styling.

```html preview
<ion-button>iOS 26 theme</ion-button> <ion-button class="ios26-disabled">Standard Ionic button</ion-button>
<ion-button>iOS 26 theme</ion-button> <ion-button class="ios-theme-disabled">Standard Ionic button</ion-button>
```

For the background model behind inset lists, see [Using `ion-item-group`](./using-ion-item-group.md).

`ios26-disabled` remains supported as a deprecated alias for `ios-theme-disabled`.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"build:demo": "npm run build && cd demo && npm install && npm run build -- --configuration=production",
"lint": "prettier --check \"./**/*.{scss,ts}\" && prettier --parser angular --check \"./**/*.html\"",
"fmt": "prettier --write \"./**/*.{scss,ts}\" && prettier --parser angular --write \"./**/*.html\"",
"release": "np --no-tests --no-publish",
"release": "np --branch ios26 --no-tests --no-publish",
"prepublishOnly": "npm run build"
},
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion src/popover/animations/ios.enter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const iosEnterAnimation = (baseEl: HTMLElement, opts?: any): Animation =>
if (!['ion-button', 'ion-buttons'].includes(referenceSizeEl.localName)) {
return false;
}
if (referenceSizeEl.classList.contains('ios26-disabled')) {
if (referenceSizeEl.matches('.ios-theme-disabled, .ios26-disabled')) {
return false;
}
return true;
Expand Down
6 changes: 3 additions & 3 deletions src/styles/components/ion-action-sheet.scss
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
@use '../utils/api';

ion-action-sheet.ios:not(.ios26-disabled) {
ion-action-sheet.ios:not(.ios-theme-disabled, .ios26-disabled) {
--backdrop-opacity: 0.2;
--color: var(--ion-color-step-600, var(--ion-text-color-step-400, #666666));

.action-sheet-wrapper {
padding-bottom: 0;
bottom: var(--ios26-floating-safe-area-bottom);
bottom: var(--ios-theme-floating-safe-area-bottom, var(--ios26-floating-safe-area-bottom));
}

.action-sheet-container {
Expand Down Expand Up @@ -74,7 +74,7 @@ ion-action-sheet.ios:not(.ios26-disabled) {
* ios26, action-sheet should not has 'cancel'.
* It is recommend to use Popover.
*/
ion-action-sheet.ios.select-action-sheet:not(.ios26-disabled) {
ion-action-sheet.ios.select-action-sheet:not(.ios-theme-disabled, .ios26-disabled) {
.action-sheet-container {
.action-sheet-group {
button.action-sheet-cancel {
Expand Down
2 changes: 1 addition & 1 deletion src/styles/components/ion-alert.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@use '../utils/api';

ion-alert.ios:not(.ios26-disabled) {
ion-alert.ios:not(.ios-theme-disabled, .ios26-disabled) {
--min-width: 280px;
--backdrop-opacity: 0.2;

Expand Down
45 changes: 25 additions & 20 deletions src/styles/components/ion-button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ $scaleup-large-icon-only: 1.22;
/**
* compare .header-collapse-main ion-toolbar.in-toolbar ion-title, .header-collapse-main ion-toolbar.in-toolbar ion-buttons
*/
transition: transform var(--ios26-activated-transition-duration) ease-out;
transition: transform var(--ios-theme-activated-transition-duration, var(--ios26-activated-transition-duration)) ease-out;

z-index: 0;

Expand Down Expand Up @@ -59,11 +59,11 @@ $scaleup-large-icon-only: 1.22;
}
}

&:has(ion-button:not(.ios26-disabled).button-clear.button-large) {
&:has(ion-button:not(.ios-theme-disabled, .ios26-disabled).button-clear.button-large) {
border-radius: 30px;
}

ion-button:not(.ios26-disabled).button-clear {
ion-button:not(.ios-theme-disabled, .ios26-disabled).button-clear {
--background: transparent;
--border-style: none;
--border-width: 0;
Expand Down Expand Up @@ -121,11 +121,11 @@ $scaleup-large-icon-only: 1.22;
}
}

ion-button:not(.ios26-disabled) {
ion-button:not(.ios-theme-disabled, .ios26-disabled) {
margin: 0;
}

ion-menu-button:not(.ios26-disabled) {
ion-menu-button:not(.ios-theme-disabled, .ios26-disabled) {
width: 44px;
height: 44px;
padding: 2px;
Expand All @@ -140,7 +140,7 @@ $scaleup-large-icon-only: 1.22;
}

transition:
transform var(--ios26-activated-transition-duration) ease-out,
transform var(--ios-theme-activated-transition-duration, var(--ios26-activated-transition-duration)) ease-out,
color 50ms ease;

&.in-toolbar:not(.ion-color):not(.in-toolbar-color) {
Expand All @@ -153,13 +153,13 @@ $scaleup-large-icon-only: 1.22;
}

// will-change is optimized to only apply when needed for performance
&:not(.ios26-disabled):hover,
&:not(.ios26-disabled):active,
&:not(.ios26-disabled).ion-activated {
&:not(.ios-theme-disabled, .ios26-disabled):hover,
&:not(.ios-theme-disabled, .ios26-disabled):active,
&:not(.ios-theme-disabled, .ios26-disabled).ion-activated {
will-change: transform;
}

&:not(.ios26-disabled):not(:hover):not(:active):not(.ion-activated) {
&:not(.ios-theme-disabled, .ios26-disabled):not(:hover):not(:active):not(.ion-activated) {
will-change: auto;
}

Expand Down Expand Up @@ -248,8 +248,8 @@ $scaleup-large-icon-only: 1.22;
}
} @else {
--box-shadow:
inset 0 0 8px 0 rgba(var(--ios26-glass-box-shadow-color-rgb), 0.2),
0 0 8px -4px rgba(var(--ios26-glass-box-shadow-color-rgb), 0.92);
inset 0 0 8px 0 rgba(var(--ios-theme-glass-box-shadow-color-rgb, var(--ios26-glass-box-shadow-color-rgb)), 0.2),
0 0 8px -4px rgba(var(--ios-theme-glass-box-shadow-color-rgb, var(--ios26-glass-box-shadow-color-rgb)), 0.92);
}
&::part(native)::before {
content: '';
Expand Down Expand Up @@ -313,12 +313,13 @@ $scaleup-large-icon-only: 1.22;

// fill:default only
&:not(.button-solid):not(.button-outline):not(.button-clear) {
--background: rgba(var(--ios26-glass-background-rgb), 0.72);
--background: rgba(var(--ios-theme-glass-background-rgb, var(--ios26-glass-background-rgb)), 0.72);
--background-hover: transparent;
--background-activated: transparent;
@if not $is-back-button {
--box-shadow:
inset 0 0 8px 0 rgba(var(--ios26-glass-box-shadow-color-rgb), 0.2), 0 0 10px 0 rgba(var(--ios26-glass-box-shadow-color-rgb), 0.82);
inset 0 0 8px 0 rgba(var(--ios-theme-glass-box-shadow-color-rgb, var(--ios26-glass-box-shadow-color-rgb)), 0.2),
0 0 10px 0 rgba(var(--ios-theme-glass-box-shadow-color-rgb, var(--ios26-glass-box-shadow-color-rgb)), 0.82);
}

&:not(.button-has-icon-only):not(.back-button-has-icon-only) {
Expand Down Expand Up @@ -404,18 +405,22 @@ $scaleup-large-icon-only: 1.22;
}
}

ion-buttons.ios:not(.ios26-disabled):not(:has(ion-back-button, ion-button:not(.button-clear), ion-menu-button.menu-button-hidden)) {
ion-buttons.ios:not(.ios-theme-disabled, .ios26-disabled):not(
:has(ion-back-button, ion-button:not(.button-clear), ion-menu-button.menu-button-hidden)
) {
@include theme-buttons;
}

// Ionic's collapsing-header transition has a more specific contextual selector.
.header-collapse-main
:where(ion-toolbar.in-toolbar)
ion-buttons.ios:not(.ios26-disabled):not(:has(ion-back-button, ion-button:not(.button-clear), ion-menu-button.menu-button-hidden)) {
transition: transform var(--ios26-activated-transition-duration) ease-out;
ion-buttons.ios:not(.ios-theme-disabled, .ios26-disabled):not(
:has(ion-back-button, ion-button:not(.button-clear), ion-menu-button.menu-button-hidden)
) {
transition: transform var(--ios-theme-activated-transition-duration, var(--ios26-activated-transition-duration)) ease-out;
}

ion-buttons.ios:not(.ios26-disabled):not(:has(ion-back-button, ion-button.button-clear)) {
ion-buttons.ios:not(.ios-theme-disabled, .ios26-disabled):not(:has(ion-back-button, ion-button.button-clear)) {
ion-button {
margin: 0 6px;
&:first-child {
Expand All @@ -430,9 +435,9 @@ ion-buttons.ios:not(.ios26-disabled):not(:has(ion-back-button, ion-button.button
}
}

ion-button.ios:not(.ios26-disabled) {
ion-button.ios:not(.ios-theme-disabled, .ios26-disabled) {
@include theme-button;
}
ion-back-button.ios:not(.ios26-disabled) {
ion-back-button.ios:not(.ios-theme-disabled, .ios26-disabled) {
@include theme-button($is-back-button: true);
}
2 changes: 1 addition & 1 deletion src/styles/components/ion-card.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ion-card.ios:not(.ios26-disabled) {
ion-card.ios:not(.ios-theme-disabled, .ios26-disabled) {
border-radius: 24px;

ion-list ion-item {
Expand Down
16 changes: 9 additions & 7 deletions src/styles/components/ion-content.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
* so we adjust the margin and padding of the content.
* Also supports virtual scrolling using .ion-content-scroll-host.
*/
.ion-page.ios:not(.ios26-disabled):has(ion-header.header-translucent)
ion-content.ios:not(.ios26-disabled).content-fullscreen:has(.ion-content-scroll-host) {
.ion-page.ios:not(.ios-theme-disabled, .ios26-disabled):has(ion-header.header-translucent)
ion-content.ios:not(.ios-theme-disabled, .ios26-disabled).content-fullscreen:has(.ion-content-scroll-host) {
--padding-top: calc(var(--offset-top) * -1);
> *:not(.ion-content-scroll-host, ion-fab) {
transform: translateY(var(--offset-top));
Expand All @@ -13,10 +13,12 @@
padding-top: var(--offset-top);
}
}
.ion-page.ios:not(.ios26-disabled):not(:has(ion-footer)) ion-content.ios:not(.ios26-disabled).content-fullscreen .ion-content-scroll-host {
margin-bottom: calc(calc(60px + var(--ios26-floating-safe-area-bottom)) * -1);
padding-bottom: calc(60px + var(--ios26-floating-safe-area-bottom));
.ion-page.ios:not(.ios-theme-disabled, .ios26-disabled):not(:has(ion-footer))
ion-content.ios:not(.ios-theme-disabled, .ios26-disabled).content-fullscreen
.ion-content-scroll-host {
margin-bottom: calc(calc(60px + var(--ios-theme-floating-safe-area-bottom, var(--ios26-floating-safe-area-bottom))) * -1);
padding-bottom: calc(60px + var(--ios-theme-floating-safe-area-bottom, var(--ios26-floating-safe-area-bottom)));
}
ion-content.ios:not(.ios26-disabled).content-fullscreen:not(:has(.ion-content-scroll-host)) {
--padding-bottom: calc(60px + var(--ios26-floating-safe-area-bottom));
ion-content.ios:not(.ios-theme-disabled, .ios26-disabled).content-fullscreen:not(:has(.ion-content-scroll-host)) {
--padding-bottom: calc(60px + var(--ios-theme-floating-safe-area-bottom, var(--ios26-floating-safe-area-bottom)));
}
2 changes: 1 addition & 1 deletion src/styles/components/ion-datetime.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@use '../utils/api';

ion-datetime.ios:not(.ios26-disabled) {
ion-datetime.ios:not(.ios-theme-disabled, .ios26-disabled) {
@include api.glass-background-overlay;
border-radius: 24px;

Expand Down
20 changes: 11 additions & 9 deletions src/styles/components/ion-fab.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,21 @@ $scaleup-small-icon-only: 1.18;
$scaleup-default-icon-only: 1.2;
$scaleup-large-icon-only: 1.12;

ion-fab.ios:not(.ios26-disabled) {
ion-fab.ios:not(.ios-theme-disabled, .ios26-disabled) {
--transform-value: 2.5px;
&:has(> ion-fab-button.fab-button-small) {
--transform-value: 4px;
}

ion-fab-button {
--background: rgba(var(--ios26-glass-background-rgb), 0.72);
--background: rgba(var(--ios-theme-glass-background-rgb, var(--ios26-glass-background-rgb)), 0.72);
--box-shadow:
inset 0 0 8px 0 rgba(var(--ios26-glass-box-shadow-color-rgb), 0.2), 0 0 10px 0 rgba(var(--ios26-glass-box-shadow-color-rgb), 0.82);
inset 0 0 8px 0 rgba(var(--ios-theme-glass-box-shadow-color-rgb, var(--ios26-glass-box-shadow-color-rgb)), 0.2),
0 0 10px 0 rgba(var(--ios-theme-glass-box-shadow-color-rgb, var(--ios26-glass-box-shadow-color-rgb)), 0.82);
--transition:
var(--ios26-activated-transition-duration) transform cubic-bezier(0.25, 1.11, 0.78, 1.59),
var(--ios26-activated-transition-duration) color ease;
var(--ios-theme-activated-transition-duration, var(--ios26-activated-transition-duration)) transform
cubic-bezier(0.25, 1.11, 0.78, 1.59),
var(--ios-theme-activated-transition-duration, var(--ios26-activated-transition-duration)) color ease;
--background-activated-opacity: 0;
--background-focused-opacity: 0;
--background-hover-opacity: 0;
Expand Down Expand Up @@ -78,15 +80,15 @@ ion-fab.ios:not(.ios26-disabled) {
}

&.fab-vertical-top {
top: var(--ios26-floating-safe-area-top);
top: var(--ios-theme-floating-safe-area-top, var(--ios26-floating-safe-area-top));
}

&.fab-vertical-bottom {
bottom: var(--ios26-floating-safe-area-bottom);
bottom: var(--ios-theme-floating-safe-area-bottom, var(--ios26-floating-safe-area-bottom));
}

&.fab-horizontal-start {
left: calc(16px + var(--ios26-menu-width, var(--ion-safe-area-left), 0px));
left: calc(16px + var(--ios-theme-menu-width, var(--ios26-menu-width, var(--ion-safe-area-left), 0px)));
}

&.fab-horizontal-end {
Expand All @@ -95,7 +97,7 @@ ion-fab.ios:not(.ios26-disabled) {

&:has(.fab-button-small) {
&.fab-horizontal-start {
left: calc(10px + var(--ios26-menu-width, var(--ion-safe-area-left), 0px));
left: calc(10px + var(--ios-theme-menu-width, var(--ios26-menu-width, var(--ion-safe-area-left), 0px)));
}

&.fab-horizontal-end {
Expand Down
4 changes: 2 additions & 2 deletions src/styles/components/ion-inputs.scss
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
}
}

ion-input.ios:not(.ios26-disabled) {
ion-input.ios:not(.ios-theme-disabled, .ios26-disabled) {
&.input-fill-outline {
@include outline-field('input');

Expand All @@ -65,7 +65,7 @@ ion-input.ios:not(.ios26-disabled) {
}
}

ion-textarea.ios:not(.ios26-disabled) {
ion-textarea.ios:not(.ios-theme-disabled, .ios26-disabled) {
&.textarea-fill-outline {
@include outline-field('textarea');

Expand Down
4 changes: 2 additions & 2 deletions src/styles/components/ion-list.scss
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
@use '../utils/structured-list';

ion-list.ios:not(.ios26-disabled) {
ion-list.ios:not(.ios-theme-disabled, .ios26-disabled) {
@include structured-list.support-text;
ion-item {
--background-hover-opacity: 0;
--background-focused-opacity: 0;
}
}

ion-list.list-inset.ios:not(.ios26-disabled) {
ion-list.list-inset.ios:not(.ios-theme-disabled, .ios26-disabled) {
@include structured-list.layout(20px, 24px);

ion-radio-group,
Expand Down
2 changes: 1 addition & 1 deletion src/styles/components/ion-loading.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@use '../utils/api';

ion-loading.ios:not(.ios26-disabled) {
ion-loading.ios:not(.ios-theme-disabled, .ios26-disabled) {
--backdrop-opacity: 0.2;
.loading-wrapper {
@include api.glass-background;
Expand Down
Loading
Loading