@rdlabo/ionic-theme-ios27 0.1.0-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +241 -0
  3. package/dist/css/components/ion-action-sheet.css +1 -0
  4. package/dist/css/components/ion-alert.css +1 -0
  5. package/dist/css/components/ion-button.css +1 -0
  6. package/dist/css/components/ion-card.css +1 -0
  7. package/dist/css/components/ion-content.css +1 -0
  8. package/dist/css/components/ion-datetime.css +1 -0
  9. package/dist/css/components/ion-fab.css +1 -0
  10. package/dist/css/components/ion-inputs.css +1 -0
  11. package/dist/css/components/ion-list.css +1 -0
  12. package/dist/css/components/ion-loading.css +1 -0
  13. package/dist/css/components/ion-menu.css +1 -0
  14. package/dist/css/components/ion-modal.css +1 -0
  15. package/dist/css/components/ion-picker.css +1 -0
  16. package/dist/css/components/ion-popover.css +1 -0
  17. package/dist/css/components/ion-range.css +1 -0
  18. package/dist/css/components/ion-searchbar.css +1 -0
  19. package/dist/css/components/ion-segment.css +1 -0
  20. package/dist/css/components/ion-tabs.css +1 -0
  21. package/dist/css/components/ion-toast.css +1 -0
  22. package/dist/css/components/ion-toggle.css +1 -0
  23. package/dist/css/components/ion-toolbar.css +1 -0
  24. package/dist/css/default-variables.css +1 -0
  25. package/dist/css/ionic-theme-ios26-dark-always.css +1 -0
  26. package/dist/css/ionic-theme-ios26-dark-class.css +1 -0
  27. package/dist/css/ionic-theme-ios26-dark-system.css +1 -0
  28. package/dist/css/ionic-theme-ios26.css +1 -0
  29. package/dist/css/md-ion-list-inset.css +1 -0
  30. package/dist/css/md-remove-ios-class-effect.css +1 -0
  31. package/dist/css/utils/api.css +1 -0
  32. package/dist/css/utils/dark/ion-button.css +1 -0
  33. package/dist/css/utils/dark/ion-fab.css +1 -0
  34. package/dist/css/utils/dark/ion-segment.css +1 -0
  35. package/dist/css/utils/dark/ion-tabs.css +1 -0
  36. package/dist/css/utils/searchable.css +1 -0
  37. package/dist/css/utils/structured-list.css +1 -0
  38. package/dist/css/utils/theme-dark.css +1 -0
  39. package/dist/css/utils/translucent.css +1 -0
  40. package/dist/docs.json +234 -0
  41. package/dist/focus-controller/index.d.ts +6 -0
  42. package/dist/focus-controller/index.d.ts.map +1 -0
  43. package/dist/focus-controller/index.js +62 -0
  44. package/dist/focus-controller /index.d.ts +6 -0
  45. package/dist/focus-controller /index.d.ts.map +1 -0
  46. package/dist/focus-controller /index.js +62 -0
  47. package/dist/index.d.ts +9 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +30 -0
  50. package/dist/popover/animations/ios.enter.d.ts +4 -0
  51. package/dist/popover/animations/ios.enter.d.ts.map +1 -0
  52. package/dist/popover/animations/ios.enter.js +97 -0
  53. package/dist/popover/animations/ios.leave.d.ts +3 -0
  54. package/dist/popover/animations/ios.leave.d.ts.map +1 -0
  55. package/dist/popover/animations/ios.leave.js +60 -0
  56. package/dist/popover/popover-interface.d.ts +38 -0
  57. package/dist/popover/popover-interface.d.ts.map +1 -0
  58. package/dist/popover/popover-interface.js +1 -0
  59. package/dist/popover/utils.d.ts +37 -0
  60. package/dist/popover/utils.d.ts.map +1 -0
  61. package/dist/popover/utils.js +377 -0
  62. package/dist/sheets-of-glass/animations.d.ts +8 -0
  63. package/dist/sheets-of-glass/animations.d.ts.map +1 -0
  64. package/dist/sheets-of-glass/animations.js +97 -0
  65. package/dist/sheets-of-glass/index.d.ts +3 -0
  66. package/dist/sheets-of-glass/index.d.ts.map +1 -0
  67. package/dist/sheets-of-glass/index.js +174 -0
  68. package/dist/sheets-of-glass/interfaces.d.ts +16 -0
  69. package/dist/sheets-of-glass/interfaces.d.ts.map +1 -0
  70. package/dist/sheets-of-glass/interfaces.js +1 -0
  71. package/dist/tab-bar-searchable/animations/enter.d.ts +8 -0
  72. package/dist/tab-bar-searchable/animations/enter.d.ts.map +1 -0
  73. package/dist/tab-bar-searchable/animations/enter.js +73 -0
  74. package/dist/tab-bar-searchable/animations/leave.d.ts +8 -0
  75. package/dist/tab-bar-searchable/animations/leave.d.ts.map +1 -0
  76. package/dist/tab-bar-searchable/animations/leave.js +66 -0
  77. package/dist/tab-bar-searchable/index.d.ts +4 -0
  78. package/dist/tab-bar-searchable/index.d.ts.map +1 -0
  79. package/dist/tab-bar-searchable/index.js +75 -0
  80. package/dist/tab-bar-searchable/interfaces.d.ts +41 -0
  81. package/dist/tab-bar-searchable/interfaces.d.ts.map +1 -0
  82. package/dist/tab-bar-searchable/interfaces.js +5 -0
  83. package/dist/tab-bar-searchable/utils.d.ts +12 -0
  84. package/dist/tab-bar-searchable/utils.d.ts.map +1 -0
  85. package/dist/tab-bar-searchable/utils.js +60 -0
  86. package/dist/transition/index.d.ts +19 -0
  87. package/dist/transition/index.d.ts.map +1 -0
  88. package/dist/transition/index.js +219 -0
  89. package/dist/transition/ios.transition.d.ts +5 -0
  90. package/dist/transition/ios.transition.d.ts.map +1 -0
  91. package/dist/transition/ios.transition.js +609 -0
  92. package/dist/utils.d.ts +17 -0
  93. package/dist/utils.d.ts.map +1 -0
  94. package/dist/utils.js +69 -0
  95. package/docs/e2e-testing.md +51 -0
  96. package/docs/eslint.md +73 -0
  97. package/docs/experimental-animation.md +85 -0
  98. package/docs/features.md +88 -0
  99. package/docs/feedback.md +65 -0
  100. package/docs/ios-18.md +16 -0
  101. package/docs/migration.md +94 -0
  102. package/docs/special-markup.md +145 -0
  103. package/docs/using-ion-item-group.md +47 -0
  104. package/package.json +63 -0
  105. package/src/focus-controller/index.ts +123 -0
  106. package/src/index.ts +33 -0
  107. package/src/popover/animations/ios.enter.ts +144 -0
  108. package/src/popover/animations/ios.leave.ts +76 -0
  109. package/src/popover/popover-interface.ts +44 -0
  110. package/src/popover/utils.ts +696 -0
  111. package/src/sheets-of-glass/animations.ts +120 -0
  112. package/src/sheets-of-glass/index.ts +214 -0
  113. package/src/sheets-of-glass/interfaces.ts +17 -0
  114. package/src/styles/components/ion-action-sheet.scss +85 -0
  115. package/src/styles/components/ion-alert.scss +66 -0
  116. package/src/styles/components/ion-button.scss +443 -0
  117. package/src/styles/components/ion-card.scss +7 -0
  118. package/src/styles/components/ion-content.scss +22 -0
  119. package/src/styles/components/ion-datetime.scss +9 -0
  120. package/src/styles/components/ion-fab.scss +104 -0
  121. package/src/styles/components/ion-inputs.scss +82 -0
  122. package/src/styles/components/ion-list.scss +44 -0
  123. package/src/styles/components/ion-loading.scss +11 -0
  124. package/src/styles/components/ion-menu.scss +110 -0
  125. package/src/styles/components/ion-modal.scss +42 -0
  126. package/src/styles/components/ion-picker.scss +4 -0
  127. package/src/styles/components/ion-popover.scss +33 -0
  128. package/src/styles/components/ion-range.scss +90 -0
  129. package/src/styles/components/ion-searchbar.scss +68 -0
  130. package/src/styles/components/ion-segment.scss +150 -0
  131. package/src/styles/components/ion-tabs.scss +118 -0
  132. package/src/styles/components/ion-toast.scss +35 -0
  133. package/src/styles/components/ion-toggle.scss +65 -0
  134. package/src/styles/components/ion-toolbar.scss +76 -0
  135. package/src/styles/default-variables.scss +38 -0
  136. package/src/styles/ionic-theme-ios26-dark-always.scss +6 -0
  137. package/src/styles/ionic-theme-ios26-dark-class.scss +6 -0
  138. package/src/styles/ionic-theme-ios26-dark-system.scss +8 -0
  139. package/src/styles/ionic-theme-ios26.scss +23 -0
  140. package/src/styles/md-ion-list-inset.scss +22 -0
  141. package/src/styles/md-remove-ios-class-effect.scss +14 -0
  142. package/src/styles/utils/api.scss +146 -0
  143. package/src/styles/utils/dark/ion-button.scss +58 -0
  144. package/src/styles/utils/dark/ion-fab.scss +18 -0
  145. package/src/styles/utils/dark/ion-segment.scss +12 -0
  146. package/src/styles/utils/dark/ion-tabs.scss +21 -0
  147. package/src/styles/utils/searchable.scss +0 -0
  148. package/src/styles/utils/structured-list.scss +170 -0
  149. package/src/styles/utils/theme-dark.scss +25 -0
  150. package/src/styles/utils/translucent.scss +95 -0
  151. package/src/tab-bar-searchable/animations/enter.ts +92 -0
  152. package/src/tab-bar-searchable/animations/leave.ts +89 -0
  153. package/src/tab-bar-searchable/index.ts +141 -0
  154. package/src/tab-bar-searchable/interfaces.ts +28 -0
  155. package/src/tab-bar-searchable/utils.ts +75 -0
  156. package/src/transition/index.ts +369 -0
  157. package/src/transition/ios.transition.ts +966 -0
  158. package/src/utils.ts +91 -0
