diff --git a/README.md b/README.md index c7405139..8a102488 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,9 @@ A CSS/JS theme library that brings the iOS 27 Liquid Glass appearance and navigation animations to Ionic applications while preserving Ionic components and conventions. > [!IMPORTANT] -> This `main` branch contains the iOS 27 theme under the package name `@rdlabo/ionic-theme-ios27`. Its first npm release is pending. For the iOS 26 theme (`@rdlabo/ionic-theme-ios26`), see the [`ios26` branch](https://github.com/rdlabo-dev/ionic-theme-ios27/tree/ios26). +> This `main` branch contains the iOS 27 theme under the package name `@rdlabo/ionic-theme-ios27`. For the iOS 26 theme (`@rdlabo/ionic-theme-ios26`), see the [`ios26` branch](https://github.com/rdlabo-dev/ionic-theme-ios27/tree/ios26). + +> All versions before 1.0.0 are release candidates (RC). APIs, CSS variables, classes, styling, and behavior may change without backward compatibility, including in minor and patch releases. A stable compatibility commitment starts with 1.0.0. [Try the iOS 27 demo](https://ionic-theme-ios27.rdlabo.dev/). @@ -19,19 +21,17 @@ A CSS/JS theme library that brings the iOS 27 Liquid Glass appearance and naviga ## Installation -Requires `@ionic/core` 8.8.1 or later (Ionic 8 and 9). After the first npm release, install it in an existing Ionic project: +Requires `@ionic/core` 8.8.1 or later (Ionic 8 and 9). Install it in an existing Ionic project: ```bash npm install @rdlabo/ionic-theme-ios27 ``` -The stylesheet filenames still use `ionic-theme-ios26` inside the new package. Use the paths below to load the iOS 27 theme. - And import the theme in your project's main CSS file (e.g., `src/styles.scss`). ```css @import '@rdlabo/ionic-theme-ios27/dist/css/default-variables.css'; -@import '@rdlabo/ionic-theme-ios27/dist/css/ionic-theme-ios26.css'; +@import '@rdlabo/ionic-theme-ios27/dist/css/ionic-theme-ios27.css'; /** * Keep Material Design mode unaffected by the iOS theme @@ -50,9 +50,9 @@ And import the theme in your project's main CSS file (e.g., `src/styles.scss`). /* * Support Dark Mode * We support Ionic Dark Mode. More information is here: https://ionicframework.com/docs/theming/dark-mode - * use Always: @import '@rdlabo/ionic-theme-ios27/dist/css/ionic-theme-ios26-dark-always.css' - * use System: @import '@rdlabo/ionic-theme-ios27/dist/css/ionic-theme-ios26-dark-system.css' - * use CSS Class: @import '@rdlabo/ionic-theme-ios27/dist/css/ionic-theme-ios26-dark-class.css' + * use Always: @import '@rdlabo/ionic-theme-ios27/dist/css/ionic-theme-ios27-dark-always.css' + * use System: @import '@rdlabo/ionic-theme-ios27/dist/css/ionic-theme-ios27-dark-system.css' + * use CSS Class: @import '@rdlabo/ionic-theme-ios27/dist/css/ionic-theme-ios27-dark-class.css' */ ``` @@ -119,8 +119,8 @@ When your global stylesheet uses Sass, initialize the themes in this order: ```scss @use '@rdlabo/ionic-theme-ios27/src/styles/default-variables.scss' as ios27-vars; -@use '@rdlabo/ionic-theme-ios27/src/styles/ionic-theme-ios26.scss'; -@use '@rdlabo/ionic-theme-ios27/src/styles/ionic-theme-ios26-dark-class.scss'; +@use '@rdlabo/ionic-theme-ios27/src/styles/ionic-theme-ios27.scss'; +@use '@rdlabo/ionic-theme-ios27/src/styles/ionic-theme-ios27-dark-class.scss'; @use '@rdlabo/ionic-theme-ios27/src/styles/md-remove-ios-class-effect.scss'; @use '@rdlabo/ionic-theme-md3/dist/css/default-variables.css' as md3-vars; @use '@rdlabo/ionic-theme-md3/dist/css/ionic-theme-md3.css'; @@ -163,11 +163,13 @@ createApp(App) ## Documentation +- [Adaptive iOS themes](./docs/ios-adaptive.md) — conditionally load iOS 26 or iOS 27 styles. - [Using ion-item-group](./docs/using-ion-item-group.md) — required markup for inset lists. - [Special markup and classes](./docs/special-markup.md) — opt-in markup and utility classes used by the theme. - [ESLint](./docs/eslint.md) — check list structure with ESLint rules. - [Features](./docs/features.md) — CSS variables, Liquid Glass, selective imports, and dark mode. - [Experimental Animation](./docs/experimental-animation.md) — tab bar and searchable effects. +- [Migration](./docs/migration.md) — stylesheet, class, and CSS variable naming changes. - [iOS 26 migration history](https://github.com/rdlabo-dev/ionic-theme-ios27/blob/ios26/docs/migration.md) — earlier major-version changes for the previous package. diff --git a/demo/README.md b/demo/README.md index e6f5d38d..d294d3d9 100644 --- a/demo/README.md +++ b/demo/README.md @@ -1,6 +1,6 @@ # Demo Application -This is an Angular-based demo application for the Ionic iOS26 Theme Library. +This is an Angular-based demo application for the Ionic iOS27 Theme Library. ## Getting Started @@ -84,8 +84,8 @@ demo/ │ │ ├── pages/ # All page components │ │ └── index.routes.ts │ ├── theme/ -│ │ └── theme-ios26.scss # iOS26 theme -│ └── global.scss +│ │ └── variables.scss # Demo variables +│ └── global.scss # Imports the iOS 27 theme ├── playwright.config.ts # Playwright configuration └── package.json ``` diff --git a/demo/capacitor.config.ts b/demo/capacitor.config.ts index 2a9e4e8c..70129205 100644 --- a/demo/capacitor.config.ts +++ b/demo/capacitor.config.ts @@ -1,8 +1,8 @@ import type { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { - appId: 'io.ionic.theme.ios26', - appName: 'theme-ios26', + appId: 'io.ionic.theme.ios27', + appName: 'theme-ios27', webDir: 'www', }; diff --git a/demo/ios/App/App.xcodeproj/project.pbxproj b/demo/ios/App/App.xcodeproj/project.pbxproj index d1f414b8..164886b4 100644 --- a/demo/ios/App/App.xcodeproj/project.pbxproj +++ b/demo/ios/App/App.xcodeproj/project.pbxproj @@ -355,7 +355,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MARKETING_VERSION = 1.0; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; - PRODUCT_BUNDLE_IDENTIFIER = io.ionic.theme.ios26; + PRODUCT_BUNDLE_IDENTIFIER = io.ionic.theme.ios27; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_VERSION = 5.0; @@ -375,7 +375,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = io.ionic.theme.ios26; + PRODUCT_BUNDLE_IDENTIFIER = io.ionic.theme.ios27; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; SWIFT_VERSION = 5.0; diff --git a/demo/ios/App/App/Info.plist b/demo/ios/App/App/Info.plist index 2389efbd..927649dc 100644 --- a/demo/ios/App/App/Info.plist +++ b/demo/ios/App/App/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion en CFBundleDisplayName - theme-ios26 + theme-ios27 CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier diff --git a/demo/src/app/album/album-page.component.html b/demo/src/app/album/album-page.component.html index bc1f08ab..292c116d 100644 --- a/demo/src/app/album/album-page.component.html +++ b/demo/src/app/album/album-page.component.html @@ -2,7 +2,7 @@ Library - Select diff --git a/demo/src/app/docs/docs-content.generated.ts b/demo/src/app/docs/docs-content.generated.ts index e332be5c..5033ae16 100644 --- a/demo/src/app/docs/docs-content.generated.ts +++ b/demo/src/app/docs/docs-content.generated.ts @@ -1,3 +1,3 @@ // Generated from docs/special-markup.md. Do not edit directly. export const docsContentHtml = - '

Special markup and classes

\n

Most Ionic markup works without changes. The combinations below are explicit opt-ins provided by the theme.

\n

Primary submit buttons

\n

Solid primary submit buttons use --ion-color-primary-brightness for their foreground and border treatment. Define a value with sufficient contrast for your primary color.

\n
:root {\n  --ion-color-primary-brightness: #96feff;\n}
\n\n
<ion-button type="submit" color="primary">Submit</ion-button>\n<ion-button class="button-submit" fill="solid" color="primary">Continue</ion-button>
\n\n
\n
Preview
\nSubmit\nContinue\n
\n\n

Use .button-submit when the button needs the same treatment but cannot use type="submit".

\n

Two-line inset list items

\n

Place an unslotted ion-label immediately alongside an unslotted ion-note to render a two-line item. When using the iOS-style inset-list background, wrap the items in ion-item-group; keep ion-list-header outside the group.

\n
<ion-list inset="true">\n  <ion-list-header>\n    <ion-label>Connections</ion-label>\n  </ion-list-header>\n  <ion-item-group>\n    <ion-item>\n      <ion-label>Network &amp; internet</ion-label>\n      <ion-note>Mobile, Wi-Fi, hotspot</ion-note>\n    </ion-item>\n  </ion-item-group>\n</ion-list>
\n\n
\n
Preview
\n\n \n Connections\n \n \n \n Network & internet\n Mobile, Wi-Fi, hotspot\n \n \n\n
\n\n

Use slot="end" on ion-note when you want the standard trailing-note layout instead.

\n

Inset-list section headers

\n

Add .item-group-header to an ion-item-group to create the centered icon, title, and description used at the top of the component demo pages.

\n

This is an introductory group. Place regular list items in a separate ion-item-group that follows it.

\n
<ion-list inset="true">\n  <ion-item-group class="item-group-header">\n    <ion-item>\n      <ion-label>\n        <ion-icon name="list" style="background: var(--ion-color-primary)"></ion-icon>\n        <h2>Lists</h2>\n        <ion-text>Inset-list examples</ion-text>\n      </ion-label>\n    </ion-item>\n  </ion-item-group>\n  <ion-item-group>\n    <ion-item><ion-label>First item</ion-label></ion-item>\n  </ion-item-group>\n</ion-list>
\n\n
\n
Preview
\n\n \n \n \n \n

Lists

\n Inset-list examples\n
\n
\n
\n \n First item\n \n
\n
\n\n

Full-width segments

\n

Add .segment-expand when segment buttons should divide the available width evenly. The class also changes the Liquid Glass effect sizing when registerSegmentEffect is used.

\n
<ion-segment class="segment-expand" value="new">\n  <ion-segment-button value="new"><ion-label>New</ion-label></ion-segment-button>\n  <ion-segment-button value="replied"><ion-label>Replied</ion-label></ion-segment-button>\n</ion-segment>
\n\n
\n
Preview
\n\n New\n Replied\n\n
\n\n

Classic search bar in a condense header

\n

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.

\n

Place it in a toolbar with a color, such as color="light"; the classic background is derived from that color's contrast value.

\n

The example uses Ionic's standard collapsible large-title structure. Scroll the preview to collapse the large title and reveal the fixed header.

\n
<div class="ion-page">\n  <ion-header translucent="true">\n    <ion-toolbar color="light">\n      <ion-title>Search</ion-title>\n    </ion-toolbar>\n  </ion-header>\n  <ion-content color="light" fullscreen="true">\n    <ion-header collapse="condense">\n      <ion-toolbar color="light">\n        <ion-title size="large">Search</ion-title>\n      </ion-toolbar>\n      <ion-toolbar color="light">\n        <ion-searchbar class="searchbar-classic" placeholder="Filter results"></ion-searchbar>\n      </ion-toolbar>\n    </ion-header>\n    <ion-list inset="true">\n      <ion-item-group>\n        <ion-item><ion-label>Recent item 1</ion-label></ion-item>\n        <ion-item><ion-label>Recent item 2</ion-label></ion-item>\n        <ion-item><ion-label>Recent item 3</ion-label></ion-item>\n        <ion-item><ion-label>Recent item 4</ion-label></ion-item>\n        <ion-item><ion-label>Recent item 5</ion-label></ion-item>\n        <ion-item><ion-label>Recent item 6</ion-label></ion-item>\n        <ion-item><ion-label>Recent item 7</ion-label></ion-item>\n        <ion-item><ion-label>Recent item 8</ion-label></ion-item>\n        <ion-item><ion-label>Recent item 9</ion-label></ion-item>\n        <ion-item><ion-label>Recent item 10</ion-label></ion-item>\n      </ion-item-group>\n    </ion-list>\n  </ion-content>\n</div>
\n\n
\n
Preview
\n
\n \n \n Search\n \n \n \n \n \n Search\n \n \n \n \n \n \n \n Recent item 1\n Recent item 2\n Recent item 3\n Recent item 4\n Recent item 5\n Recent item 6\n Recent item 7\n Recent item 8\n Recent item 9\n Recent item 10\n \n \n \n
\n
\n\n

The .ion-page wrapper makes this embedded preview behave like a complete routed page. An application using ion-router-outlet normally receives that page container automatically. The inset list and its items only provide enough content to demonstrate scrolling; they are not required by .searchbar-classic.

\n

Search-bar toolbars

\n

Add .toolbar-searchbar when an ion-toolbar combines a search bar with start or end buttons. The class centers the slotted controls and adjusts the spacing around the search field.

\n
<ion-toolbar class="toolbar-searchbar">\n  <ion-buttons slot="start">\n    <ion-button>Cancel</ion-button>\n  </ion-buttons>\n  <ion-searchbar></ion-searchbar>\n</ion-toolbar>
\n\n
\n
Preview
\n\n \n Cancel\n \n \n\n
\n\n

Opting out

\n

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

\n
<ion-button>iOS 26 theme</ion-button> <ion-button class="ios26-disabled">Standard Ionic button</ion-button>
\n\n
\n
Preview
\niOS 26 theme Standard Ionic button\n
\n\n

For the background model behind inset lists, see Using ion-item-group.

\n'; + '

Special markup and classes

\n

Most Ionic markup works without changes. The combinations below are explicit opt-ins provided by the theme.

\n

Primary submit buttons

\n

Solid primary submit buttons use --ion-color-primary-brightness for their foreground and border treatment. Define a value with sufficient contrast for your primary color.

\n
:root {\n  --ion-color-primary-brightness: #96feff;\n}
\n\n
<ion-button type="submit" color="primary">Submit</ion-button>\n<ion-button class="button-submit" fill="solid" color="primary">Continue</ion-button>
\n\n
\n
Preview
\nSubmit\nContinue\n
\n\n

Use .button-submit when the button needs the same treatment but cannot use type="submit".

\n

Two-line inset list items

\n

Place an unslotted ion-label immediately alongside an unslotted ion-note to render a two-line item. When using the iOS-style inset-list background, wrap the items in ion-item-group; keep ion-list-header outside the group.

\n
<ion-list inset="true">\n  <ion-list-header>\n    <ion-label>Connections</ion-label>\n  </ion-list-header>\n  <ion-item-group>\n    <ion-item>\n      <ion-label>Network &amp; internet</ion-label>\n      <ion-note>Mobile, Wi-Fi, hotspot</ion-note>\n    </ion-item>\n  </ion-item-group>\n</ion-list>
\n\n
\n
Preview
\n\n \n Connections\n \n \n \n Network & internet\n Mobile, Wi-Fi, hotspot\n \n \n\n
\n\n

Use slot="end" on ion-note when you want the standard trailing-note layout instead.

\n

Inset-list section headers

\n

Add .item-group-header to an ion-item-group to create the centered icon, title, and description used at the top of the component demo pages.

\n

This is an introductory group. Place regular list items in a separate ion-item-group that follows it.

\n
<ion-list inset="true">\n  <ion-item-group class="item-group-header">\n    <ion-item>\n      <ion-label>\n        <ion-icon name="list" style="background: var(--ion-color-primary)"></ion-icon>\n        <h2>Lists</h2>\n        <ion-text>Inset-list examples</ion-text>\n      </ion-label>\n    </ion-item>\n  </ion-item-group>\n  <ion-item-group>\n    <ion-item><ion-label>First item</ion-label></ion-item>\n  </ion-item-group>\n</ion-list>
\n\n
\n
Preview
\n\n \n \n \n \n

Lists

\n Inset-list examples\n
\n
\n
\n \n First item\n \n
\n
\n\n

Full-width segments

\n

Add .segment-expand when segment buttons should divide the available width evenly. The class also changes the Liquid Glass effect sizing when registerSegmentEffect is used.

\n
<ion-segment class="segment-expand" value="new">\n  <ion-segment-button value="new"><ion-label>New</ion-label></ion-segment-button>\n  <ion-segment-button value="replied"><ion-label>Replied</ion-label></ion-segment-button>\n</ion-segment>
\n\n
\n
Preview
\n\n New\n Replied\n\n
\n\n

Classic search bar in a condense header

\n

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.

\n

Place it in a toolbar with a color, such as color="light"; the classic background is derived from that color's contrast value.

\n

The example uses Ionic's standard collapsible large-title structure. Scroll the preview to collapse the large title and reveal the fixed header.

\n
<div class="ion-page">\n  <ion-header translucent="true">\n    <ion-toolbar color="light">\n      <ion-title>Search</ion-title>\n    </ion-toolbar>\n  </ion-header>\n  <ion-content color="light" fullscreen="true">\n    <ion-header collapse="condense">\n      <ion-toolbar color="light">\n        <ion-title size="large">Search</ion-title>\n      </ion-toolbar>\n      <ion-toolbar color="light">\n        <ion-searchbar class="searchbar-classic" placeholder="Filter results"></ion-searchbar>\n      </ion-toolbar>\n    </ion-header>\n    <ion-list inset="true">\n      <ion-item-group>\n        <ion-item><ion-label>Recent item 1</ion-label></ion-item>\n        <ion-item><ion-label>Recent item 2</ion-label></ion-item>\n        <ion-item><ion-label>Recent item 3</ion-label></ion-item>\n        <ion-item><ion-label>Recent item 4</ion-label></ion-item>\n        <ion-item><ion-label>Recent item 5</ion-label></ion-item>\n        <ion-item><ion-label>Recent item 6</ion-label></ion-item>\n        <ion-item><ion-label>Recent item 7</ion-label></ion-item>\n        <ion-item><ion-label>Recent item 8</ion-label></ion-item>\n        <ion-item><ion-label>Recent item 9</ion-label></ion-item>\n        <ion-item><ion-label>Recent item 10</ion-label></ion-item>\n      </ion-item-group>\n    </ion-list>\n  </ion-content>\n</div>
\n\n
\n
Preview
\n
\n \n \n Search\n \n \n \n \n \n Search\n \n \n \n \n \n \n \n Recent item 1\n Recent item 2\n Recent item 3\n Recent item 4\n Recent item 5\n Recent item 6\n Recent item 7\n Recent item 8\n Recent item 9\n Recent item 10\n \n \n \n
\n
\n\n

The .ion-page wrapper makes this embedded preview behave like a complete routed page. An application using ion-router-outlet normally receives that page container automatically. The inset list and its items only provide enough content to demonstrate scrolling; they are not required by .searchbar-classic.

\n

Search-bar toolbars

\n

Add .toolbar-searchbar when an ion-toolbar combines a search bar with start or end buttons. The class centers the slotted controls and adjusts the spacing around the search field.

\n
<ion-toolbar class="toolbar-searchbar">\n  <ion-buttons slot="start">\n    <ion-button>Cancel</ion-button>\n  </ion-buttons>\n  <ion-searchbar></ion-searchbar>\n</ion-toolbar>
\n\n
\n
Preview
\n\n \n Cancel\n \n \n\n
\n\n

Opting out

\n

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

\n

.ios26-disabled is deprecated but remains supported as an alias with the same behavior. Use .ios-theme-disabled for new code.

\n
<ion-button>iOS 27 theme</ion-button> <ion-button class="ios-theme-disabled">Standard Ionic button</ion-button>
\n\n
\n
Preview
\niOS 27 theme Standard Ionic button\n
\n\n

For the background model behind inset lists, see Using ion-item-group.

\n'; diff --git a/demo/src/app/docs/docs-page.component.html b/demo/src/app/docs/docs-page.component.html index 887cac9a..34166c0d 100644 --- a/demo/src/app/docs/docs-page.component.html +++ b/demo/src/app/docs/docs-page.component.html @@ -2,7 +2,7 @@ Docs - + diff --git a/demo/src/app/index/index-page.component.html b/demo/src/app/index/index-page.component.html index 6d8b340f..2fcf5580 100644 --- a/demo/src/app/index/index-page.component.html +++ b/demo/src/app/index/index-page.component.html @@ -5,7 +5,7 @@
Index - diff --git a/demo/src/app/index/pages/button/button.page.html b/demo/src/app/index/pages/button/button.page.html index c5d926ae..a34ce13f 100644 --- a/demo/src/app/index/pages/button/button.page.html +++ b/demo/src/app/index/pages/button/button.page.html @@ -12,7 +12,7 @@ Example - + Example Example Example @@ -144,7 +144,7 @@

button

buttons > button[fill=default][size=large] Buttons Shadow - Default Theme + Default Theme diff --git a/demo/src/app/index/pages/chip/chip.page.html b/demo/src/app/index/pages/chip/chip.page.html index 01fc479d..200be777 100644 --- a/demo/src/app/index/pages/chip/chip.page.html +++ b/demo/src/app/index/pages/chip/chip.page.html @@ -24,17 +24,17 @@

chip

- Default - Disabled - Outline - + Default + Disabled + Outline + Silhouette of a person's head Avatar Chip - + Icon Chip diff --git a/demo/src/app/index/pages/searchbar/searchbar.page.html b/demo/src/app/index/pages/searchbar/searchbar.page.html index 03e05894..fb901a30 100644 --- a/demo/src/app/index/pages/searchbar/searchbar.page.html +++ b/demo/src/app/index/pages/searchbar/searchbar.page.html @@ -25,10 +25,10 @@

searchbar

- - - - Search + + + + Search diff --git a/demo/src/app/index/pages/segment/segment.page.html b/demo/src/app/index/pages/segment/segment.page.html index c679f335..a0863775 100644 --- a/demo/src/app/index/pages/segment/segment.page.html +++ b/demo/src/app/index/pages/segment/segment.page.html @@ -57,7 +57,7 @@

segment

Segment - +
diff --git a/demo/src/app/settings/settings-page.component.html b/demo/src/app/settings/settings-page.component.html index 6acca9e1..44a3a8e9 100644 --- a/demo/src/app/settings/settings-page.component.html +++ b/demo/src/app/settings/settings-page.component.html @@ -4,8 +4,8 @@ Settings - - + diff --git a/demo/src/global.scss b/demo/src/global.scss index d7bf2204..707cffa2 100644 --- a/demo/src/global.scss +++ b/demo/src/global.scss @@ -37,13 +37,13 @@ @use '@ionic/angular/css/palettes/dark.class.css'; @use '../../src/styles/default-variables'; -@use '../../src/styles/ionic-theme-ios26'; -@use '../../src/styles/ionic-theme-ios26-dark-class'; +@use '../../src/styles/ionic-theme-ios27'; +@use '../../src/styles/ionic-theme-ios27-dark-class'; @use '../../src/styles/md-ion-list-inset'; @use '../../src/styles/md-remove-ios-class-effect'; :root { - --ios26-menu-width: 300px; + --ios-theme-menu-width: 300px; } /** @@ -189,7 +189,7 @@ code { } ion-content { - --ios26-content-box-shadow-rgb: var(--ion-color-base-rgb, var(--ion-background-color-rgb, 255, 255, 255)); + --ios-theme-content-box-shadow-rgb: var(--ion-color-base-rgb, var(--ion-background-color-rgb, 255, 255, 255)); } #main-content { diff --git a/demo/src/index.html b/demo/src/index.html index 19bf77f9..08708166 100644 --- a/demo/src/index.html +++ b/demo/src/index.html @@ -2,7 +2,7 @@ - Demo for ionic-theme-ios26 + Demo for ionic-theme-ios27 @@ -54,7 +54,7 @@ Resolved. ### should disable ion-back-button Animation -Resolved: by created https://github.com/rdlabo-dev/ionic-theme-ios26/tree/main/src/transition +Resolved: by created https://github.com/rdlabo-dev/ionic-theme-ios27/tree/main/src/transition ## feat(): ion-content[fullscreen=true] will have .content-fullscreen class diff --git a/docs/ios-18.md b/docs/ios-18.md deleted file mode 100644 index bd61b8e9..00000000 --- a/docs/ios-18.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -title: Supporting iOS 18 ---- - -# Supporting iOS 18 - -## How to prevent loading the theme on iOS 18 - -If you want to load a theme file only when the user's device is running iOS 26 (and let users on iOS 18 use the default Ionic iOS theme), you can achieve this by adding a supports-condition to your `import`. - -```css -@import '@rdlabo/ionic-theme-ios27/dist/css/default-variables.css' supports(text-wrap: pretty); -@import '@rdlabo/ionic-theme-ios27/dist/css/ionic-theme-ios26.css' supports(text-wrap: pretty); -@import '@rdlabo/ionic-theme-ios27/dist/css/md-remove-ios-class-effect.css' supports(text-wrap: pretty); -@import '@rdlabo/ionic-theme-ios27/dist/css/md-ion-list-inset.css' supports(text-wrap: pretty); -``` diff --git a/docs/ios-adaptive.md b/docs/ios-adaptive.md new file mode 100644 index 00000000..45f3325a --- /dev/null +++ b/docs/ios-adaptive.md @@ -0,0 +1,90 @@ +--- +title: Adaptive iOS themes +--- + +# Adaptive iOS themes + +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/). + +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. + +## Load only the iOS 27 theme + +In your global Sass stylesheet, use `meta.load-css` inside the feature query. Unlike `@use`, it can emit the theme inside `@supports`. + +```scss +@use 'sass:meta'; + +@supports selector(:heading) { + @include meta.load-css('@rdlabo/ionic-theme-ios27/src/styles/default-variables'); + @include meta.load-css('@rdlabo/ionic-theme-ios27/src/styles/ionic-theme-ios27'); + @include meta.load-css('@rdlabo/ionic-theme-ios27/src/styles/ionic-theme-ios27-dark-class'); + @include meta.load-css('@rdlabo/ionic-theme-ios27/src/styles/md-remove-ios-class-effect'); +} +``` + +## Use the iOS 26 and iOS 27 themes together + +Install both packages: + +```bash +npm install @rdlabo/ionic-theme-ios26 @rdlabo/ionic-theme-ios27 +``` + +Use this global Sass configuration instead of unconditional theme imports: + +```scss +@use 'sass:meta'; + +@supports selector(:heading) { + @include meta.load-css('@rdlabo/ionic-theme-ios27/src/styles/default-variables'); + @include meta.load-css('@rdlabo/ionic-theme-ios27/src/styles/ionic-theme-ios27'); + @include meta.load-css('@rdlabo/ionic-theme-ios27/src/styles/ionic-theme-ios27-dark-class'); + @include meta.load-css('@rdlabo/ionic-theme-ios27/src/styles/md-remove-ios-class-effect'); +} + +@supports (text-wrap: pretty) and (not selector(:heading)) { + @include meta.load-css('@rdlabo/ionic-theme-ios26/src/styles/default-variables'); + @include meta.load-css('@rdlabo/ionic-theme-ios26/src/styles/ionic-theme-ios26'); + @include meta.load-css('@rdlabo/ionic-theme-ios26/src/styles/ionic-theme-ios26-dark-class'); + @include meta.load-css('@rdlabo/ionic-theme-ios26/src/styles/md-remove-ios-class-effect'); +} +``` + +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. + +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. + +## Select matching JavaScript animations + +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. + +```ts +async function loadIOSAnimations() { + if (typeof CSS === 'undefined') { + return {}; + } + + const theme = CSS.supports('selector(:heading)') + ? await import('@rdlabo/ionic-theme-ios27') + : CSS.supports('text-wrap: pretty') + ? await import('@rdlabo/ionic-theme-ios26') + : undefined; + + if (!theme) { + return {}; + } + + return { + navAnimation: theme.iosTransitionAnimation, + popoverEnter: theme.popoverEnterAnimation, + popoverLeave: theme.popoverLeaveAnimation, + }; +} + +// In your browser bootstrap, before initializing Ionic: +const animations = isPlatform('ios') ? await loadIOSAnimations() : {}; +provideIonicAngular({ ...animations }); +``` + +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. diff --git a/docs/migration.md b/docs/migration.md index e0e32f2b..8d9b5935 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -4,91 +4,14 @@ title: Migration # Migration -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. +## iOS 27 naming -Each section lists only the changes that require application code or configuration updates. +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. -## Migrating to 9.0.0 +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`. -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. +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. -Both Ionic 8 and Ionic 9 remain supported. Version 9 requires `@ionic/core >=8.8.1 <10`. +See [Special markup and classes](./special-markup.md) and [Default variables](../src/styles/default-variables.scss) for the current names. -## Migrating to 3.0.0 - -### Rename `.header-item-group` to `.item-group-header` - -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. - -```diff -- -+ - ... - -``` - -The old class is no longer styled by the theme. This rename applies to markup shared with `@rdlabo/ionic-theme-md3` as well. - -## Migrating to 2.0.0 - -### Configure `iosTransitionAnimation` - -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. - -```ts -import { isPlatform } from '@ionic/core'; // or @ionic/angular/standalone, @ionic/react, @ionic/vue -import { iosTransitionAnimation } from '@rdlabo/ionic-theme-ios26'; - -// Angular -provideIonicAngular({ - // ... - navAnimation: isPlatform('ios') ? iosTransitionAnimation : undefined, -}); - -// React -setupIonicReact({ - // ... - navAnimation: isPlatform('ios') ? iosTransitionAnimation : undefined, -}); - -// Vue -createApp(App).use(IonicVue, { - // ... - navAnimation: isPlatform('ios') ? iosTransitionAnimation : undefined, -}); -``` - -With this transition configured, `` can be used without the unwanted transition side effects caused by the old animation. - -## Migrating to 1.0.0 - -### Update SCSS import paths - -The source files moved under `src/styles` when JavaScript files were added to the package. - -```diff -- @import '@rdlabo/ionic-theme-ios26/src/default-variables.scss'; -+ @import '@rdlabo/ionic-theme-ios26/src/styles/default-variables.scss'; -``` - -Generated CSS paths under `dist` did not change. - -### Rename `--ios26-color-background-rgb` - -```diff - :root { -- --ios26-color-background-rgb: 255, 255, 255; -+ --ios26-content-box-shadow-rgb: 255, 255, 255; - } -``` - -### Rename brightness variables - -Replace each `--ion-color-*-brightness-rgb` variable with `--ion-color-*-brightness` and use a color value instead of an RGB channel list. - -```diff - :root { -- --ion-color-primary-brightness-rgb: 130, 255, 255; -+ --ion-color-primary-brightness: #96feff; - } -``` +Earlier migration notes are preserved on the [previous theme branch](https://github.com/rdlabo-dev/ionic-theme-ios27/blob/ios26/docs/migration.md). diff --git a/docs/special-markup.md b/docs/special-markup.md index 3d146c03..49a1c63d 100644 --- a/docs/special-markup.md +++ b/docs/special-markup.md @@ -79,7 +79,7 @@ Add `.segment-expand` when segment buttons should divide the available width eve ## Classic search bar in a condense header -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. +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. Place it in a toolbar with a color, such as `color="light"`; the classic background is derived from that color's contrast value. @@ -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. + +`.ios26-disabled` is deprecated but remains supported as an alias with the same behavior. Use `.ios-theme-disabled` for new code. ```html preview -iOS 26 theme Standard Ionic button +iOS 27 theme Standard Ionic button ``` For the background model behind inset lists, see [Using `ion-item-group`](./using-ion-item-group.md). diff --git a/docs/using-ion-item-group.md b/docs/using-ion-item-group.md index 4ae58088..ed9d732f 100644 --- a/docs/using-ion-item-group.md +++ b/docs/using-ion-item-group.md @@ -24,9 +24,9 @@ No wrapper is required for lists that do not use `inset="true"`. ## Why the wrapper is required -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. +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. -![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) +![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) The theme therefore: diff --git a/package.json b/package.json index 4ec5a42a..bd056e51 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "bugs": { "url": "https://github.com/rdlabo-dev/ionic-theme-ios27/issues" }, - "homepage": "https://docs.rdlabo.dev/projects/ionic-theme-ios26", + "homepage": "https://docs.rdlabo.dev/projects/ionic-theme-ios27", "devDependencies": { "@ionic/angular": "^9.0.0", "husky": "^9.1.7", diff --git a/screenshots/ios26.png b/screenshots/legacy-theme.png similarity index 100% rename from screenshots/ios26.png rename to screenshots/legacy-theme.png diff --git a/src/popover/animations/ios.enter.ts b/src/popover/animations/ios.enter.ts index 18721b5e..709e76e0 100644 --- a/src/popover/animations/ios.enter.ts +++ b/src/popover/animations/ios.enter.ts @@ -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; @@ -102,7 +102,7 @@ export const iosEnterAnimation = (baseEl: HTMLElement, opts?: any): Animation => .duration(200) .addElement(referenceSizeEl) .beforeStyles({ 'transform-origin': `${originY} ${originX}` }) - .beforeAddClass('ios26-replace-element') + .beforeAddClass('ios27-replace-element') .fromTo('transform', 'scale(1)', 'scale(1.05)') .fromTo('opacity', 1, 0); } diff --git a/src/popover/animations/ios.leave.ts b/src/popover/animations/ios.leave.ts index e71b7a92..ec04d218 100644 --- a/src/popover/animations/ios.leave.ts +++ b/src/popover/animations/ios.leave.ts @@ -17,7 +17,7 @@ export const iosLeaveAnimation = (baseEl: HTMLElement): Animation => { const targetAnimation = createAnimation(); const doc = baseEl.ownerDocument as any; - const replaceElement = doc.querySelector('.ios26-replace-element') as HTMLElement | null; + const replaceElement = doc.querySelector('.ios27-replace-element') as HTMLElement | null; if (replaceElement) { const ratio = contentEl.getBoundingClientRect().width / contentEl.getBoundingClientRect().height; @@ -27,7 +27,7 @@ export const iosLeaveAnimation = (baseEl: HTMLElement): Animation => { .addElement(replaceElement) .delay(100) .duration(300) - .afterRemoveClass('ios26-replace-element') + .afterRemoveClass('ios27-replace-element') .fromTo('transform', `scale(${scale})`, 'scale(1)') .fromTo('opacity', 0, 0.9); } diff --git a/src/sheets-of-glass/index.ts b/src/sheets-of-glass/index.ts index 18d180fe..fdcac39e 100644 --- a/src/sheets-of-glass/index.ts +++ b/src/sheets-of-glass/index.ts @@ -4,8 +4,8 @@ import type { Animation, Gesture, GestureDetail } from '@ionic/core'; import { changeSelectedElement, cloneElement, getStep } from '../utils'; import { createMoveAnimation, createPreMoveAnimation, getMoveAnimationKeyframe, getScaleAnimation } from './animations'; -const GESTURE_NAME = 'ios26-enable-gesture'; -const ANIMATED_NAME = 'ios26-animated'; +const GESTURE_NAME = 'ios27-enable-gesture'; +const ANIMATED_NAME = 'ios27-animated'; export const registerEffect = ( targetElement: HTMLElement, diff --git a/src/styles/components/ion-action-sheet.scss b/src/styles/components/ion-action-sheet.scss index e0ed4629..6e144545 100644 --- a/src/styles/components/ion-action-sheet.scss +++ b/src/styles/components/ion-action-sheet.scss @@ -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 { @@ -71,10 +71,10 @@ ion-action-sheet.ios:not(.ios26-disabled) { /** * For ion-select[interface=action-sheet]. - * ios26, action-sheet should not has 'cancel'. + * ios27, 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 { diff --git a/src/styles/components/ion-alert.scss b/src/styles/components/ion-alert.scss index 07d23683..1fc4cddc 100644 --- a/src/styles/components/ion-alert.scss +++ b/src/styles/components/ion-alert.scss @@ -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; diff --git a/src/styles/components/ion-button.scss b/src/styles/components/ion-button.scss index 0dde1f38..cb74a60d 100644 --- a/src/styles/components/ion-button.scss +++ b/src/styles/components/ion-button.scss @@ -23,7 +23,7 @@ $scaleup-large-icon-only: api.$button-scaleup-large-icon-only; /** * 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; @@ -59,11 +59,11 @@ $scaleup-large-icon-only: api.$button-scaleup-large-icon-only; } } - &: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; @@ -121,11 +121,11 @@ $scaleup-large-icon-only: api.$button-scaleup-large-icon-only; } } - 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; @@ -140,7 +140,7 @@ $scaleup-large-icon-only: api.$button-scaleup-large-icon-only; } 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) { @@ -153,13 +153,13 @@ $scaleup-large-icon-only: api.$button-scaleup-large-icon-only; } // 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; } @@ -248,8 +248,8 @@ $scaleup-large-icon-only: api.$button-scaleup-large-icon-only; } } @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: ''; @@ -313,13 +313,13 @@ $scaleup-large-icon-only: api.$button-scaleup-large-icon-only; // 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.5px 1px 0 rgba(var(--ios26-glass-box-shadow-color-rgb), 0.08); + 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.5px 1px 0 rgba(var(--ios-theme-glass-box-shadow-color-rgb, var(--ios26-glass-box-shadow-color-rgb)), 0.08); } &: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; } } -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 { @@ -435,9 +439,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); } diff --git a/src/styles/components/ion-card.scss b/src/styles/components/ion-card.scss index e6510259..ee549c5c 100644 --- a/src/styles/components/ion-card.scss +++ b/src/styles/components/ion-card.scss @@ -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 { diff --git a/src/styles/components/ion-content.scss b/src/styles/components/ion-content.scss index f5ea5d14..39a0e7c7 100644 --- a/src/styles/components/ion-content.scss +++ b/src/styles/components/ion-content.scss @@ -1,10 +1,10 @@ /** - * Since iOS26, content is now displayed to the full extent of the top and bottom safe areas, + * Since iOS27, content is now displayed to the full extent of the top and bottom safe areas, * 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)); @@ -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))); } diff --git a/src/styles/components/ion-datetime.scss b/src/styles/components/ion-datetime.scss index 069a16a1..2ede64da 100644 --- a/src/styles/components/ion-datetime.scss +++ b/src/styles/components/ion-datetime.scss @@ -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; diff --git a/src/styles/components/ion-fab.scss b/src/styles/components/ion-fab.scss index 01b15dc7..0ec08abc 100644 --- a/src/styles/components/ion-fab.scss +++ b/src/styles/components/ion-fab.scss @@ -4,19 +4,21 @@ $scaleup-small-icon-only: api.$button-scaleup-small-icon-only; $scaleup-default-icon-only: api.$button-scaleup-default-icon-only; $scaleup-large-icon-only: api.$button-scaleup-large-icon-only; -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.5px 1px 0 rgba(var(--ios26-glass-box-shadow-color-rgb), 0.08); + 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.5px 1px 0 rgba(var(--ios-theme-glass-box-shadow-color-rgb, var(--ios26-glass-box-shadow-color-rgb)), 0.08); --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; @@ -77,15 +79,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 { @@ -94,7 +96,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 { diff --git a/src/styles/components/ion-inputs.scss b/src/styles/components/ion-inputs.scss index 06f8e6cb..a01b75db 100644 --- a/src/styles/components/ion-inputs.scss +++ b/src/styles/components/ion-inputs.scss @@ -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'); @@ -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'); diff --git a/src/styles/components/ion-list.scss b/src/styles/components/ion-list.scss index f71def38..2b457e81 100644 --- a/src/styles/components/ion-list.scss +++ b/src/styles/components/ion-list.scss @@ -1,6 +1,6 @@ @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; @@ -8,7 +8,7 @@ ion-list.ios:not(.ios26-disabled) { } } -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, diff --git a/src/styles/components/ion-loading.scss b/src/styles/components/ion-loading.scss index c7e907a2..f0adbccd 100644 --- a/src/styles/components/ion-loading.scss +++ b/src/styles/components/ion-loading.scss @@ -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; diff --git a/src/styles/components/ion-menu.scss b/src/styles/components/ion-menu.scss index 5fe03eec..aa4683b0 100644 --- a/src/styles/components/ion-menu.scss +++ b/src/styles/components/ion-menu.scss @@ -1,20 +1,23 @@ @use '../utils/api'; -ion-split-pane.ios:not(.ios26-disabled) { - --side-max-width: var(--ios26-menu-width); +ion-split-pane.ios:not(.ios-theme-disabled, .ios26-disabled) { + --side-max-width: var(--ios-theme-menu-width, var(--ios26-menu-width)); } -ion-split-pane.ios:not(.ios26-disabled).split-pane-visible:has(ion-menu.menu-pane-visible) { +ion-split-pane.ios:not(.ios-theme-disabled, .ios26-disabled).split-pane-visible:has(ion-menu.menu-pane-visible) { .split-pane-main { - ion-tab-bar.ios:not(.ios26-disabled) { + ion-tab-bar.ios:not(.ios-theme-disabled, .ios26-disabled) { width: calc( 100% - calc(18px + var(--ion-safe-area-left, 0px)) - calc(18px + var(--ion-safe-area-left, 0px)) - 60px - - 12px - var(--ios26-menu-width) + 12px - var(--ios-theme-menu-width, var(--ios26-menu-width)) ); &:has(:nth-child(5)) { width: calc( - 100% - calc(18px + var(--ion-safe-area-left, 0px)) - calc(18px + var(--ion-safe-area-left, 0px)) - var(--ios26-menu-width) + 100% - calc(18px + var(--ion-safe-area-left, 0px)) - calc(18px + var(--ion-safe-area-left, 0px)) - var( + --ios-theme-menu-width, + var(--ios26-menu-width) + ) ); } } @@ -25,62 +28,62 @@ ion-split-pane.ios:not(.ios26-disabled).split-pane-visible:has(ion-menu.menu-pan /* * 4px: https://github.com/ionic-team/ionic-framework/blob/main/core/src/components/toolbar/toolbar.ios.vars.scss#L28-L32 */ - --padding-start: calc(var(--ios26-menu-width) + 4px); + --padding-start: calc(var(--ios-theme-menu-width, var(--ios26-menu-width)) + 4px); ion-title.title-default { /* * 90px: https://github.com/ionic-team/ionic-framework/blob/main/core/src/components/title/title.ios.scss#L8 */ - padding-left: calc(90px + var(--ios26-menu-width)); + padding-left: calc(90px + var(--ios-theme-menu-width, var(--ios26-menu-width))); } } ion-content { - --padding-start: var(--ios26-menu-width); + --padding-start: var(--ios-theme-menu-width, var(--ios26-menu-width)); ion-header ion-toolbar { --padding-start: 4px; } } ion-footer ion-toolbar { - --padding-start: var(--ios26-menu-width); + --padding-start: var(--ios-theme-menu-width, var(--ios26-menu-width)); } - ion-tab-bar.ios:not(.ios26-disabled) { + ion-tab-bar.ios:not(.ios-theme-disabled, .ios26-disabled) { &[slot='bottom'] { - left: calc(16px + var(--ion-safe-area-left, 0px) + var(--ios26-menu-width)); + left: calc(16px + var(--ion-safe-area-left, 0px) + var(--ios-theme-menu-width, var(--ios26-menu-width))); } } } } - &:has(ion-menu.ios:not(.ios26-disabled).menu-side-end) { + &:has(ion-menu.ios:not(.ios-theme-disabled, .ios26-disabled).menu-side-end) { .split-pane-main { ion-header ion-toolbar { /* * 4px: https://github.com/ionic-team/ionic-framework/blob/main/core/src/components/toolbar/toolbar.ios.vars.scss#L28-L32 */ - --padding-end: calc(var(--ios26-menu-width) + 4px); + --padding-end: calc(var(--ios-theme-menu-width, var(--ios26-menu-width)) + 4px); ion-title.title-default { /* * 90px: https://github.com/ionic-team/ionic-framework/blob/main/core/src/components/title/title.ios.scss#L8 */ - padding-right: calc(90px + var(--ios26-menu-width)); + padding-right: calc(90px + var(--ios-theme-menu-width, var(--ios26-menu-width))); } } ion-content { - --padding-end: var(--ios26-menu-width); + --padding-end: var(--ios-theme-menu-width, var(--ios26-menu-width)); ion-header ion-toolbar { --padding-start: 4px; } } ion-footer ion-toolbar { - --padding-end: var(--ios26-menu-width); + --padding-end: var(--ios-theme-menu-width, var(--ios26-menu-width)); } } } } -ion-menu.ios:not(.ios26-disabled) { +ion-menu.ios:not(.ios-theme-disabled, .ios26-disabled) { --border: none; --background: transparent; - --max-width: var(--ios26-menu-width); + --max-width: var(--ios-theme-menu-width, var(--ios26-menu-width)); position: absolute; top: 0; z-index: 999; @@ -89,7 +92,7 @@ ion-menu.ios:not(.ios26-disabled) { left: 0; } &.menu-side-end { - left: calc(100vw - var(--ios26-menu-width)); + left: calc(100vw - var(--ios-theme-menu-width, var(--ios26-menu-width))); } &::part(backdrop) { @@ -99,10 +102,12 @@ ion-menu.ios:not(.ios26-disabled) { ion-content { --background: transparent; --ios26-content-box-shadow-rgb: transparent; + --ios-theme-content-box-shadow-rgb: var(--ios26-content-box-shadow-rgb); &::part(scroll) { @include api.glass-background; - margin: calc(var(--ios26-floating-safe-area-top) + 6px) calc(16px + var(--ion-safe-area-right)) var(--ios26-floating-safe-area-bottom) + margin: calc(var(--ios-theme-floating-safe-area-top, var(--ios26-floating-safe-area-top)) + 6px) + calc(16px + var(--ion-safe-area-right)) var(--ios-theme-floating-safe-area-bottom, var(--ios26-floating-safe-area-bottom)) calc(12px + var(--ion-safe-area-left)); border-radius: 24px; } diff --git a/src/styles/components/ion-modal.scss b/src/styles/components/ion-modal.scss index e4519b25..43205483 100644 --- a/src/styles/components/ion-modal.scss +++ b/src/styles/components/ion-modal.scss @@ -1,6 +1,6 @@ @use '../utils/api'; -ion-modal.ios:not(.ios26-disabled) { +ion-modal.ios:not(.ios-theme-disabled, .ios26-disabled) { --backdrop-opacity: 0.2; &::part(handle) { @@ -22,7 +22,7 @@ ion-modal.ios:not(.ios26-disabled) { &.modal-card { /** * IonicでCardModalのborder-radiusはハードコーディングされています。 - * また、iOS26のドキュメントで見つからないため、サポートは行いません。 + * また、iOS27のドキュメントで見つからないため、サポートは行いません。 * https://github.com/ionic-team/ionic-framework/blob/3b80473f2fd5ad4da5a9f5d66f783a69909c8965/core/src/components/modal/animations/ios.enter.ts#L82-L83 */ } @@ -34,7 +34,7 @@ ion-modal.ios:not(.ios26-disabled) { &.modal-sheet { --border-radius: 30px; - --background: rgba(var(--ios26-glass-background-rgb), 1); + --background: rgba(var(--ios-theme-glass-background-rgb, var(--ios26-glass-background-rgb)), 1); &::part(content) { @include api.glass-background($opacity: 1, $include-background: false); } diff --git a/src/styles/components/ion-picker.scss b/src/styles/components/ion-picker.scss index 70d07925..60b5f614 100644 --- a/src/styles/components/ion-picker.scss +++ b/src/styles/components/ion-picker.scss @@ -1,4 +1,4 @@ @use '../utils/api'; -ion-picker.ios:not(.ios26-disabled) { +ion-picker.ios:not(.ios-theme-disabled, .ios26-disabled) { } diff --git a/src/styles/components/ion-popover.scss b/src/styles/components/ion-popover.scss index 2c36974b..ff287872 100644 --- a/src/styles/components/ion-popover.scss +++ b/src/styles/components/ion-popover.scss @@ -1,10 +1,11 @@ @use '../utils/api'; -ion-popover.ios:not(.ios26-disabled) { +ion-popover.ios:not(.ios-theme-disabled, .ios26-disabled) { --backdrop-opacity: 0.2; - --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); &::part(arrow) { display: none; ::after { diff --git a/src/styles/components/ion-range.scss b/src/styles/components/ion-range.scss index 43658cdc..823ea48f 100644 --- a/src/styles/components/ion-range.scss +++ b/src/styles/components/ion-range.scss @@ -5,7 +5,7 @@ -webkit-transform: scale(1.56, 1.47) translateX(calc(#{$translate-x} * -0.1)) translateZ(0); } -ion-range.ios:not(.ios26-disabled) { +ion-range.ios:not(.ios-theme-disabled, .ios26-disabled) { --knob-size: 20px; --knob-width: 38px; --knob-border-radius: 24px; @@ -36,7 +36,7 @@ ion-range.ios:not(.ios26-disabled) { } &:not(.range-dual-knobs).range-pressed { - --knob-background: rgba(var(--ios26-glass-background-rgb), 0.1); + --knob-background: rgba(var(--ios-theme-glass-background-rgb, var(--ios26-glass-background-rgb)), 0.1); --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); &::part(knob) { diff --git a/src/styles/components/ion-searchbar.scss b/src/styles/components/ion-searchbar.scss index bc8a97d3..4f0c0f0f 100644 --- a/src/styles/components/ion-searchbar.scss +++ b/src/styles/components/ion-searchbar.scss @@ -1,7 +1,7 @@ @use '../utils/api'; ion-modal.ios { - ion-searchbar:not(.ios26-disabled) { + ion-searchbar:not(.ios-theme-disabled, .ios26-disabled) { .searchbar-input-container { input.searchbar-input { // Modal表示でのアイコン非表示の不具合の対応 @@ -11,7 +11,7 @@ ion-modal.ios { } } -ion-searchbar.ios:not(.ios26-disabled):not(.searchbar-classic) { +ion-searchbar.ios:not(.ios-theme-disabled, .ios26-disabled):not(.searchbar-classic) { min-height: 60px; padding: 0; @@ -45,10 +45,12 @@ ion-searchbar.ios:not(.ios26-disabled):not(.searchbar-classic) { /** * add searchbar-classic */ -ion-toolbar.ios:not(.ios26-disabled):has(ion-searchbar.searchbar-classic.ios:not(.ios26-disabled)) { +ion-toolbar.ios:not(.ios-theme-disabled, .ios26-disabled):has( + ion-searchbar.searchbar-classic.ios:not(.ios-theme-disabled, .ios26-disabled) + ) { --min-height: auto; } -ion-searchbar.ios:not(.ios26-disabled).searchbar-classic { +ion-searchbar.ios:not(.ios-theme-disabled, .ios26-disabled).searchbar-classic { padding-bottom: 12px; min-height: auto; .searchbar-input-container input.searchbar-input { diff --git a/src/styles/components/ion-segment.scss b/src/styles/components/ion-segment.scss index 6e0b8e9a..cdfa9f71 100644 --- a/src/styles/components/ion-segment.scss +++ b/src/styles/components/ion-segment.scss @@ -1,11 +1,11 @@ @use '../utils/api'; -ion-segment.ios:not(.ios26-disabled) { +ion-segment.ios:not(.ios-theme-disabled, .ios26-disabled) { @include api.glass-background; min-height: 48px; border-radius: 25px; - transition: transform var(--ios26-activated-transition-duration) ease-out; + transition: transform var(--ios-theme-activated-transition-duration, var(--ios26-activated-transition-duration)) ease-out; // will-change is optimized to only apply when needed for performance &.segment-activated, @@ -28,7 +28,7 @@ ion-segment.ios:not(.ios26-disabled) { } } - &:not(.ios26-enable-gesture).segment-activated { + &:not(.ios27-enable-gesture).segment-activated { transform: scale(1.1) translateZ(0); -webkit-transform: scale(1.1) translateZ(0); ion-segment-button { @@ -47,7 +47,7 @@ ion-segment.ios:not(.ios26-disabled) { } } - &.ios26-enable-gesture { + &.ios27-enable-gesture { &:has(ion-segment-button.ion-activated) { transform: scale(1.1) translateZ(0); -webkit-transform: scale(1.1) translateZ(0); @@ -65,7 +65,7 @@ ion-segment.ios:not(.ios26-disabled) { } } } - &.ios26-animated { + &.ios27-animated { ion-segment-button { --indicator-color: rgba(var(--ion-text-color-rgb, 0, 0, 0), 0); } @@ -87,7 +87,7 @@ ion-segment.ios:not(.ios26-disabled) { min-height: 24px; } - &.ios26-enable-gesture { + &.ios27-enable-gesture { &:has(ion-segment-button.ion-activated) { transform: scale(1) translateZ(0); -webkit-transform: scale(1) translateZ(0); @@ -105,7 +105,7 @@ ion-segment.ios:not(.ios26-disabled) { } } } - &.ios26-animated { + &.ios27-animated { ion-segment-button { --indicator-color: rgba(var(--ion-text-color-rgb, 0, 0, 0), 0); } @@ -114,7 +114,7 @@ ion-segment.ios:not(.ios26-disabled) { } } -ion-segment-button.ios:not(.ios26-disabled) { +ion-segment-button.ios:not(.ios-theme-disabled, .ios26-disabled) { --border-width: 0; --indicator-box-shadow: none; --indicator-color: rgba(var(--ion-text-color-rgb, 0, 0, 0), 0.06); diff --git a/src/styles/components/ion-tabs.scss b/src/styles/components/ion-tabs.scss index 5b24d814..93b09d69 100644 --- a/src/styles/components/ion-tabs.scss +++ b/src/styles/components/ion-tabs.scss @@ -1,6 +1,6 @@ @use '../utils/api'; -ion-tab-bar.ios:not(.ios26-disabled) { +ion-tab-bar.ios:not(.ios-theme-disabled, .ios26-disabled) { @include api.glass-background; z-index: 2; @@ -9,13 +9,13 @@ ion-tab-bar.ios:not(.ios26-disabled) { &[slot='top'] { position: absolute; - top: var(--ios26-floating-safe-area-top); + top: var(--ios-theme-floating-safe-area-top, var(--ios26-floating-safe-area-top)); right: calc(16px + var(--ion-safe-area-right, 0px)); } &[slot='bottom'] { position: absolute; - bottom: var(--ios26-floating-safe-area-bottom); + bottom: var(--ios-theme-floating-safe-area-bottom, var(--ios26-floating-safe-area-bottom)); left: calc(16px + var(--ion-safe-area-left, 0px)); } @@ -31,7 +31,7 @@ ion-tab-bar.ios:not(.ios26-disabled) { } padding: 2px 2px; - transition: transform var(--ios26-activated-transition-duration) ease-out; + transition: transform var(--ios-theme-activated-transition-duration, var(--ios26-activated-transition-duration)) ease-out; // will-change is optimized to only apply when needed for performance &:has(ion-tab-button.ion-activated) { @@ -53,9 +53,9 @@ ion-tab-bar.ios:not(.ios26-disabled) { /** * effectが有効な場合、effectでスタイリングするため不要 */ - &.ios26-enable-gesture.ios26-animated { + &.ios27-enable-gesture.ios27-animated { ion-tab-button.tab-selected::part(native) { - background: rgba(var(--ios26-button-color-selected-rgb), 0); + background: rgba(var(--ios-theme-button-color-selected-rgb, var(--ios26-button-color-selected-rgb)), 0); } ion-tab-button.ion-activated { ion-label, @@ -66,19 +66,19 @@ ion-tab-bar.ios:not(.ios26-disabled) { } } -ion-tab-button.ios:not(.ios26-disabled) { +ion-tab-button.ios:not(.ios-theme-disabled, .ios26-disabled) { ion-icon { font-size: 26px; } ion-label, ion-icon { transition: - filter var(--ios26-activated-transition-duration) ease, - color var(--ios26-activated-transition-duration) ease; + filter var(--ios-theme-activated-transition-duration, var(--ios26-activated-transition-duration)) ease, + color var(--ios-theme-activated-transition-duration, var(--ios26-activated-transition-duration)) ease; } - background: rgba(var(--ios26-glass-background-rgb), 0); + background: rgba(var(--ios-theme-glass-background-rgb, var(--ios26-glass-background-rgb)), 0); height: auto; - transition: transform var(--ios26-activated-transition-duration) ease; + transition: transform var(--ios-theme-activated-transition-duration, var(--ios26-activated-transition-duration)) ease; &::part(native) { overflow: visible; min-height: 56px; @@ -97,8 +97,8 @@ ion-tab-button.ios:not(.ios26-disabled) { &.tab-selected { &::part(native) { - background: rgba(var(--ios26-button-color-selected-rgb), 0.095); - transition: background var(--ios26-activated-transition-duration) ease; + background: rgba(var(--ios-theme-button-color-selected-rgb, var(--ios26-button-color-selected-rgb)), 0.095); + transition: background var(--ios-theme-activated-transition-duration, var(--ios26-activated-transition-duration)) ease; } } diff --git a/src/styles/components/ion-toast.scss b/src/styles/components/ion-toast.scss index e78ab596..ba23f95b 100644 --- a/src/styles/components/ion-toast.scss +++ b/src/styles/components/ion-toast.scss @@ -1,21 +1,22 @@ @use '../utils/api'; -ion-toast.ios:not(.ios26-disabled) { - --background: rgba(var(--ios26-glass-background-rgb), 0.72); +ion-toast.ios:not(.ios-theme-disabled, .ios26-disabled) { + --background: rgba(var(--ios-theme-glass-background-rgb, var(--ios26-glass-background-rgb)), 0.72); --border-radius: 24px; --border-style: solid; --border-width: 0.5px; --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); --start: 16px; --end: 16px; &::part(wrapper) { @include api.glass-background($include-background: false, $include-box-shadow: false, $include-border: false); - border-top-color: rgba(var(--ios26-glass-border-color-rgb), 1); - border-right-color: rgba(var(--ios26-glass-border-color-rgb), 0.8); - border-bottom-color: rgba(var(--ios26-glass-border-color-rgb), 1); - border-left-color: rgba(var(--ios26-glass-border-color-rgb), 0.6); + border-top-color: rgba(var(--ios-theme-glass-border-color-rgb, var(--ios26-glass-border-color-rgb)), 1); + border-right-color: rgba(var(--ios-theme-glass-border-color-rgb, var(--ios26-glass-border-color-rgb)), 0.8); + border-bottom-color: rgba(var(--ios-theme-glass-border-color-rgb, var(--ios26-glass-border-color-rgb)), 1); + border-left-color: rgba(var(--ios-theme-glass-border-color-rgb, var(--ios26-glass-border-color-rgb)), 0.6); } &.ion-color { diff --git a/src/styles/components/ion-toggle.scss b/src/styles/components/ion-toggle.scss index 18508905..4560b713 100644 --- a/src/styles/components/ion-toggle.scss +++ b/src/styles/components/ion-toggle.scss @@ -4,7 +4,7 @@ * Adjustments have been made to prevent the right edge from being cut off within the ion-item. * When the native-inner part be added to the ion-item, it will be removed. */ -ion-item.ios:not(.ios26-disabled) ion-toggle.ios:not(.ios26-disabled) { +ion-item.ios:not(.ios-theme-disabled, .ios26-disabled) ion-toggle.ios:not(.ios-theme-disabled, .ios26-disabled) { &.toggle-checked.toggle-activated::part(track) { transform-origin: left; transform: scaleX(0.894) translateZ(0); @@ -16,7 +16,7 @@ ion-item.ios:not(.ios26-disabled) ion-toggle.ios:not(.ios26-disabled) { } } -ion-toggle.ios:not(.ios26-disabled) { +ion-toggle.ios:not(.ios-theme-disabled, .ios26-disabled) { --handle-width: 38px; --handle-height: 24px; --handle-max-width: none; @@ -48,8 +48,8 @@ ion-toggle.ios:not(.ios26-disabled) { @include api.glass-background($opacity: 0.1, $blur: 0.5px, $saturate: 120%, $include-background: false, $include-box-shadow: false); } &.toggle-activated { - --handle-background: rgba(var(--ios26-glass-background-rgb), 0.1); - --handle-background-checked: rgba(var(--ios26-glass-background-rgb), 0.1); + --handle-background: rgba(var(--ios-theme-glass-background-rgb, var(--ios26-glass-background-rgb)), 0.1); + --handle-background-checked: rgba(var(--ios-theme-glass-background-rgb, var(--ios26-glass-background-rgb)), 0.1); --handle-box-shadow: inset 0 0 8px 0 var(--track-background); } &.ion-color.toggle-checked.toggle-activated { diff --git a/src/styles/components/ion-toolbar.scss b/src/styles/components/ion-toolbar.scss index 4b6f80e7..d2878602 100644 --- a/src/styles/components/ion-toolbar.scss +++ b/src/styles/components/ion-toolbar.scss @@ -6,34 +6,35 @@ * padding-top: var(--ion-safe-area-top, 0); * } */ -ion-header.ios:not(.ios26-disabled) ion-toolbar:not(.ios26-disabled):first-of-type { +ion-header.ios:not(.ios-theme-disabled, .ios26-disabled) ion-toolbar:not(.ios-theme-disabled, .ios26-disabled):first-of-type { padding-top: max(calc(var(--ion-safe-area-top) - 12px), 4px); } -ion-modal.ios:not(.ios26-disabled).modal-card, -ion-modal.ios:not(.ios26-disabled).modal-sheet { - ion-header.ios:not(.ios26-disabled) ion-toolbar.ios:not(.ios26-disabled):first-of-type { +ion-modal.ios:not(.ios-theme-disabled, .ios26-disabled).modal-card, +ion-modal.ios:not(.ios-theme-disabled, .ios26-disabled).modal-sheet { + ion-header.ios:not(.ios-theme-disabled, .ios26-disabled) ion-toolbar.ios:not(.ios-theme-disabled, .ios26-disabled):first-of-type { padding-top: 6px; } } -ion-header.ios.header-collapse-condense:not(.ios26-disabled) ion-toolbar:not(.ios26-disabled):first-of-type { +ion-header.ios.header-collapse-condense:not(.ios-theme-disabled, .ios26-disabled) + ion-toolbar:not(.ios-theme-disabled, .ios26-disabled):first-of-type { padding-top: 0; } -ion-toolbar.ios:not(.toolbar-title-large):not(.ios26-disabled):not(:has(ion-segment.segment-expand)) { +ion-toolbar.ios:not(.toolbar-title-large):not(.ios-theme-disabled, .ios26-disabled):not(:has(ion-segment.segment-expand)) { --min-height: 68px; } -ion-header.ios:not(.ios26-disabled):not(.header-collapse-condense):not(:where(ion-modal *)) - > ion-toolbar.ios:not(.ios26-disabled):not(.toolbar-title-large):not(.toolbar-searchbar):not( +ion-header.ios:not(.ios-theme-disabled, .ios26-disabled):not(.header-collapse-condense):not(:where(ion-modal *)) + > ion-toolbar.ios:not(.ios-theme-disabled, .ios26-disabled):not(.toolbar-title-large):not(.toolbar-searchbar):not( :has(ion-segment.segment-expand) ):first-of-type { padding-top: max(calc(var(--ion-safe-area-top) - 10px), 6px); --min-height: 64px; } -ion-toolbar.ios:not(.ios26-disabled).toolbar-searchbar { +ion-toolbar.ios:not(.ios-theme-disabled, .ios26-disabled).toolbar-searchbar { *[slot='start'], *[slot='end'] { align-self: center; @@ -42,7 +43,7 @@ ion-toolbar.ios:not(.ios26-disabled).toolbar-searchbar { ion-buttons[slot='start'] { margin-right: 6px; } - ion-searchbar.ios:not(.ios26-disabled) { + ion-searchbar.ios:not(.ios-theme-disabled, .ios26-disabled) { .searchbar-input-container { margin: 0 12px 0 6px; } @@ -52,7 +53,7 @@ ion-toolbar.ios:not(.ios26-disabled).toolbar-searchbar { ion-buttons[slot='end'] { margin-left: 6px; } - ion-searchbar.ios:not(.ios26-disabled) { + ion-searchbar.ios:not(.ios-theme-disabled, .ios26-disabled) { .searchbar-input-container { margin: 0 6px 0 12px; } @@ -60,7 +61,7 @@ ion-toolbar.ios:not(.ios26-disabled).toolbar-searchbar { } } -ion-toolbar.ios:not(.ios26-disabled) { +ion-toolbar.ios:not(.ios-theme-disabled, .ios26-disabled) { ion-buttons { margin-left: 12px; margin-right: 12px; diff --git a/src/styles/default-variables.scss b/src/styles/default-variables.scss index eccfd2d3..f669e8b4 100644 --- a/src/styles/default-variables.scss +++ b/src/styles/default-variables.scss @@ -1,12 +1,13 @@ /** * @naming - * prefix: ios26 + * prefix: ios-theme * target-element: tab-button * target css property: color * status: selected * additional information: rgb */ +// Deprecated --ios26-* defaults back the --ios-theme-* fallbacks. :root { --ios26-floating-safe-area-top: max(10px, var(--ion-safe-area-top, 0px)); --ios26-floating-safe-area-bottom: max(10px, calc(var(--ion-safe-area-bottom, 0px) - 12px)); @@ -22,9 +23,9 @@ /** * `--ios26-color-background-rgb` is deprecated. - * You should replace to '--ios26-content-box-shadow-rgb'. + * Use '--ios-theme-content-box-shadow-rgb'. * This variable can also be defined in root, but if you want to set multiple background colors individually, it is easier to set them as follows. - * ion-content { --ios26-content-box-shadow-rgb: var(--ion-color-base-rgb); } + * ion-content { --ios-theme-content-box-shadow-rgb: var(--ion-color-base-rgb); } */ --ios26-content-box-shadow-rgb: var(--ion-color-base-rgb, var(--ion-background-color-rgb, 255, 255, 255)); diff --git a/src/styles/ionic-theme-ios26-dark-always.scss b/src/styles/ionic-theme-ios27-dark-always.scss similarity index 100% rename from src/styles/ionic-theme-ios26-dark-always.scss rename to src/styles/ionic-theme-ios27-dark-always.scss diff --git a/src/styles/ionic-theme-ios26-dark-class.scss b/src/styles/ionic-theme-ios27-dark-class.scss similarity index 100% rename from src/styles/ionic-theme-ios26-dark-class.scss rename to src/styles/ionic-theme-ios27-dark-class.scss diff --git a/src/styles/ionic-theme-ios26-dark-system.scss b/src/styles/ionic-theme-ios27-dark-system.scss similarity index 100% rename from src/styles/ionic-theme-ios26-dark-system.scss rename to src/styles/ionic-theme-ios27-dark-system.scss diff --git a/src/styles/ionic-theme-ios26.scss b/src/styles/ionic-theme-ios27.scss similarity index 100% rename from src/styles/ionic-theme-ios26.scss rename to src/styles/ionic-theme-ios27.scss diff --git a/src/styles/md-remove-ios-class-effect.scss b/src/styles/md-remove-ios-class-effect.scss index 0180a5ff..adb5f84c 100644 --- a/src/styles/md-remove-ios-class-effect.scss +++ b/src/styles/md-remove-ios-class-effect.scss @@ -1,9 +1,9 @@ ion-buttons.md { /** - * ion-buttons.ios26-disabled > ion-button.button-default is for ios26 structure. + * ion-buttons.ios-theme-disabled > ion-button.button-default is for ios27 structure. * for .md, remove button-default styles. Will be button-clear. */ - ion-button.md:not(.ios26-disabled).button-default { + ion-button.md:not(.ios-theme-disabled, .ios26-disabled).button-default { // copy .button-clear --color: initial; --background: transparent; diff --git a/src/styles/utils/api.scss b/src/styles/utils/api.scss index f2d843cc..f6c4d695 100644 --- a/src/styles/utils/api.scss +++ b/src/styles/utils/api.scss @@ -22,8 +22,8 @@ $_glass-surface: linear-gradient( $opacity: 0.72, $blur: 2px, $saturate: 360%, - $background-rgb: var(--ios26-glass-background-rgb), - $border-color-rgb: var(--ios26-glass-border-color-rgb), + $background-rgb: var(--ios-theme-glass-background-rgb, var(--ios26-glass-background-rgb)), + $border-color-rgb: var(--ios-theme-glass-border-color-rgb, var(--ios26-glass-border-color-rgb)), $include-background: true, $include-box-shadow: true, $include-border: true, @@ -40,10 +40,10 @@ $_glass-surface: linear-gradient( @if $include-box-shadow { box-shadow: inset 0 0 0 0.5px rgba($border-color-rgb, 0.6), - inset 0 0 1px 0 rgba(var(--ios26-glass-box-shadow-color-rgb), 0.18), + inset 0 0 1px 0 rgba(var(--ios-theme-glass-box-shadow-color-rgb, var(--ios26-glass-box-shadow-color-rgb)), 0.18), inset 0 1px 2px 0 rgba($border-color-rgb, 0.8), inset 0 -1px 2px 0 rgba($border-color-rgb, 0.5), - 0 0.5px 1px 0 rgba(var(--ios26-glass-box-shadow-color-rgb), $shadow-opacity); + 0 0.5px 1px 0 rgba(var(--ios-theme-glass-box-shadow-color-rgb, var(--ios26-glass-box-shadow-color-rgb)), $shadow-opacity); } @if $include-border { border: 0.5px solid; @@ -84,14 +84,14 @@ $_glass-surface: linear-gradient( } @mixin glass-background-button-activated { - background-color: rgba(var(--ios26-glass-background-rgb), 0.96); + background-color: rgba(var(--ios-theme-glass-background-rgb, var(--ios26-glass-background-rgb)), 0.96); border-color: rgba(var(--ion-text-color-rgb, 0, 0, 0), 0.12) rgba(var(--ion-text-color-rgb, 0, 0, 0), 0.32) rgba(var(--ion-text-color-rgb, 0, 0, 0), 0.16) rgba(var(--ion-text-color-rgb, 0, 0, 0), 0.28); box-shadow: - inset 0 0 0 0.5px rgba(var(--ios26-glass-border-color-rgb), 0.95), - inset 0 1px 3px 0 rgba(var(--ios26-glass-border-color-rgb), 0.85), - inset 0 -1px 2px 0 rgba(var(--ios26-glass-border-color-rgb), 0.6), - 0 0.5px 1px 0 rgba(var(--ios26-glass-box-shadow-color-rgb), 0.12); + inset 0 0 0 0.5px rgba(var(--ios-theme-glass-border-color-rgb, var(--ios26-glass-border-color-rgb)), 0.95), + inset 0 1px 3px 0 rgba(var(--ios-theme-glass-border-color-rgb, var(--ios26-glass-border-color-rgb)), 0.85), + inset 0 -1px 2px 0 rgba(var(--ios-theme-glass-border-color-rgb, var(--ios26-glass-border-color-rgb)), 0.6), + 0 0.5px 1px 0 rgba(var(--ios-theme-glass-box-shadow-color-rgb, var(--ios26-glass-box-shadow-color-rgb)), 0.12); } @mixin glass-background-button-activated-dark( @@ -102,18 +102,18 @@ $_glass-surface: linear-gradient( $include-box-shadow: true ) { @if $include-background { - background: rgba(var(--ios26-button-color-selected-rgb), $opacity); + background: rgba(var(--ios-theme-button-color-selected-rgb, var(--ios26-button-color-selected-rgb)), $opacity); } @else { - background-color: rgba(var(--ios26-button-color-selected-rgb), $opacity); + background-color: rgba(var(--ios-theme-button-color-selected-rgb, var(--ios26-button-color-selected-rgb)), $opacity); } backdrop-filter: blur($blur) saturate($saturate); @if $include-box-shadow { - box-shadow: inset 0 0 16px 0 rgba(var(--ios26-glass-box-shadow-color-rgb), 0.55); + box-shadow: inset 0 0 16px 0 rgba(var(--ios-theme-glass-box-shadow-color-rgb, var(--ios26-glass-box-shadow-color-rgb)), 0.55); } - border-top: 0.8px solid rgba(var(--ios26-button-color-selected-rgb), 0.8); - border-right: 0.8px solid rgba(var(--ios26-button-color-selected-rgb), 0.4); - border-bottom: 0.8px solid rgba(var(--ios26-button-color-selected-rgb), 0.8); - border-left: 0.8px solid rgba(var(--ios26-button-color-selected-rgb), 0.6); + border-top: 0.8px solid rgba(var(--ios-theme-button-color-selected-rgb, var(--ios26-button-color-selected-rgb)), 0.8); + border-right: 0.8px solid rgba(var(--ios-theme-button-color-selected-rgb, var(--ios26-button-color-selected-rgb)), 0.4); + border-bottom: 0.8px solid rgba(var(--ios-theme-button-color-selected-rgb, var(--ios26-button-color-selected-rgb)), 0.8); + border-left: 0.8px solid rgba(var(--ios-theme-button-color-selected-rgb, var(--ios26-button-color-selected-rgb)), 0.6); background-image: none; background-clip: border-box; background-origin: padding-box; diff --git a/src/styles/utils/dark/ion-button.scss b/src/styles/utils/dark/ion-button.scss index 2d2f3fdc..661d60cb 100644 --- a/src/styles/utils/dark/ion-button.scss +++ b/src/styles/utils/dark/ion-button.scss @@ -5,7 +5,7 @@ @include api.glass-background-button-activated-dark; } - ion-button:not(.ios26-disabled).button-clear { + ion-button:not(.ios-theme-disabled, .ios26-disabled).button-clear { &.ion-activated { color: rgb(var(--ion-text-color-rgb, 0, 0, 0)); &.ion-color { @@ -14,8 +14,8 @@ } } - ion-button:not(.ios26-disabled), - ion-back-button:not(.ios26-disabled) { + ion-button:not(.ios-theme-disabled, .ios26-disabled), + ion-back-button:not(.ios-theme-disabled, .ios26-disabled) { &.ion-activated { --color: rgba(var(--ion-text-color-rgb, 0, 0, 0), 1); @@ -28,10 +28,10 @@ @mixin theme-dark-button($is-back-button: false) { &.ion-activated:not(.button-solid):not(.button-outline):not(.button-clear) { - --background: rgba(var(--ios26-button-color-selected-rgb), 0.56); + --background: rgba(var(--ios-theme-button-color-selected-rgb, var(--ios26-button-color-selected-rgb)), 0.56); --color: rgba(var(--ion-text-color-rgb, 0, 0, 0), 1); @if not $is-back-button { - --box-shadow: inset 0 0 16px 0 rgba(var(--ios26-glass-box-shadow-color-rgb), 0.55); + --box-shadow: inset 0 0 16px 0 rgba(var(--ios-theme-glass-box-shadow-color-rgb, var(--ios26-glass-box-shadow-color-rgb)), 0.55); } &.ion-color { --color: rgba(var(--ion-color-base-rgb, var(--ion-text-color-rgb, 0, 0, 0)), 1); @@ -46,13 +46,13 @@ } @mixin ion-button { - ion-buttons.ios:not(.ios26-disabled):not(:has(ion-back-button, ion-button:not(.button-clear))) { + ion-buttons.ios:not(.ios-theme-disabled, .ios26-disabled):not(:has(ion-back-button, ion-button:not(.button-clear))) { @include theme-dark-buttons; } - ion-button.ios:not(.ios26-disabled) { + ion-button.ios:not(.ios-theme-disabled, .ios26-disabled) { @include theme-dark-button; } - ion-back-button.ios:not(.ios26-disabled) { + ion-back-button.ios:not(.ios-theme-disabled, .ios26-disabled) { @include theme-dark-button($is-back-button: true); } } diff --git a/src/styles/utils/dark/ion-fab.scss b/src/styles/utils/dark/ion-fab.scss index b6933207..ece43c82 100644 --- a/src/styles/utils/dark/ion-fab.scss +++ b/src/styles/utils/dark/ion-fab.scss @@ -1,7 +1,7 @@ @use '../api'; @mixin ion-fab { - ion-fab.ios:not(.ios26-disabled) { + ion-fab.ios:not(.ios-theme-disabled, .ios26-disabled) { ion-fab-button { &.ion-activated { &::part(native) { diff --git a/src/styles/utils/dark/ion-segment.scss b/src/styles/utils/dark/ion-segment.scss index 535ba659..3887001c 100644 --- a/src/styles/utils/dark/ion-segment.scss +++ b/src/styles/utils/dark/ion-segment.scss @@ -1,10 +1,11 @@ @mixin ion-segment { - ion-segment.ios:not(.ios26-disabled) { + ion-segment.ios:not(.ios-theme-disabled, .ios26-disabled) { } - ion-segment-button.ios:not(.ios26-disabled) { + ion-segment-button.ios:not(.ios-theme-disabled, .ios26-disabled) { &.ion-cloned-element { &::part(native) { --ios26-glass-border-color-rgb: 120, 120, 120; + --ios-theme-glass-border-color-rgb: var(--ios26-glass-border-color-rgb); box-shadow: inset 0 0 8px 0 rgba(255, 255, 255, 0.2); } } diff --git a/src/styles/utils/dark/ion-tabs.scss b/src/styles/utils/dark/ion-tabs.scss index 1d9bf51d..0022c3b2 100644 --- a/src/styles/utils/dark/ion-tabs.scss +++ b/src/styles/utils/dark/ion-tabs.scss @@ -1,9 +1,9 @@ @use '../api'; @mixin ion-tabs { - ion-tab-bar.ios:not(.ios26-disabled) { + ion-tab-bar.ios:not(.ios-theme-disabled, .ios26-disabled) { } - ion-tab-button.ios:not(.ios26-disabled) { + ion-tab-button.ios:not(.ios-theme-disabled, .ios26-disabled) { &.ion-activated { ion-label, ion-icon { @@ -14,6 +14,7 @@ &.ion-cloned-element { &::part(native) { --ios26-glass-border-color-rgb: 120, 120, 120; + --ios-theme-glass-border-color-rgb: var(--ios26-glass-border-color-rgb); box-shadow: inset 0 0 8px 0 rgba(255, 255, 255, 0.2); } } diff --git a/src/styles/utils/translucent.scss b/src/styles/utils/translucent.scss index e6830ed5..fa3998c2 100644 --- a/src/styles/utils/translucent.scss +++ b/src/styles/utils/translucent.scss @@ -1,7 +1,7 @@ @use 'api'; -ion-header.ios:not(.ios26-disabled).header-translucent, -ion-footer.ios:not(.ios26-disabled).footer-translucent { +ion-header.ios:not(.ios-theme-disabled, .ios26-disabled).header-translucent, +ion-footer.ios:not(.ios-theme-disabled, .ios26-disabled).footer-translucent { .header-background, .footer-background { backdrop-filter: none; @@ -20,14 +20,17 @@ ion-footer.ios:not(.ios26-disabled).footer-translucent { } } -ion-header.ios:not(.ios26-disabled).header-translucent:not(.header-collapse-condense) { - &:has(> ion-toolbar.ios:not(.ios26-disabled) > *)::before { +ion-header.ios:not(.ios-theme-disabled, .ios26-disabled).header-translucent:not(.header-collapse-condense) { + &:has(> ion-toolbar.ios:not(.ios-theme-disabled, .ios26-disabled) > *)::before { height: 100%; - background: rgba(var(--ion-color-base-rgb, var(--ios26-content-box-shadow-rgb)), calc(0.2 * var(--opacity-scale, 1))); + background: rgba( + var(--ion-color-base-rgb, var(--ios-theme-content-box-shadow-rgb, var(--ios26-content-box-shadow-rgb))), + calc(0.2 * var(--opacity-scale, 1)) + ); backdrop-filter: blur(8px); } - &:not(.ion-no-border) > ion-toolbar.ios:not(.ios26-disabled):has(> *):last-of-type { + &:not(.ion-no-border) > ion-toolbar.ios:not(.ios-theme-disabled, .ios26-disabled):has(> *):last-of-type { --border-width: 0 0 0.5px; --border-color: var(--ion-toolbar-border-color, rgba(var(--ion-text-color-rgb, 0, 0, 0), 0.08)); &::part(background) { @@ -37,7 +40,7 @@ ion-header.ios:not(.ios26-disabled).header-translucent:not(.header-collapse-cond } } -ion-header.ios:not(.ios26-disabled).header-collapse-main ion-toolbar.in-toolbar ion-title { +ion-header.ios:not(.ios-theme-disabled, .ios26-disabled).header-collapse-main ion-toolbar.in-toolbar ion-title { transition: opacity 0.5s ease-in-out, color 0.5s ease-in-out, @@ -45,15 +48,17 @@ ion-header.ios:not(.ios26-disabled).header-collapse-main ion-toolbar.in-toolbar transform 0.5s ease-in-out; } -ion-header.ios:not(.ios26-disabled).header-collapse-condense-inactive:not(.header-collapse-condense) ion-toolbar.in-toolbar ion-title { +ion-header.ios:not(.ios-theme-disabled, .ios26-disabled).header-collapse-condense-inactive:not(.header-collapse-condense) + ion-toolbar.in-toolbar + ion-title { color: transparent; text-shadow: 0 0 1px var(--ion-color-base, var(--ion-toolbar-color, var(--ion-text-color, #000))); transform: translateY(8px); } -ion-header.ios:not(.ios26-disabled).header-translucent::before, -ion-header.ios:not(.ios26-disabled).header-translucent::after, -ion-content.ios:not(.ios26-disabled).content-fullscreen::part(background)::before { +ion-header.ios:not(.ios-theme-disabled, .ios26-disabled).header-translucent::before, +ion-header.ios:not(.ios-theme-disabled, .ios26-disabled).header-translucent::after, +ion-content.ios:not(.ios-theme-disabled, .ios26-disabled).content-fullscreen::part(background)::before { content: ''; position: absolute; width: 100%; @@ -61,7 +66,7 @@ ion-content.ios:not(.ios26-disabled).content-fullscreen::part(background)::befor z-index: 1; } -ion-header.ios:not(.ios26-disabled).header-translucent::before { +ion-header.ios:not(.ios-theme-disabled, .ios26-disabled).header-translucent::before { /** * With safe-area effect, this is need. */ @@ -70,7 +75,7 @@ ion-header.ios:not(.ios26-disabled).header-translucent::before { height: var(--ion-safe-area-top); } -ion-header.ios:not(.ios26-disabled).header-translucent::after { +ion-header.ios:not(.ios-theme-disabled, .ios26-disabled).header-translucent::after { z-index: 2; top: 0; height: 100%; @@ -88,8 +93,8 @@ ion-header.ios:not(.ios26-disabled).header-translucent::after { height: var(--offset-top, 0px); background: linear-gradient( in oklab, - rgba(var(--ios26-content-box-shadow-rgb), 0.5) 0%, - rgba(var(--ios26-content-box-shadow-rgb), 0.2) 50%, - rgba(var(--ios26-content-box-shadow-rgb), 0) 100% + rgba(var(--ios-theme-content-box-shadow-rgb, var(--ios26-content-box-shadow-rgb)), 0.5) 0%, + rgba(var(--ios-theme-content-box-shadow-rgb, var(--ios26-content-box-shadow-rgb)), 0.2) 50%, + rgba(var(--ios-theme-content-box-shadow-rgb, var(--ios26-content-box-shadow-rgb)), 0) 100% ); } diff --git a/src/transition/ios.transition.ts b/src/transition/ios.transition.ts index e5301941..b70fa817 100644 --- a/src/transition/ios.transition.ts +++ b/src/transition/ios.transition.ts @@ -24,14 +24,14 @@ const animateFixedBackButton = ( otherPage?: HTMLElement, ) => { const button = page.querySelector( - ':scope > ion-header.header-translucent:not(.ios26-disabled) ion-back-button:not(.ios26-disabled)', + ':scope > ion-header.header-translucent:not(.ios-theme-disabled, .ios26-disabled) ion-back-button:not(.ios-theme-disabled, .ios26-disabled)', ); if (!button || button.offsetWidth === 0) { return; } const otherButton = otherPage?.querySelector( - ':scope > ion-header.header-translucent:not(.ios26-disabled) ion-back-button:not(.ios26-disabled)', + ':scope > ion-header.header-translucent:not(.ios-theme-disabled, .ios26-disabled) ion-back-button:not(.ios-theme-disabled, .ios26-disabled)', ); const persistent = !!otherButton && otherButton.offsetWidth > 0; const buttons = [button, ...(persistent ? [otherButton!] : [])].map((element) => ({ @@ -623,7 +623,7 @@ export const iosTransitionAnimation = (navEl: HTMLElement, opts: TransitionOptio .beforeRemoveClass('ion-page-invisible'); const topPage = backDirection ? leavingEl : enteringEl; - if (topPage?.querySelector(':scope > ion-header.header-translucent:not(.ios26-disabled)')) { + if (topPage?.querySelector(':scope > ion-header.header-translucent:not(.ios-theme-disabled, .ios26-disabled)')) { const shadow = topPage.style.boxShadow; rootAnimation.beforeAddWrite(() => { topPage.style.boxShadow = `${isRTL ? 4 : -4}px 0 24px rgba(0, 0, 0, 0.04)`; @@ -640,7 +640,7 @@ export const iosTransitionAnimation = (navEl: HTMLElement, opts: TransitionOptio rootAnimation.addAnimation(navDecorAnimation); } - if (enteringEl.querySelector(':scope > ion-header.header-translucent:not(.ios26-disabled)')) { + if (enteringEl.querySelector(':scope > ion-header.header-translucent:not(.ios-theme-disabled, .ios26-disabled)')) { enteringContentAnimation.addElement(enteringEl); } else if (!contentEl && enteringToolBarEls.length === 0 && headerEls.length === 0) { enteringContentAnimation.addElement(enteringEl.querySelector(':scope > .ion-page, :scope > ion-nav, :scope > ion-tabs')!); // REVIEW @@ -661,7 +661,7 @@ export const iosTransitionAnimation = (navEl: HTMLElement, opts: TransitionOptio enteringContentAnimation.beforeClearStyles([OPACITY]).fromTo('transform', `translateX(${OFF_RIGHT})`, `translateX(${CENTER})`); } - if (contentEl && !enteringEl.querySelector(':scope > ion-header.header-translucent:not(.ios26-disabled)')) { + if (contentEl && !enteringEl.querySelector(':scope > ion-header.header-translucent:not(.ios-theme-disabled, .ios26-disabled)')) { const enteringTransitionEffectEl = shadow(contentEl).querySelector('.transition-effect'); if (enteringTransitionEffectEl) { const enteringTransitionCoverEl = enteringTransitionEffectEl.querySelector('.transition-cover'); @@ -705,7 +705,7 @@ export const iosTransitionAnimation = (navEl: HTMLElement, opts: TransitionOptio const enteringContentHasLargeTitle = enteringEl.querySelector('ion-header.header-collapse-condense'); enteringToolBarEls.forEach((enteringToolBarEl) => { - if (enteringToolBarEl.closest('ion-header')?.matches('.header-translucent:not(.ios26-disabled)')) { + if (enteringToolBarEl.closest('ion-header')?.matches('.header-translucent:not(.ios-theme-disabled, .ios26-disabled)')) { return; } const enteringToolBar = createAnimation(); @@ -796,7 +796,7 @@ export const iosTransitionAnimation = (navEl: HTMLElement, opts: TransitionOptio const leavingToolBarEls = leavingEl.querySelectorAll(':scope > ion-header > ion-toolbar'); const leavingHeaderEls = leavingEl.querySelectorAll(':scope > ion-header > *:not(ion-toolbar), :scope > ion-footer > *'); - if (leavingEl.querySelector(':scope > ion-header.header-translucent:not(.ios26-disabled)')) { + if (leavingEl.querySelector(':scope > ion-header.header-translucent:not(.ios-theme-disabled, .ios26-disabled)')) { leavingContent.addElement(leavingEl); } else if (!leavingContentEl && leavingToolBarEls.length === 0 && leavingHeaderEls.length === 0) { leavingContent.addElement(leavingEl.querySelector(':scope > .ion-page, :scope > ion-nav, :scope > ion-tabs')!); // REVIEW @@ -824,7 +824,10 @@ export const iosTransitionAnimation = (navEl: HTMLElement, opts: TransitionOptio leavingContent.fromTo('transform', `translateX(${CENTER})`, `translateX(${OFF_LEFT})`).fromTo(OPACITY, 1, OFF_OPACITY); } - if (leavingContentEl && !leavingEl.querySelector(':scope > ion-header.header-translucent:not(.ios26-disabled)')) { + if ( + leavingContentEl && + !leavingEl.querySelector(':scope > ion-header.header-translucent:not(.ios-theme-disabled, .ios26-disabled)') + ) { const leavingTransitionEffectEl = shadow(leavingContentEl).querySelector('.transition-effect'); if (leavingTransitionEffectEl) { @@ -856,7 +859,7 @@ export const iosTransitionAnimation = (navEl: HTMLElement, opts: TransitionOptio } leavingToolBarEls.forEach((leavingToolBarEl) => { - if (leavingToolBarEl.closest('ion-header')?.matches('.header-translucent:not(.ios26-disabled)')) { + if (leavingToolBarEl.closest('ion-header')?.matches('.header-translucent:not(.ios-theme-disabled, .ios26-disabled)')) { return; } const leavingToolBar = createAnimation(); diff --git a/swiftui/ionic-ios26-native.xcodeproj/project.pbxproj b/swiftui/ionic-ios26-native.xcodeproj/project.pbxproj deleted file mode 100644 index 028dd38c..00000000 --- a/swiftui/ionic-ios26-native.xcodeproj/project.pbxproj +++ /dev/null @@ -1,337 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 77; - objects = { - -/* Begin PBXFileReference section */ - DBF0AF4E2F5BBC5100EBC719 /* ionic-ios26-native.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ionic-ios26-native.app"; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFileSystemSynchronizedRootGroup section */ - DBF0AF502F5BBC5100EBC719 /* ionic-ios26-native */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = "ionic-ios26-native"; - sourceTree = ""; - }; -/* End PBXFileSystemSynchronizedRootGroup section */ - -/* Begin PBXFrameworksBuildPhase section */ - DBF0AF4B2F5BBC5100EBC719 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - DBF0AF452F5BBC5100EBC719 = { - isa = PBXGroup; - children = ( - DBF0AF502F5BBC5100EBC719 /* ionic-ios26-native */, - DBF0AF4F2F5BBC5100EBC719 /* Products */, - ); - sourceTree = ""; - }; - DBF0AF4F2F5BBC5100EBC719 /* Products */ = { - isa = PBXGroup; - children = ( - DBF0AF4E2F5BBC5100EBC719 /* ionic-ios26-native.app */, - ); - name = Products; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - DBF0AF4D2F5BBC5100EBC719 /* ionic-ios26-native */ = { - isa = PBXNativeTarget; - buildConfigurationList = DBF0AF592F5BBC5200EBC719 /* Build configuration list for PBXNativeTarget "ionic-ios26-native" */; - buildPhases = ( - DBF0AF4A2F5BBC5100EBC719 /* Sources */, - DBF0AF4B2F5BBC5100EBC719 /* Frameworks */, - DBF0AF4C2F5BBC5100EBC719 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - fileSystemSynchronizedGroups = ( - DBF0AF502F5BBC5100EBC719 /* ionic-ios26-native */, - ); - name = "ionic-ios26-native"; - packageProductDependencies = ( - ); - productName = "ionic-ios26-native"; - productReference = DBF0AF4E2F5BBC5100EBC719 /* ionic-ios26-native.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - DBF0AF462F5BBC5100EBC719 /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 2610; - LastUpgradeCheck = 2610; - TargetAttributes = { - DBF0AF4D2F5BBC5100EBC719 = { - CreatedOnToolsVersion = 26.1.1; - }; - }; - }; - buildConfigurationList = DBF0AF492F5BBC5100EBC719 /* Build configuration list for PBXProject "ionic-ios26-native" */; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = DBF0AF452F5BBC5100EBC719; - minimizedProjectReferenceProxies = 1; - preferredProjectObjectVersion = 77; - productRefGroup = DBF0AF4F2F5BBC5100EBC719 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - DBF0AF4D2F5BBC5100EBC719 /* ionic-ios26-native */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - DBF0AF4C2F5BBC5100EBC719 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - DBF0AF4A2F5BBC5100EBC719 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - DBF0AF572F5BBC5200EBC719 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - DEVELOPMENT_TEAM = P7E2DJ2G49; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 26.1; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - DBF0AF582F5BBC5200EBC719 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = P7E2DJ2G49; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; - GCC_C_LANGUAGE_STANDARD = gnu17; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 26.1; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - DBF0AF5A2F5BBC5200EBC719 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = P7E2DJ2G49; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = "jp.rdlabo.ionic-ios26-native"; - PRODUCT_NAME = "$(TARGET_NAME)"; - STRING_CATALOG_GENERATE_SYMBOLS = YES; - SWIFT_APPROACHABLE_CONCURRENCY = YES; - SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - DBF0AF5B2F5BBC5200EBC719 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = P7E2DJ2G49; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = "jp.rdlabo.ionic-ios26-native"; - PRODUCT_NAME = "$(TARGET_NAME)"; - STRING_CATALOG_GENERATE_SYMBOLS = YES; - SWIFT_APPROACHABLE_CONCURRENCY = YES; - SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - DBF0AF492F5BBC5100EBC719 /* Build configuration list for PBXProject "ionic-ios26-native" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - DBF0AF572F5BBC5200EBC719 /* Debug */, - DBF0AF582F5BBC5200EBC719 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - DBF0AF592F5BBC5200EBC719 /* Build configuration list for PBXNativeTarget "ionic-ios26-native" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - DBF0AF5A2F5BBC5200EBC719 /* Debug */, - DBF0AF5B2F5BBC5200EBC719 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = DBF0AF462F5BBC5100EBC719 /* Project object */; -} diff --git a/swiftui/ionic-ios26-native.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/swiftui/ionic-ios26-native.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a6..00000000 --- a/swiftui/ionic-ios26-native.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/swiftui/ionic-ios26-native.xcodeproj/project.xcworkspace/xcuserdata/sakakibara.xcuserdatad/UserInterfaceState.xcuserstate b/swiftui/ionic-ios26-native.xcodeproj/project.xcworkspace/xcuserdata/sakakibara.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index c1bbedf7..00000000 Binary files a/swiftui/ionic-ios26-native.xcodeproj/project.xcworkspace/xcuserdata/sakakibara.xcuserdatad/UserInterfaceState.xcuserstate and /dev/null differ diff --git a/swiftui/ionic-ios26-native.xcodeproj/xcuserdata/sakakibara.xcuserdatad/xcschemes/xcschememanagement.plist b/swiftui/ionic-ios26-native.xcodeproj/xcuserdata/sakakibara.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 6f24c05d..00000000 --- a/swiftui/ionic-ios26-native.xcodeproj/xcuserdata/sakakibara.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,14 +0,0 @@ - - - - - SchemeUserState - - ionic-ios26-native.xcscheme_^#shared#^_ - - orderHint - 0 - - - - diff --git a/swiftui/ionic-ios26-native/Assets.xcassets/AccentColor.colorset/Contents.json b/swiftui/ionic-ios26-native/Assets.xcassets/AccentColor.colorset/Contents.json deleted file mode 100644 index eb878970..00000000 --- a/swiftui/ionic-ios26-native/Assets.xcassets/AccentColor.colorset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "colors" : [ - { - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/swiftui/ionic-ios26-native/Assets.xcassets/AppIcon.appiconset/Contents.json b/swiftui/ionic-ios26-native/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 23058801..00000000 --- a/swiftui/ionic-ios26-native/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "tinted" - } - ], - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/swiftui/ionic-ios26-native/Assets.xcassets/Contents.json b/swiftui/ionic-ios26-native/Assets.xcassets/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/swiftui/ionic-ios26-native/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/swiftui/ionic-ios26-native/ContentView.swift b/swiftui/ionic-ios26-native/ContentView.swift deleted file mode 100644 index d217f2a0..00000000 --- a/swiftui/ionic-ios26-native/ContentView.swift +++ /dev/null @@ -1,11 +0,0 @@ -import SwiftUI - -struct ContentView: View { - var body: some View { - IndexView() - } -} - -#Preview { - ContentView() -} diff --git a/swiftui/ionic-ios26-native/Theme/IOS26Theme.swift b/swiftui/ionic-ios26-native/Theme/IOS26Theme.swift deleted file mode 100644 index b8f53f9f..00000000 --- a/swiftui/ionic-ios26-native/Theme/IOS26Theme.swift +++ /dev/null @@ -1,35 +0,0 @@ -import SwiftUI -extension Color { - /// Light theme: rgb(255, 255, 255) - static let ios26GlassBackground = Color(red: 255/255, green: 255/255, blue: 255/255) - /// Light theme: rgb(220, 220, 220) - static let ios26GlassBoxShadow = Color(red: 220/255, green: 220/255, blue: 220/255) - /// Selected/primary text: rgb(16, 16, 16) - static let ios26ButtonColorSelected = Color(red: 16/255, green: 16/255, blue: 16/255) - - /// Dark theme: rgb(62, 62, 62) - static let ios26DarkGlassBackground = Color(red: 62/255, green: 62/255, blue: 62/255) - /// Dark theme: rgb(68, 68, 68) - static let ios26DarkGlassBorder = Color(red: 68/255, green: 68/255, blue: 68/255) - /// Dark theme selected: rgb(255, 255, 255) - static let ios26DarkButtonColorSelected = Color.white - - /// Content background (light) - #f2f2f7 - static let ios26ContentBackground = Color(red: 242/255, green: 242/255, blue: 247/255) - /// Content background (dark) - static let ios26DarkContentBackground = Color(red: 0/255, green: 0/255, blue: 0/255) -} - -struct IOS26InsetListStyle: ViewModifier { - func body(content: Content) -> some View { - content - .listStyle(.insetGrouped) - .scrollContentBackground(.hidden) - } -} - -extension View { - func ios26InsetList() -> some View { - modifier(IOS26InsetListStyle()) - } -} diff --git a/swiftui/ionic-ios26-native/Views/ComponentDetailView.swift b/swiftui/ionic-ios26-native/Views/ComponentDetailView.swift deleted file mode 100644 index 2db8cd7b..00000000 --- a/swiftui/ionic-ios26-native/Views/ComponentDetailView.swift +++ /dev/null @@ -1,28 +0,0 @@ -import SwiftUI - -struct ComponentDetailView: View { - let component: ComponentItem - - var body: some View { - VStack(spacing: 16) { - Image(systemName: "square.grid.2x2") - .font(.system(size: 64)) - .foregroundStyle(.secondary) - Text(component.name) - .font(.title2) - Text("Component demo placeholder") - .font(.subheadline) - .foregroundStyle(.secondary) - } - .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(Color(.systemGroupedBackground)) - .navigationTitle(component.name) - .navigationBarTitleDisplayMode(.inline) - } -} - -#Preview { - NavigationStack { - ComponentDetailView(component: ComponentItem(name: "button", enable: true)) - } -} diff --git a/swiftui/ionic-ios26-native/Views/IndexView.swift b/swiftui/ionic-ios26-native/Views/IndexView.swift deleted file mode 100644 index 553f2a99..00000000 --- a/swiftui/ionic-ios26-native/Views/IndexView.swift +++ /dev/null @@ -1,112 +0,0 @@ -import SwiftUI - -struct ComponentItem: Identifiable, Hashable { - let id = UUID() - let name: String - var enable: Bool - - func hash(into hasher: inout Hasher) { hasher.combine(name) } - static func == (lhs: ComponentItem, rhs: ComponentItem) -> Bool { lhs.name == rhs.name } -} - -struct IndexView: View { - @State private var components: [ComponentItem] = [ - ComponentItem(name: "accordion", enable: true), - ComponentItem(name: "action-sheet", enable: true), - ComponentItem(name: "alert", enable: true), - ComponentItem(name: "breadcrumbs", enable: true), - ComponentItem(name: "button", enable: true), - ComponentItem(name: "card", enable: true), - ComponentItem(name: "checkbox", enable: true), - ComponentItem(name: "chip", enable: true), - ComponentItem(name: "date-and-time-pickers", enable: true), - ComponentItem(name: "floating-action-button", enable: true), - ComponentItem(name: "inputs", enable: true), - ComponentItem(name: "item-list", enable: true), - ComponentItem(name: "modal", enable: true), - ComponentItem(name: "popover", enable: true), - ComponentItem(name: "progress-indicators", enable: true), - ComponentItem(name: "radio", enable: true), - ComponentItem(name: "range", enable: true), - ComponentItem(name: "reorder", enable: true), - ComponentItem(name: "searchbar", enable: true), - ComponentItem(name: "segment", enable: true), - ComponentItem(name: "select", enable: true), - ComponentItem(name: "tabs", enable: true), - ComponentItem(name: "toast", enable: true), - ComponentItem(name: "toggle", enable: true), - ComponentItem(name: "toolbar", enable: true), - ] - - @AppStorage("isDarkMode") private var isDarkMode = false - @Environment(\.colorScheme) private var systemColorScheme - @Environment(\.openURL) private var openURL - - private var effectiveColorScheme: ColorScheme { - isDarkMode ? .dark : systemColorScheme - } - - private var enableComponents: [ComponentItem] { - components.filter { $0.enable } - } - - var body: some View { - NavigationStack { - ZStack { - backgroundColor - .ignoresSafeArea() - - List { - Section { - Toggle("Dark Mode", isOn: $isDarkMode) - } header: { - Text("Settings") - } - - Section { - ForEach(enableComponents) { item in - NavigationLink(value: item) { - Text(item.name) - } - } - } header: { - Text("Components") - } - } - .ios26InsetList() - .scrollContentBackground(.hidden) - } - .navigationTitle("Index") - .navigationBarTitleDisplayMode(.large) - .toolbar { - ToolbarItem(placement: .topBarTrailing) { - HStack(spacing: 12) { - Button { - openURL(URL(string: "https://github.com/rdlabo-dev/ionic-theme-ios26")!) - } label: { - Image(systemName: "chevron.left.forwardslash.chevron.right") - } - - Button { - openURL(URL(string: "https://ionicframework.com/")!) - } label: { - Image(systemName: "bolt.fill") - } - } - } - } - .navigationDestination(for: ComponentItem.self) { item in - ComponentDetailView(component: item) - } - } - .preferredColorScheme(isDarkMode ? .dark : nil) - } - - private var backgroundColor: Color { - effectiveColorScheme == .dark ? .ios26DarkContentBackground : .ios26ContentBackground - } -} - -#Preview { - IndexView() -} diff --git a/swiftui/ionic-ios26-native/ionic_ios26_nativeApp.swift b/swiftui/ionic-ios26-native/ionic_ios26_nativeApp.swift deleted file mode 100644 index df4253b2..00000000 --- a/swiftui/ionic-ios26-native/ionic_ios26_nativeApp.swift +++ /dev/null @@ -1,10 +0,0 @@ -import SwiftUI - -@main -struct ionic_ios26_nativeApp: App { - var body: some Scene { - WindowGroup { - ContentView() - } - } -}