package/dist/utils.js ADDED
@@ -0,0 +1,69 @@
1
+ export const getElementRoot = (el, fallback = el) => {
2
+ return el.shadowRoot || fallback;
3
+ };
4
+ export const raf = (h) => {
5
+ if (typeof __zone_symbol__requestAnimationFrame === 'function') {
6
+ return __zone_symbol__requestAnimationFrame(h);
7
+ }
8
+ if (typeof requestAnimationFrame === 'function') {
9
+ return requestAnimationFrame(h);
10
+ }
11
+ return setTimeout(h);
12
+ };
13
+ export const cloneElement = (tagName) => {
14
+ const getCachedEl = document.querySelector(`${tagName}.ion-cloned-element`);
15
+ if (getCachedEl !== null) {
16
+ return getCachedEl;
17
+ }
18
+ const clonedEl = document.createElement(tagName);
19
+ clonedEl.classList.add('ion-cloned-element');
20
+ clonedEl.style.setProperty('display', 'none');
21
+ document.body.appendChild(clonedEl);
22
+ return clonedEl;
23
+ };
24
+ export const getStep = (targetX, animationPosition) => {
25
+ if (animationPosition === undefined) {
26
+ return 0;
27
+ }
28
+ const currentX = targetX - animationPosition.width / 2;
29
+ let progress = (currentX - animationPosition.minPositionX) / (animationPosition.maxPositionX - animationPosition.minPositionX);
30
+ progress = Math.max(0, Math.min(1, progress));
31
+ return progress;
32
+ };
33
+ export const changeSelectedElement = (targetElement, selectedElement, effectTagName, selectedClassName) => {
34
+ targetElement.querySelectorAll(effectTagName).forEach((element) => {
35
+ element.classList.remove(selectedClassName);
36
+ element.classList.remove('ion-activated');
37
+ });
38
+ selectedElement.classList.add('ion-activated');
39
+ };
40
+ export class Config {
41
+ constructor() {
42
+ this.m = new Map();
43
+ }
44
+ reset(configObj) {
45
+ this.m = new Map(Object.entries(configObj));
46
+ }
47
+ get(key, fallback) {
48
+ const value = this.m.get(key);
49
+ return value !== undefined ? value : fallback;
50
+ }
51
+ getBoolean(key, fallback = false) {
52
+ const val = this.m.get(key);
53
+ if (val === undefined) {
54
+ return fallback;
55
+ }
56
+ if (typeof val === 'string') {
57
+ return val === 'true';
58
+ }
59
+ return !!val;
60
+ }
61
+ getNumber(key, fallback) {
62
+ const val = parseFloat(this.m.get(key));
63
+ return isNaN(val) ? (fallback !== undefined ? fallback : NaN) : val;
64
+ }
65
+ set(key, value) {
66
+ this.m.set(key, value);
67
+ }
68
+ }
69
+ export const config = new Config();
@@ -0,0 +1,51 @@
1
+ # E2E screenshot testing
2
+
3
+ This maintainer guide explains how to run the Playwright visual-regression suite for the iOS26 demo. The suite covers every entry declared in `demo/e2e/screenshot.spec.ts` in both light and dark modes. Overlay variants are generated from the shared arrays in `demo/src/app/overlay-types.ts`.
4
+
5
+ ## Run the suite
6
+
7
+ Install the demo dependencies first:
8
+
9
+ ```bash
10
+ cd demo
11
+ npm install
12
+ ```
13
+
14
+ Then choose the command that matches the task:
15
+
16
+ ```bash
17
+ npm run test:e2e # Run the suite
18
+ npm run test:e2e:ui # Open Playwright UI mode
19
+ npm run test:e2e:debug # Run with the Playwright debugger
20
+ npm run test:e2e:update # Regenerate intentional baseline changes
21
+ ```
22
+
23
+ To reproduce the Linux environment used by CI, run the Docker variants from `demo/`:
24
+
25
+ ```bash
26
+ npm run test:e2e:docker
27
+ npm run test:e2e:docker:update
28
+ ```
29
+
30
+ The Docker commands use the Playwright image pinned in `demo/package.json`.
31
+
32
+ ## Review a failure
33
+
34
+ A screenshot mismatch can be a regression or an intentional visual change. Before updating a baseline:
35
+
36
+ 1. Inspect the actual, expected, and diff images in `demo/test-results/`.
37
+ 2. Check the affected route in both light and dark modes.
38
+ 3. Confirm that the component change is intentional.
39
+ 4. Regenerate the baseline with `npm run test:e2e:update`, or use the Docker variant when matching CI rendering.
40
+
41
+ The HTML report is written to `demo/playwright-report/` and can be opened with:
42
+
43
+ ```bash
44
+ npx playwright show-report
45
+ ```
46
+
47
+ ## Add coverage
48
+
49
+ When adding a demo route or overlay variant, update `demo/e2e/screenshot.spec.ts` and regenerate the relevant baselines. Commit baseline changes only after reviewing the visual diff.
50
+
51
+ Pull requests targeting `main` or `ios26` run the E2E workflow in `.github/workflows/e2e-pull_request.yml`; pushes to either branch run `.github/workflows/e2e-main.yml`. GitHub builds the pull-request merge ref against its target branch, so an `ios26` pull request starts with the iOS 26 snapshots. Snapshot changes intentionally committed by the pull request are then tested as part of that merge result. The `/update-screenshots` command loads its update procedure from the pull request's target branch and commits the regenerated screenshots to the pull request branch.
package/docs/eslint.md ADDED
@@ -0,0 +1,73 @@
1
+ ---
2
+ title: Keep lists consistent with ESLint
3
+ ---
4
+
5
+ Catch missing list groups before they reach the screen. In Ionic Angular apps, `@rdlabo/eslint-plugin-rules` checks the markup used by iOS 26.
6
+
7
+ ## Enable the list check
8
+
9
+ This setup targets Ionic Angular 9 with Angular / Angular ESLint 21–22. Install plugin 22 in an app that already has Angular ESLint configured:
10
+
11
+ ```sh
12
+ npm install --save-dev @rdlabo/eslint-plugin-rules@22
13
+ ```
14
+
15
+ Add the plugin to the existing HTML config in `eslint.config.mjs` (or the equivalent CommonJS config). Keep `angular.processInlineTemplates` on the TypeScript config so inline templates are checked too:
16
+
17
+ ```js
18
+ import tseslint from 'typescript-eslint';
19
+ import angular from 'angular-eslint';
20
+ import rdlabo from '@rdlabo/eslint-plugin-rules';
21
+
22
+ export default tseslint.config(
23
+ {
24
+ files: ['**/*.ts'],
25
+ languageOptions: { parser: tseslint.parser },
26
+ processor: angular.processInlineTemplates,
27
+ },
28
+ {
29
+ files: ['**/*.html'],
30
+ languageOptions: { parser: angular.templateParser },
31
+ plugins: { '@rdlabo/rules': rdlabo },
32
+ rules: {
33
+ '@rdlabo/rules/require-ion-item-group': 'error',
34
+ },
35
+ },
36
+ );
37
+ ```
38
+
39
+ Merge these entries into your existing configuration to retain its other checks. Already using `rdlabo.configs.recommended`? It includes this rule.
40
+
41
+ ## See what it catches
42
+
43
+ This template is reported:
44
+
45
+ ```html
46
+ <ion-list [inset]="true">
47
+ <ion-item>Notifications</ion-item>
48
+ </ion-list>
49
+ ```
50
+
51
+ Group the items and import `IonItemGroup` in the standalone component:
52
+
53
+ ```html
54
+ <ion-list [inset]="true">
55
+ <ion-item-group>
56
+ <ion-item>Notifications</ion-item>
57
+ </ion-item-group>
58
+ </ion-list>
59
+ ```
60
+
61
+ The rule checks all `ion-list` elements, including non-inset lists. Radio, reorder, and accordion groups are also supported. It checks Angular templates; it does not inspect React or Vue templates.
62
+
63
+ See [Using ion-item-group](./using-ion-item-group.md) for layout examples and the [rule reference](https://docs.rdlabo.dev/projects/eslint-plugin-rules/docs/rules/require-ion-item-group) for supported structures and automatic fixes.
64
+
65
+ ## Keep it checked
66
+
67
+ Run this locally and in CI after installing dependencies:
68
+
69
+ ```sh
70
+ npx eslint 'src/**/*.{ts,html}' --max-warnings 0
71
+ ```
72
+
73
+ If your Angular project uses a different source directory, adjust the path. Keep a visual check for spacing, colors, and transitions; this rule checks list structure.
@@ -0,0 +1,85 @@
1
+ ---
2
+ title: Experimental animation
3
+ ---
4
+
5
+ # Experimental animation
6
+
7
+ These gesture and animation helpers are experimental and optional. The theme works without them.
8
+
9
+ ## Sheet of Glass with `ion-tab-button` / `ion-segment-button`
10
+
11
+ Register an `ion-tab-bar` or `ion-segment` element to add a moving selection effect to its buttons.
12
+
13
+ [![Sheet of Glass animation on ion-tab-button and ion-segment-button](https://i.gyazo.com/fafd726b520827f042c76b6c73abd81c.gif)](https://gyazo.com/fafd726b520827f042c76b6c73abd81c)
14
+
15
+ ```ts
16
+ import { registerTabBarEffect, registerSegmentEffect } from '@rdlabo/ionic-theme-ios27';
17
+
18
+ /**
19
+ * Register DOM elements. Effects are applied using Ionic Gesture and Ionic Animation.
20
+ */
21
+ const tabBar = document.querySelector<HTMLElement>('ion-tab-bar');
22
+ const segment = document.querySelector<HTMLElement>('ion-segment');
23
+ const registeredTabBarEffect = tabBar ? registerTabBarEffect(tabBar) : undefined;
24
+ const registeredSegmentEffect = segment ? registerSegmentEffect(segment) : undefined;
25
+
26
+ const destroy = () => {
27
+ /**
28
+ * If the registered DOM element is removed (e.g., due to page navigation),
29
+ * make sure to destroy the gesture and animation. This will also remove the event listeners.
30
+ * You can re-register them if needed.
31
+ */
32
+ registeredTabBarEffect?.destroy();
33
+ registeredSegmentEffect?.destroy();
34
+ };
35
+ ```
36
+
37
+ ## TabBarSearchable: Searchable with `ion-tab-bar` and `ion-fab-button`
38
+
39
+ Use the following structure inside `ion-tabs` to animate a search button into a search toolbar.
40
+
41
+ [![TabBarSearchable animation expanding search from ion-fab-button into the tab bar](https://i.gyazo.com/06bc63f4a474f9f19f5b1d865f5c2a85.gif)](https://gyazo.com/06bc63f4a474f9f19f5b1d865f5c2a85)
42
+
43
+ ```html
44
+ <ion-content>...</ion-content>
45
+ <ion-fab vertical="bottom" horizontal="end" slot="fixed">
46
+ <ion-fab-button (click)="present($event)">
47
+ <ion-icon name="search"></ion-icon>
48
+ </ion-fab-button>
49
+ </ion-fab>
50
+ <ion-footer [translucent]="true">
51
+ <ion-toolbar>
52
+ <ion-buttons slot="start">
53
+ <!-- ion-icon name is set dynamically by the animation -->
54
+ <ion-button fill="default"><ion-icon slot="icon-only"></ion-icon> </ion-button>
55
+ </ion-buttons>
56
+ <!-- User set `ionChange` or other events. -->
57
+ <ion-searchbar (ionChange)="example($event)"></ion-searchbar>
58
+ </ion-toolbar>
59
+ </ion-footer>
60
+ ```
61
+
62
+ ```ts
63
+ import { attachTabBarSearchable, TabBarSearchableType } from '@rdlabo/ionic-theme-ios27';
64
+ import type { TabBarSearchableFunction } from '@rdlabo/ionic-theme-ios27';
65
+
66
+ let searchableFun: TabBarSearchableFunction | undefined;
67
+ const initialize = () => {
68
+ // attachTabBarSearchable has state. You should initialize per page.
69
+ const tabBar = document.querySelector<HTMLElement>('ion-tab-bar');
70
+ const fabButton = document.querySelector<HTMLElement>('ion-fab-button');
71
+ const footer = document.querySelector<HTMLElement>('ion-footer');
72
+ if (!tabBar || !fabButton || !footer) {
73
+ return;
74
+ }
75
+ searchableFun = attachTabBarSearchable(tabBar, fabButton, footer);
76
+ };
77
+
78
+ const present = (event: Event) => {
79
+ searchableFun!(event, TabBarSearchableType.Enter);
80
+ };
81
+
82
+ const dismiss = (event: Event) => {
83
+ searchableFun!(event, TabBarSearchableType.Leave);
84
+ };
85
+ ```
@@ -0,0 +1,88 @@
1
+ ---
2
+ title: Features
3
+ ---
4
+
5
+ # Features
6
+
7
+ Customize the theme with CSS variables and Sass mixins, or adopt it one component at a time. Markup-specific opt-ins are documented in [Special markup and classes](./special-markup.md).
8
+
9
+ ## CSS variables
10
+
11
+ To customize the library's default styles to match your design, several CSS variables are provided. See this file for details:
12
+ [Default variables](../src/styles/default-variables.scss)
13
+
14
+ ## Liquid Glass mixin
15
+
16
+ Import the SCSS files from the main package to use the liquid glass mixin.
17
+
18
+ ```scss
19
+ @use '@rdlabo/ionic-theme-ios27/src/styles/utils/api.scss';
20
+
21
+ ion-textarea label.textarea-wrapper {
22
+ @include api.glass-background;
23
+ }
24
+ ```
25
+
26
+ ## Selective component imports
27
+
28
+ For gradual adoption, you can import individual components instead of the full theme file.
29
+
30
+ ```css
31
+ @import '@rdlabo/ionic-theme-ios27/dist/css/utils/translucent';
32
+ @import '@rdlabo/ionic-theme-ios27/dist/css/components/ion-action-sheet';
33
+ @import '@rdlabo/ionic-theme-ios27/dist/css/components/ion-alert';
34
+ @import '@rdlabo/ionic-theme-ios27/dist/css/components/ion-button';
35
+ /* Import the remaining components your application uses. */
36
+ ```
37
+
38
+ ### Dark mode with individual components
39
+
40
+ Use SCSS when selectively importing components with dark mode support because the selectors differ between Always, System, and Class modes.
41
+
42
+ Always:
43
+
44
+ ```scss
45
+ @use '@rdlabo/ionic-theme-ios27/src/styles/utils/theme-dark';
46
+
47
+ :root {
48
+ @include theme-dark.default-variables;
49
+ }
50
+ @include theme-dark.ion-button;
51
+ @include theme-dark.ion-fab;
52
+ @include theme-dark.ion-tabs;
53
+ @include theme-dark.ion-segment;
54
+ ```
55
+
56
+ System:
57
+
58
+ ```scss
59
+ @use '@rdlabo/ionic-theme-ios27/src/styles/utils/theme-dark';
60
+
61
+ @media (prefers-color-scheme: dark) {
62
+ :root {
63
+ @include theme-dark.default-variables;
64
+ }
65
+ @include theme-dark.ion-button;
66
+ @include theme-dark.ion-fab;
67
+ @include theme-dark.ion-tabs;
68
+ @include theme-dark.ion-segment;
69
+ }
70
+ ```
71
+
72
+ Class:
73
+
74
+ ```scss
75
+ @use '@rdlabo/ionic-theme-ios27/src/styles/utils/theme-dark';
76
+
77
+ .ion-palette-dark {
78
+ @include theme-dark.default-variables;
79
+ @include theme-dark.ion-button;
80
+ @include theme-dark.ion-fab;
81
+ @include theme-dark.ion-tabs;
82
+ @include theme-dark.ion-segment;
83
+ }
84
+ ```
85
+
86
+ ## Interactive examples
87
+
88
+ [Browse rendered examples in the demo](https://ionic-theme-ios27.rdlabo.dev/main/docs).
@@ -0,0 +1,65 @@
1
+ # Feature requests for Ionic Framework
2
+
3
+ ## feat(): change `--knob-handle-size` to `--knob-handle-width` / `--knob-handle-height`
4
+
5
+ Currently, only square sizes are taken into consideration.
6
+ Overwriting the `knob` itself is possible, but `--knob-handle-size` cannot be ignored as it plays a crucial role in determining the `top` and `margin-inline-start` of `div.range-knob-handle`.
7
+
8
+ ## feat(): ion-config new property for `collapse`
9
+
10
+ Currently, `collapse` behavior is automatically enabled in iOS mode, but this should be configurable through `ion-config` for better control.
11
+
12
+ ex:
13
+
14
+ ```typescript
15
+ export interface IonicConfig {
16
+ ...,
17
+ collapseLargeTitle: {
18
+ ios: boolean;
19
+ md: boolean;
20
+ ionic: boolean;
21
+ },
22
+ collapseBackButtonAnimation: {
23
+ ios: boolean;
24
+ md: boolean;
25
+ ionic: boolean;
26
+ },
27
+ }
28
+ ```
29
+
30
+ ## feat(): add native shadow-part for design
31
+
32
+ ### native-inner(or item-inner) part to ion-item
33
+
34
+ The styling for `ion-item[lines=inset]` is applied to `.item-inner`, which cannot be styled directly. This limitation means that for iOS 26 styling, I can only modify the border-bottom style through `::part(native)` with padding-right, preventing me from utilizing the full right side of `ion-item`. Adding `::part(native-inner)` would increase styling flexibility.
35
+
36
+ ```diff
37
+ <ion-item>
38
+ <button type="button" class="item-native" part="native">
39
+ - <div class="item-inner">
40
+ + <div class="item-inner" part="native-inner">
41
+ ...
42
+ </div>
43
+ </button>
44
+ </ion-item>
45
+ ```
46
+
47
+ ### native part to ion-toast
48
+
49
+ Resolved: https://github.com/ionic-team/ionic-framework/pull/30992#event-23306774962
50
+
51
+ ## docs(): Naming conventions for Ionic theme classes
52
+
53
+ Resolved.
54
+
55
+ ### should disable ion-back-button Animation
56
+
57
+ Resolved: by created https://github.com/rdlabo-dev/ionic-theme-ios26/tree/main/src/transition
58
+
59
+ ## feat(): ion-content[fullscreen=true] will have .content-fullscreen class
60
+
61
+ Resolved: https://github.com/ionic-team/ionic-framework/pull/30926
62
+
63
+ ## feat(): add .range-knob-min and .range-knob-max directly to ion-range
64
+
65
+ Resolved: https://github.com/ionic-team/ionic-framework/pull/30932
package/docs/ios-18.md ADDED
@@ -0,0 +1,16 @@
1
+ ---
2
+ title: Supporting iOS 18
3
+ ---
4
+
5
+ # Supporting iOS 18
6
+
7
+ ## How to prevent loading the theme on iOS 18
8
+
9
+ 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`.
10
+
11
+ ```css
12
+ @import '@rdlabo/ionic-theme-ios27/dist/css/default-variables.css' supports(text-wrap: pretty);
13
+ @import '@rdlabo/ionic-theme-ios27/dist/css/ionic-theme-ios26.css' supports(text-wrap: pretty);
14
+ @import '@rdlabo/ionic-theme-ios27/dist/css/md-remove-ios-class-effect.css' supports(text-wrap: pretty);
15
+ @import '@rdlabo/ionic-theme-ios27/dist/css/md-ion-list-inset.css' supports(text-wrap: pretty);
16
+ ```
@@ -0,0 +1,94 @@
1
+ ---
2
+ title: Migration
3
+ ---
4
+
5
+ # Migration
6
+
7
+ Review every section newer than the version currently installed, in ascending order. For example, when upgrading from 1.x to 9.0.0, complete the 2.0.0 and 3.0.0 migration steps before reviewing 9.0.0.
8
+
9
+ Each section lists only the changes that require application code or configuration updates.
10
+
11
+ ## Migrating to 9.0.0
12
+
13
+ Version 9 aligns the theme's major version with Ionic Framework 9. It does not introduce additional breaking changes beyond those documented in the earlier migration sections.
14
+
15
+ Both Ionic 8 and Ionic 9 remain supported. Version 9 requires `@ionic/core >=8.8.1 <10`.
16
+
17
+ ## Migrating to 3.0.0
18
+
19
+ ### Rename `.header-item-group` to `.item-group-header`
20
+
21
+ The class for an `ion-item-group` used as a section header has been renamed for consistency with the element it modifies. Replace every occurrence of `.header-item-group` in application templates and styles.
22
+
23
+ ```diff
24
+ - <ion-item-group class="header-item-group">
25
+ + <ion-item-group class="item-group-header">
26
+ ...
27
+ </ion-item-group>
28
+ ```
29
+
30
+ The old class is no longer styled by the theme. This rename applies to markup shared with `@rdlabo/ionic-theme-md3` as well.
31
+
32
+ ## Migrating to 2.0.0
33
+
34
+ ### Configure `iosTransitionAnimation`
35
+
36
+ Version 2 requires the package navigation transition. It follows Ionic's default iOS transition without the obsolete `animateBackButton()` behavior that animated a Large Title into the back-button label.
37
+
38
+ ```ts
39
+ import { isPlatform } from '@ionic/core'; // or @ionic/angular/standalone, @ionic/react, @ionic/vue
40
+ import { iosTransitionAnimation } from '@rdlabo/ionic-theme-ios26';
41
+
42
+ // Angular
43
+ provideIonicAngular({
44
+ // ...
45
+ navAnimation: isPlatform('ios') ? iosTransitionAnimation : undefined,
46
+ });
47
+
48
+ // React
49
+ setupIonicReact({
50
+ // ...
51
+ navAnimation: isPlatform('ios') ? iosTransitionAnimation : undefined,
52
+ });
53
+
54
+ // Vue
55
+ createApp(App).use(IonicVue, {
56
+ // ...
57
+ navAnimation: isPlatform('ios') ? iosTransitionAnimation : undefined,
58
+ });
59
+ ```
60
+
61
+ With this transition configured, `<ion-buttons><ion-back-button></ion-back-button></ion-buttons>` can be used without the unwanted transition side effects caused by the old animation.
62
+
63
+ ## Migrating to 1.0.0
64
+
65
+ ### Update SCSS import paths
66
+
67
+ The source files moved under `src/styles` when JavaScript files were added to the package.
68
+
69
+ ```diff
70
+ - @import '@rdlabo/ionic-theme-ios26/src/default-variables.scss';
71
+ + @import '@rdlabo/ionic-theme-ios26/src/styles/default-variables.scss';
72
+ ```
73
+
74
+ Generated CSS paths under `dist` did not change.
75
+
76
+ ### Rename `--ios26-color-background-rgb`
77
+
78
+ ```diff
79
+ :root {
80
+ - --ios26-color-background-rgb: 255, 255, 255;
81
+ + --ios26-content-box-shadow-rgb: 255, 255, 255;
82
+ }
83
+ ```
84
+
85
+ ### Rename brightness variables
86
+
87
+ Replace each `--ion-color-*-brightness-rgb` variable with `--ion-color-*-brightness` and use a color value instead of an RGB channel list.
88
+
89
+ ```diff
90
+ :root {
91
+ - --ion-color-primary-brightness-rgb: 130, 255, 255;
92
+ + --ion-color-primary-brightness: #96feff;
93
+ }
94
+ ```
@@ -0,0 +1,145 @@
1
+ ---
2
+ title: Special markup and classes
3
+ ---
4
+
5
+ # Special markup and classes
6
+
7
+ Most Ionic markup works without changes. The combinations below are explicit opt-ins provided by the theme.
8
+
9
+ ## Primary submit buttons
10
+
11
+ 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.
12
+
13
+ ```css
14
+ :root {
15
+ --ion-color-primary-brightness: #96feff;
16
+ }
17
+ ```
18
+
19
+ ```html preview
20
+ <ion-button type="submit" color="primary">Submit</ion-button>
21
+ <ion-button class="button-submit" fill="solid" color="primary">Continue</ion-button>
22
+ ```
23
+
24
+ Use `.button-submit` when the button needs the same treatment but cannot use `type="submit"`.
25
+
26
+ ## Two-line inset list items
27
+
28
+ 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.
29
+
30
+ ```html preview
31
+ <ion-list inset="true">
32
+ <ion-list-header>
33
+ <ion-label>Connections</ion-label>
34
+ </ion-list-header>
35
+ <ion-item-group>
36
+ <ion-item>
37
+ <ion-label>Network &amp; internet</ion-label>
38
+ <ion-note>Mobile, Wi-Fi, hotspot</ion-note>
39
+ </ion-item>
40
+ </ion-item-group>
41
+ </ion-list>
42
+ ```
43
+
44
+ Use `slot="end"` on `ion-note` when you want the standard trailing-note layout instead.
45
+
46
+ ## Inset-list section headers
47
+
48
+ 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.
49
+
50
+ This is an introductory group. Place regular list items in a separate `ion-item-group` that follows it.
51
+
52
+ ```html preview
53
+ <ion-list inset="true">
54
+ <ion-item-group class="item-group-header">
55
+ <ion-item>
56
+ <ion-label>
57
+ <ion-icon name="list" style="background: var(--ion-color-primary)"></ion-icon>
58
+ <h2>Lists</h2>
59
+ <ion-text>Inset-list examples</ion-text>
60
+ </ion-label>
61
+ </ion-item>
62
+ </ion-item-group>
63
+ <ion-item-group>
64
+ <ion-item><ion-label>First item</ion-label></ion-item>
65
+ </ion-item-group>
66
+ </ion-list>
67
+ ```
68
+
69
+ ## Full-width segments
70
+
71
+ 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.
72
+
73
+ ```html preview
74
+ <ion-segment class="segment-expand" value="new">
75
+ <ion-segment-button value="new"><ion-label>New</ion-label></ion-segment-button>
76
+ <ion-segment-button value="replied"><ion-label>Replied</ion-label></ion-segment-button>
77
+ </ion-segment>
78
+ ```
79
+
80
+ ## Classic search bar in a condense header
81
+
82
+ The theme gives iOS search bars the iOS 26 appearance by default. Add `.searchbar-classic` to the search field shown beneath a large title in an `ion-header` with `collapse="condense"`. It uses the conventional filled iOS appearance and collapses with the large title instead of remaining in the fixed header.
83
+
84
+ Place it in a toolbar with a color, such as `color="light"`; the classic background is derived from that color's contrast value.
85
+
86
+ The example uses Ionic's standard collapsible large-title structure. Scroll the preview to collapse the large title and reveal the fixed header.
87
+
88
+ ```html preview
89
+ <div class="ion-page">
90
+ <ion-header translucent="true">
91
+ <ion-toolbar color="light">
92
+ <ion-title>Search</ion-title>
93
+ </ion-toolbar>
94
+ </ion-header>
95
+ <ion-content color="light" fullscreen="true">
96
+ <ion-header collapse="condense">
97
+ <ion-toolbar color="light">
98
+ <ion-title size="large">Search</ion-title>
99
+ </ion-toolbar>
100
+ <ion-toolbar color="light">
101
+ <ion-searchbar class="searchbar-classic" placeholder="Filter results"></ion-searchbar>
102
+ </ion-toolbar>
103
+ </ion-header>
104
+ <ion-list inset="true">
105
+ <ion-item-group>
106
+ <ion-item><ion-label>Recent item 1</ion-label></ion-item>
107
+ <ion-item><ion-label>Recent item 2</ion-label></ion-item>
108
+ <ion-item><ion-label>Recent item 3</ion-label></ion-item>
109
+ <ion-item><ion-label>Recent item 4</ion-label></ion-item>
110
+ <ion-item><ion-label>Recent item 5</ion-label></ion-item>
111
+ <ion-item><ion-label>Recent item 6</ion-label></ion-item>
112
+ <ion-item><ion-label>Recent item 7</ion-label></ion-item>
113
+ <ion-item><ion-label>Recent item 8</ion-label></ion-item>
114
+ <ion-item><ion-label>Recent item 9</ion-label></ion-item>
115
+ <ion-item><ion-label>Recent item 10</ion-label></ion-item>
116
+ </ion-item-group>
117
+ </ion-list>
118
+ </ion-content>
119
+ </div>
120
+ ```
121
+
122
+ 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`.
123
+
124
+ ## Search-bar toolbars
125
+
126
+ 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.
127
+
128
+ ```html preview
129
+ <ion-toolbar class="toolbar-searchbar">
130
+ <ion-buttons slot="start">
131
+ <ion-button>Cancel</ion-button>
132
+ </ion-buttons>
133
+ <ion-searchbar></ion-searchbar>
134
+ </ion-toolbar>
135
+ ```
136
+
137
+ ## Opting out
138
+
139
+ Add `.ios26-disabled` to an individual Ionic component when it must retain Ionic's standard iOS styling.
140
+
141
+ ```html preview
142
+ <ion-button>iOS 26 theme</ion-button> <ion-button class="ios26-disabled">Standard Ionic button</ion-button>
143
+ ```
144
+
145
+ For the background model behind inset lists, see [Using `ion-item-group`](./using-ion-item-group.md).