@rdlabo/ionic-theme-ios27 0.1.0 → 0.2.1-beta.pr185.sha0793981f6cf4

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 (148) hide show
  1. package/README.md +34 -8
  2. package/dist/css/components/ion-button.css +1 -1
  3. package/dist/css/components/ion-fab.css +1 -1
  4. package/dist/css/components/ion-list.css +1 -1
  5. package/dist/css/components/ion-menu.css +1 -1
  6. package/dist/css/components/ion-searchbar.css +1 -1
  7. package/dist/css/components/ion-tabs.css +1 -1
  8. package/dist/css/components/ion-toolbar.css +1 -1
  9. package/dist/css/ionic-theme-ios27.css +1 -1
  10. package/dist/css/md-ion-list-inset.css +1 -1
  11. package/dist/css/utils/translucent.css +1 -1
  12. package/dist/index.d.ts +1 -2
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +1 -2
  15. package/dist/native/components/searchable-tabs.d.ts +2 -0
  16. package/dist/native/components/searchable-tabs.d.ts.map +1 -1
  17. package/dist/native/components/searchable-tabs.js +131 -21
  18. package/dist/native/definitions.d.ts +19 -0
  19. package/dist/native/definitions.d.ts.map +1 -1
  20. package/dist/native/index.d.ts +4 -3
  21. package/dist/native/index.d.ts.map +1 -1
  22. package/dist/native/index.js +22 -9
  23. package/dist/native/lifecycle.d.ts +4 -0
  24. package/dist/native/lifecycle.d.ts.map +1 -0
  25. package/dist/native/lifecycle.js +34 -0
  26. package/dist/native/runtime.d.ts +2 -2
  27. package/dist/native/runtime.d.ts.map +1 -1
  28. package/dist/native/runtime.js +57 -11
  29. package/dist/tab-bar-searchable/animations/enter.d.ts +1 -1
  30. package/dist/tab-bar-searchable/animations/enter.d.ts.map +1 -1
  31. package/dist/tab-bar-searchable/animations/enter.js +1 -1
  32. package/dist/tab-bar-searchable/animations/leave.d.ts +1 -1
  33. package/dist/tab-bar-searchable/animations/leave.d.ts.map +1 -1
  34. package/dist/tab-bar-searchable/animations/leave.js +1 -1
  35. package/dist/tab-bar-searchable/index.d.ts +2 -2
  36. package/dist/tab-bar-searchable/index.d.ts.map +1 -1
  37. package/dist/tab-bar-searchable/index.js +2 -3
  38. package/dist/transition/ios.transition.d.ts +6 -1
  39. package/dist/transition/ios.transition.d.ts.map +1 -1
  40. package/dist/transition/ios.transition.js +10 -385
  41. package/docs/features.md +3 -1
  42. package/docs/ios-adaptive.md +20 -21
  43. package/docs/native-ui-shell.md +117 -3
  44. package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellSearchController.swift +243 -64
  45. package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellTabBar.swift +77 -6
  46. package/ios/Sources/IonicNativeUIShellPlugin/IonicNativeUIShellPlugin.swift +106 -11
  47. package/ios/Tests/IonicNativeUIShellPluginTests/ShellSnapshotTests.swift +258 -2
  48. package/node_modules/@rdlabo/ionic-theme-utils/LICENSE +21 -0
  49. package/node_modules/@rdlabo/ionic-theme-utils/README.md +28 -0
  50. package/node_modules/@rdlabo/ionic-theme-utils/dist/dom.d.ts +3 -0
  51. package/node_modules/@rdlabo/ionic-theme-utils/dist/dom.d.ts.map +1 -0
  52. package/node_modules/@rdlabo/ionic-theme-utils/dist/dom.js +13 -0
  53. package/node_modules/@rdlabo/ionic-theme-utils/dist/dom.js.map +1 -0
  54. package/node_modules/@rdlabo/ionic-theme-utils/dist/focus-controller.d.ts +6 -0
  55. package/node_modules/@rdlabo/ionic-theme-utils/dist/focus-controller.d.ts.map +1 -0
  56. package/node_modules/@rdlabo/ionic-theme-utils/dist/focus-controller.js +37 -0
  57. package/node_modules/@rdlabo/ionic-theme-utils/dist/focus-controller.js.map +1 -0
  58. package/node_modules/@rdlabo/ionic-theme-utils/dist/index.d.ts +13 -0
  59. package/node_modules/@rdlabo/ionic-theme-utils/dist/index.d.ts.map +1 -0
  60. package/node_modules/@rdlabo/ionic-theme-utils/dist/index.js +11 -0
  61. package/node_modules/@rdlabo/ionic-theme-utils/dist/index.js.map +1 -0
  62. package/{dist → node_modules/@rdlabo/ionic-theme-utils/dist}/popover/animations/ios.enter.d.ts +0 -1
  63. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/animations/ios.enter.d.ts.map +1 -0
  64. package/{dist → node_modules/@rdlabo/ionic-theme-utils/dist}/popover/animations/ios.enter.js +8 -5
  65. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/animations/ios.enter.js.map +1 -0
  66. package/{dist → node_modules/@rdlabo/ionic-theme-utils/dist}/popover/animations/ios.leave.d.ts.map +1 -1
  67. package/{dist → node_modules/@rdlabo/ionic-theme-utils/dist}/popover/animations/ios.leave.js +11 -5
  68. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/animations/ios.leave.js.map +1 -0
  69. package/{dist → node_modules/@rdlabo/ionic-theme-utils/dist}/popover/callout-surface.js +1 -0
  70. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/callout-surface.js.map +1 -0
  71. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/popover-interface.js +2 -0
  72. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/popover-interface.js.map +1 -0
  73. package/{dist → node_modules/@rdlabo/ionic-theme-utils/dist}/popover/utils.d.ts +1 -0
  74. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/utils.d.ts.map +1 -0
  75. package/{dist → node_modules/@rdlabo/ionic-theme-utils/dist}/popover/utils.js +3 -2
  76. package/node_modules/@rdlabo/ionic-theme-utils/dist/popover/utils.js.map +1 -0
  77. package/node_modules/@rdlabo/ionic-theme-utils/dist/sheets-of-glass.d.ts +16 -0
  78. package/node_modules/@rdlabo/ionic-theme-utils/dist/sheets-of-glass.d.ts.map +1 -0
  79. package/node_modules/@rdlabo/ionic-theme-utils/dist/sheets-of-glass.js +2 -0
  80. package/node_modules/@rdlabo/ionic-theme-utils/dist/sheets-of-glass.js.map +1 -0
  81. package/{dist → node_modules/@rdlabo/ionic-theme-utils/dist}/tab-bar-searchable/interfaces.js +1 -0
  82. package/node_modules/@rdlabo/ionic-theme-utils/dist/tab-bar-searchable/interfaces.js.map +1 -0
  83. package/{dist → node_modules/@rdlabo/ionic-theme-utils/dist}/tab-bar-searchable/utils.js +1 -0
  84. package/node_modules/@rdlabo/ionic-theme-utils/dist/tab-bar-searchable/utils.js.map +1 -0
  85. package/node_modules/@rdlabo/ionic-theme-utils/dist/theme.d.ts +15 -0
  86. package/node_modules/@rdlabo/ionic-theme-utils/dist/theme.d.ts.map +1 -0
  87. package/node_modules/@rdlabo/ionic-theme-utils/dist/theme.js +49 -0
  88. package/node_modules/@rdlabo/ionic-theme-utils/dist/theme.js.map +1 -0
  89. package/node_modules/@rdlabo/ionic-theme-utils/dist/transition/ios.transition.d.ts +18 -0
  90. package/node_modules/@rdlabo/ionic-theme-utils/dist/transition/ios.transition.d.ts.map +1 -0
  91. package/node_modules/@rdlabo/ionic-theme-utils/dist/transition/ios.transition.js +412 -0
  92. package/node_modules/@rdlabo/ionic-theme-utils/dist/transition/ios.transition.js.map +1 -0
  93. package/node_modules/@rdlabo/ionic-theme-utils/package.json +64 -0
  94. package/node_modules/@rdlabo/ionic-theme-utils/src/dom.spec.ts +19 -0
  95. package/node_modules/@rdlabo/ionic-theme-utils/src/dom.ts +16 -0
  96. package/node_modules/@rdlabo/ionic-theme-utils/src/focus-controller.ts +38 -0
  97. package/node_modules/@rdlabo/ionic-theme-utils/src/index.ts +59 -0
  98. package/{src → node_modules/@rdlabo/ionic-theme-utils/src}/popover/animations/ios.enter.ts +7 -5
  99. package/{src → node_modules/@rdlabo/ionic-theme-utils/src}/popover/animations/ios.leave.ts +14 -5
  100. package/node_modules/@rdlabo/ionic-theme-utils/src/popover/utils.spec.ts +58 -0
  101. package/{src → node_modules/@rdlabo/ionic-theme-utils/src}/popover/utils.ts +4 -2
  102. package/node_modules/@rdlabo/ionic-theme-utils/src/sheets-of-glass.ts +17 -0
  103. package/node_modules/@rdlabo/ionic-theme-utils/src/styles/index.scss +1 -0
  104. package/node_modules/@rdlabo/ionic-theme-utils/src/styles/md-ion-list-inset.scss +20 -0
  105. package/node_modules/@rdlabo/ionic-theme-utils/src/styles/structured-list.scss +310 -0
  106. package/node_modules/@rdlabo/ionic-theme-utils/src/tab-bar-searchable/utils.spec.ts +56 -0
  107. package/node_modules/@rdlabo/ionic-theme-utils/src/theme.ts +51 -0
  108. package/node_modules/@rdlabo/ionic-theme-utils/src/transition/ios.transition.spec.ts +52 -0
  109. package/node_modules/@rdlabo/ionic-theme-utils/src/transition/ios.transition.ts +542 -0
  110. package/package.json +13 -5
  111. package/src/index.ts +4 -2
  112. package/src/native/components/searchable-tabs.ts +142 -22
  113. package/src/native/definitions.ts +34 -0
  114. package/src/native/index.ts +38 -11
  115. package/src/native/lifecycle.ts +38 -0
  116. package/src/native/runtime.ts +60 -10
  117. package/src/styles/components/ion-button.scss +21 -10
  118. package/src/styles/components/ion-fab.scss +10 -4
  119. package/src/styles/components/ion-list.scss +12 -3
  120. package/src/styles/components/ion-menu.scss +17 -10
  121. package/src/styles/components/ion-searchbar.scss +1 -1
  122. package/src/styles/components/ion-tabs.scss +14 -10
  123. package/src/styles/components/ion-toolbar.scss +9 -10
  124. package/src/styles/md-ion-list-inset.scss +2 -2
  125. package/src/styles/utils/dark/ion-list.scss +1 -1
  126. package/src/styles/utils/translucent.scss +44 -0
  127. package/src/tab-bar-searchable/animations/enter.ts +1 -2
  128. package/src/tab-bar-searchable/animations/leave.ts +1 -2
  129. package/src/tab-bar-searchable/index.ts +12 -4
  130. package/src/transition/ios.transition.ts +14 -473
  131. package/dist/css/utils/structured-list.css +0 -1
  132. package/dist/popover/animations/ios.enter.d.ts.map +0 -1
  133. package/dist/popover/popover-interface.js +0 -1
  134. package/dist/popover/utils.d.ts.map +0 -1
  135. package/src/styles/utils/structured-list.scss +0 -223
  136. /package/{dist → node_modules/@rdlabo/ionic-theme-utils/dist}/popover/animations/ios.leave.d.ts +0 -0
  137. /package/{dist → node_modules/@rdlabo/ionic-theme-utils/dist}/popover/callout-surface.d.ts +0 -0
  138. /package/{dist → node_modules/@rdlabo/ionic-theme-utils/dist}/popover/callout-surface.d.ts.map +0 -0
  139. /package/{dist → node_modules/@rdlabo/ionic-theme-utils/dist}/popover/popover-interface.d.ts +0 -0
  140. /package/{dist → node_modules/@rdlabo/ionic-theme-utils/dist}/popover/popover-interface.d.ts.map +0 -0
  141. /package/{dist → node_modules/@rdlabo/ionic-theme-utils/dist}/tab-bar-searchable/interfaces.d.ts +0 -0
  142. /package/{dist → node_modules/@rdlabo/ionic-theme-utils/dist}/tab-bar-searchable/interfaces.d.ts.map +0 -0
  143. /package/{dist → node_modules/@rdlabo/ionic-theme-utils/dist}/tab-bar-searchable/utils.d.ts +0 -0
  144. /package/{dist → node_modules/@rdlabo/ionic-theme-utils/dist}/tab-bar-searchable/utils.d.ts.map +0 -0
  145. /package/{src → node_modules/@rdlabo/ionic-theme-utils/src}/popover/callout-surface.ts +0 -0
  146. /package/{src → node_modules/@rdlabo/ionic-theme-utils/src}/popover/popover-interface.ts +0 -0
  147. /package/{src → node_modules/@rdlabo/ionic-theme-utils/src}/tab-bar-searchable/interfaces.ts +0 -0
  148. /package/{src → node_modules/@rdlabo/ionic-theme-utils/src}/tab-bar-searchable/utils.ts +0 -0
package/docs/features.md CHANGED
@@ -13,6 +13,8 @@ To customize the library's default styles to match your design, several CSS vari
13
13
 
14
14
  For menus, `--ios-theme-menu-background-rgb` sets the surface RGB channels (light: `225, 230, 240`; dark: `26, 31, 34`). `--ios-theme-menu-background-opacity` controls opacity and defaults to `0.96`. Both are public customization variables and can be set on `ion-menu`.
15
15
 
16
+ Button press scaling follows `:active` with a slight overshoot. Set `--ios-theme-button-press-duration` (default: `380ms`) to customize its duration instead of the shared activated-transition duration variables.
17
+
16
18
  ## Liquid Glass mixin
17
19
 
18
20
  Import the SCSS files from the main package to use the liquid glass mixin.
@@ -27,7 +29,7 @@ ion-textarea label.textarea-wrapper {
27
29
 
28
30
  ## Native UI Shell (Experimental)
29
31
 
30
- Capacitor iOS apps can use the optional, experimental [Native UI Shell](native-ui-shell.md) to render supported fixed Ionic controls with UIKit and system Liquid Glass. Content and application logic stay in the WebView, with Ionic owning routing and page transitions. The guide explains the hybrid approach's origins in Basecamp and Capacitor, setup, supported controls and Web fallback behavior.
32
+ Capacitor iOS apps can use the optional, experimental [Native UI Shell](https://docs.rdlabo.dev/projects/ionic-theme-ios27/docs/native-ui-shell) to render supported fixed Ionic controls with UIKit and system Liquid Glass. Content and application logic stay in the WebView, with Ionic owning routing and page transitions. The guide explains the hybrid approach's origins in Basecamp and Capacitor, setup, supported controls and Web fallback behavior.
31
33
 
32
34
  ## Selective component imports
33
35
 
@@ -4,7 +4,7 @@ title: Adaptive iOS themes
4
4
 
5
5
  # Adaptive iOS themes
6
6
 
7
- Use CSS feature queries to choose a theme while keeping Ionic's default iOS styles on older browsers. Safari 27 adds `:heading` support; see the [WebKit announcement](https://webkit.org/blog/17967/news-from-wwdc26-webkit-in-safari-27-beta/).
7
+ Use CSS feature queries to choose a theme while keeping Ionic's default iOS styles on older browsers. The newer theme branch uses `overflow-anchor` support; see the [MDN `overflow-anchor` documentation](https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-anchor).
8
8
 
9
9
  These checks detect browser capabilities, not the operating system version. Other browsers may support the same features. Keep Ionic mode and platform-specific animation configuration consistent with your application.
10
10
 
@@ -15,7 +15,7 @@ In your global Sass stylesheet, use `meta.load-css` inside the feature query. Un
15
15
  ```scss
16
16
  @use 'sass:meta';
17
17
 
18
- @supports selector(:heading) {
18
+ @supports (overflow-anchor: auto) {
19
19
  @include meta.load-css('@rdlabo/ionic-theme-ios27/src/styles/default-variables');
20
20
  @include meta.load-css('@rdlabo/ionic-theme-ios27/src/styles/ionic-theme-ios27');
21
21
  @include meta.load-css('@rdlabo/ionic-theme-ios27/src/styles/ionic-theme-ios27-dark-class');
@@ -23,6 +23,8 @@ In your global Sass stylesheet, use `meta.load-css` inside the feature query. Un
23
23
  }
24
24
  ```
25
25
 
26
+ Configure animations from the same package as in the [README](https://docs.rdlabo.dev/projects/ionic-theme-ios27#configure-animations).
27
+
26
28
  ## Use the iOS 26 and iOS 27 themes together
27
29
 
28
30
  Install both packages:
@@ -36,14 +38,14 @@ Use this global Sass configuration instead of unconditional theme imports:
36
38
  ```scss
37
39
  @use 'sass:meta';
38
40
 
39
- @supports selector(:heading) {
41
+ @supports (overflow-anchor: auto) {
40
42
  @include meta.load-css('@rdlabo/ionic-theme-ios27/src/styles/default-variables');
41
43
  @include meta.load-css('@rdlabo/ionic-theme-ios27/src/styles/ionic-theme-ios27');
42
44
  @include meta.load-css('@rdlabo/ionic-theme-ios27/src/styles/ionic-theme-ios27-dark-class');
43
45
  @include meta.load-css('@rdlabo/ionic-theme-ios27/src/styles/md-remove-ios-class-effect');
44
46
  }
45
47
 
46
- @supports (text-wrap: pretty) and (not selector(:heading)) {
48
+ @supports (text-wrap: pretty) and (not (overflow-anchor: auto)) {
47
49
  @include meta.load-css('@rdlabo/ionic-theme-ios26/src/styles/default-variables');
48
50
  @include meta.load-css('@rdlabo/ionic-theme-ios26/src/styles/ionic-theme-ios26');
49
51
  @include meta.load-css('@rdlabo/ionic-theme-ios26/src/styles/ionic-theme-ios26-dark-class');
@@ -51,40 +53,37 @@ Use this global Sass configuration instead of unconditional theme imports:
51
53
  }
52
54
  ```
53
55
 
54
- The branches are mutually exclusive. Browsers without either feature retain Ionic's default styling. The `text-wrap: pretty` check preserves the earlier theme's capability-based fallback for older Safari versions.
56
+ 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 when `overflow-anchor` is unavailable.
55
57
 
56
58
  Both examples use class-based dark mode. Load Ionic's matching dark palette in your application; for system or always-dark mode, replace the `-dark-class` imports with the matching variant. If you also use `md-ion-list-inset`, load the corresponding package's stylesheet inside each branch.
57
59
 
58
- ## Select matching JavaScript animations
60
+ ### Page transition and overlay animations
59
61
 
60
- Use the same feature checks for dynamic imports before initializing Ionic. Call this loader for the iOS platform; leave other platforms on their existing animation configuration.
62
+ When both packages are installed, keep **page transition** (`navAnimation`) on the iOS 27 animation. Styles still follow the feature queries above; only the transition stays on `@rdlabo/ionic-theme-ios27`. Use the same package for popover enter/leave so overlay motion stays consistent with that transition.
61
63
 
62
64
  ```ts
63
- async function loadIOSAnimations() {
65
+ import { iosTransitionAnimation, popoverEnterAnimation, popoverLeaveAnimation } from '@rdlabo/ionic-theme-ios27';
66
+
67
+ function loadIOSAnimations() {
64
68
  if (typeof CSS === 'undefined') {
65
69
  return {};
66
70
  }
67
71
 
68
- const theme = CSS.supports('selector(:heading)')
69
- ? await import('@rdlabo/ionic-theme-ios27')
70
- : CSS.supports('text-wrap: pretty')
71
- ? await import('@rdlabo/ionic-theme-ios26')
72
- : undefined;
73
-
74
- if (!theme) {
72
+ // Styles are adaptive; apply iOS theme animations when either generation matches.
73
+ if (!CSS.supports('overflow-anchor: auto') && !CSS.supports('text-wrap: pretty')) {
75
74
  return {};
76
75
  }
77
76
 
78
77
  return {
79
- navAnimation: theme.iosTransitionAnimation,
80
- popoverEnter: theme.popoverEnterAnimation,
81
- popoverLeave: theme.popoverLeaveAnimation,
78
+ navAnimation: iosTransitionAnimation,
79
+ popoverEnter: popoverEnterAnimation,
80
+ popoverLeave: popoverLeaveAnimation,
82
81
  };
83
82
  }
84
83
 
85
- // In your browser bootstrap, before initializing Ionic:
86
- const animations = isPlatform('ios') ? await loadIOSAnimations() : {};
84
+ // Resolve synchronously before bootstrapping so Ionic's global config exists before any element upgrades.
85
+ const animations = isPlatform('ios') ? loadIOSAnimations() : {};
87
86
  provideIonicAngular({ ...animations });
88
87
  ```
89
88
 
90
- Import `isPlatform` and `provideIonicAngular` from your Ionic Angular entry point as shown in the README. Pass the same options to `setupIonicReact` or `IonicVue` when using those frameworks. The imports are selected at runtime; the bundler may still emit chunks for both packages. In server-rendered apps, run the selection during browser initialization.
89
+ 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. In server-rendered apps, run the selection during browser initialization.
@@ -24,6 +24,14 @@ import { enableNativeUIShell } from '@rdlabo/ionic-theme-ios27/native';
24
24
  void enableNativeUIShell();
25
25
  ```
26
26
 
27
+ `enableNativeUIShell()` also reads the WebView's effective top-left corner radius and applies it to page transitions. To configure only the transition without enabling native controls, call:
28
+
29
+ ```ts
30
+ import { configureNativeTransition } from '@rdlabo/ionic-theme-ios27/native';
31
+
32
+ await configureNativeTransition();
33
+ ```
34
+
27
35
  Keep the existing `navAnimation: iosTransitionAnimation` setting. No per-page registration, component list, native callback, or Swift view controller is required. Run `npx cap sync ios` after installing or updating the package. The native plugin uses Swift Package Manager (SPM). For an existing CocoaPods app, run `npx cap spm-migration-assistant` and link the generated `CapApp-SPM` package to the app target in Xcode. Build with Xcode 26 or later and Capacitor 8; native glass requires iOS 26 or later. Web, Android, SSR and older iOS keep the Web implementation.
28
36
 
29
37
  This is an opt-in feature. The ordinary package entry point does not import Capacitor, and `@capacitor/core` is an optional peer dependency. Native sources are still detected and built by Capacitor's sync when this package is installed in a Capacitor project, even if the application does not call `enableNativeUIShell()`.
@@ -38,7 +46,7 @@ Native appearance follows the applied class, system or always-dark theme CSS. Sy
38
46
  | `ion-buttons` | Fixed toolbar, two or more direct clear `ion-button` / `ion-menu-button` children sharing the theme glass | One `UIGlassEffect` surface with independent native buttons |
39
47
  | `ion-back-button` | Standard icon and color in a fixed header/footer toolbar | Glass `UIButton`, using the resolved Ionic label/icon |
40
48
  | `ion-menu-button` | Fixed toolbar, inside the theme glass `ion-buttons` | Glass `UIButton`; original Ionic menu toggle |
41
- | `ion-tab-bar` | Fixed tabs, icon-only/label-only items, one icon per item, dot/text badges, selection and disabled state | `UITabBar` and `UITabBarItem` |
49
+ | `ion-tab-bar` | Fixed tabs, icon-only/label-only items, one icon per item, dot/text badges, selection and disabled state | `UITabBar` and `UITabBarItem` |
42
50
  | `ion-segment` | Fixed toolbar, non-scrollable, text **or** one icon per item | `UISegmentedControl` |
43
51
  | `ion-fab` / `ion-fab-button` / `ion-fab-list` | Glass FAB in an `ion-content` fixed slot; one main button and optional directional lists | Persistent glass `UIButton` per button; one FAB synchronization group |
44
52
 
@@ -84,7 +92,9 @@ Existing `attachTabBarSearchable(tabBar, fabButton, footer)` registrations autom
84
92
 
85
93
  Search registration does not bypass placement restrictions. Its searchbar and close button must be in fixed footer toolbars. Its trigger must belong to an `ion-fab[slot="fixed"]` directly inside `ion-content`, or directly on the existing non-scrolling `.ion-page` layout. A wrapper inside scrolling content is not a fixed slot.
86
94
 
87
- Opening search preserves the selected Ionic tab and does not automatically show the keyboard. Tap the search field or call the existing `ion-searchbar.setFocus()`. UIKit owns the search expansion, keyboard placement and return to the ordinary tabs. The trigger's resolved SVG and the search icon are projected from Ionic, including `ion-icon name`. The resting layout is measured against `ion-tab-bar` and both coordinates of the original FAB's center. A layout that cannot preserve those origins uses Web search.
95
+ While a registration is alive, Native UI Shell keeps the searchable controller even when the page is transitioning or temporarily unavailable (`available: false`), instead of demoting back to a separate ordinary control identity. Resting chrome (including Album before search opens) uses the same `UITabBar` + `ShellTabBar.fit` path as ordinary tabs, with the search trigger pinned to the FAB when available; `UISearchTab` is shown only while the search session is active. Flipping availability or entering/leaving search crossfades between those layers. Register before the destination page finishes entering (for example in `ionViewWillEnter`) so the first visit does not paint ordinary tabs and then swap.
96
+
97
+ Opening search preserves the selected Ionic tab and does not automatically show the keyboard (`automaticallyActivatesSearch` stays off). Tap the field or call `ion-searchbar.setFocus()` for the keyboard. While search is active, Native UI Shell freezes Web layout projection and holds Capacitor Keyboard resize at `none`; UIKit owns tab/search chrome (no Ionic `fit` mid-session). Leave re-fits ordinary tabs to `ion-tab-bar` and search to the FAB. Ordinary native tabs keep an optimistic selection until the Web `selected` state catches up. Input events and application `value` updates continue across the bridge until search closes. The trigger's resolved SVG and the search icon are projected from Ionic, including `ion-icon name`.
88
98
 
89
99
  Native edits pass through Ionic's input handlers, preserving `ionInput` debounce, `ionChange`, `ionFocus`, `ionBlur`, and `ionClear`. Programmatic `value` changes do not emit `ionInput`; synchronous application corrections and stale native input are distinguished. Native editing owns marked text and the caret. Returning through the footer's close action retains the value and does not emit `ionCancel` or `ionClear`.
90
100
 
@@ -122,8 +132,113 @@ console.log(shell.getStatus()); // state, projected control count, update count,
122
132
  await shell.destroy(); // restore DOM, remove native controls and release listeners/cache
123
133
  ```
124
134
 
135
+ Projection is globally enabled by default. Limit it to selected Ionic components when an application only wants part of the native shell, or disable it globally while retaining the same configuration path:
136
+
137
+ ```ts
138
+ const shell = await enableNativeUIShell({
139
+ enabled: true,
140
+ controls: {
141
+ tabs: true,
142
+ },
143
+ });
144
+
145
+ // Equivalent to leaving Native UI Shell off; all controls remain on the Web.
146
+ const disabledShell = await enableNativeUIShell({ enabled: false });
147
+ ```
148
+
149
+ Omitting `controls` enables every supported control for backward compatibility. When `controls` is present, only entries set to `true` are native-eligible. Available entries are `tabs`, `toolbar`, `segment`, and `fab`.
150
+
151
+ For a custom modal or overlay that Native UI Shell cannot detect, acquire a suspension before presenting it. The resolved suspension means projected controls have returned to Web rendering. Always release it after dismissal:
152
+
153
+ ```ts
154
+ const suspension = await shell.suspend();
155
+
156
+ try {
157
+ await modal.present();
158
+ await modal.onDidDismiss();
159
+ } finally {
160
+ await suspension.resume();
161
+ }
162
+ ```
163
+
164
+ Suspensions are nestable and `resume()` is idempotent. Native projection resumes only after every active suspension has been released, using the current DOM rather than a stale snapshot.
165
+
125
166
  The native material and control appearance follow the running iOS version; an iOS 26 device does not acquire iOS 27's appearance merely by installing this theme.
126
167
 
168
+ ## Native UI Shell API
169
+
170
+ The generated reference below documents the handle returned by `enableNativeUIShell()`. The underlying Capacitor bridge and its control-snapshot protocol are implementation details.
171
+
172
+ <docgen-index>
173
+
174
+ * [`getStatus()`](#getstatus)
175
+ * [`suspend()`](#suspend)
176
+ * [`destroy()`](#destroy)
177
+ * [Interfaces](#interfaces)
178
+
179
+ </docgen-index>
180
+
181
+ <docgen-api>
182
+ <!--Update the source file JSDoc comments and rerun docgen to update the docs below-->
183
+
184
+ ### getStatus()
185
+
186
+ ```typescript
187
+ getStatus() => NativeUIShellStatus
188
+ ```
189
+
190
+ Returns the current Web/native projection state.
191
+
192
+ **Returns:** <code><a href="#nativeuishellstatus">NativeUIShellStatus</a></code>
193
+
194
+ --------------------
195
+
196
+
197
+ ### suspend()
198
+
199
+ ```typescript
200
+ suspend() => Promise<NativeUIShellSuspension>
201
+ ```
202
+
203
+ Restores projected controls to the Web until the returned lease is resumed.
204
+
205
+ **Returns:** <code>Promise&lt;<a href="#nativeuishellsuspension">NativeUIShellSuspension</a>&gt;</code>
206
+
207
+ --------------------
208
+
209
+
210
+ ### destroy()
211
+
212
+ ```typescript
213
+ destroy() => Promise<void>
214
+ ```
215
+
216
+ Stops synchronization, restores Web controls and releases native resources.
217
+
218
+ --------------------
219
+
220
+
221
+ ### Interfaces
222
+
223
+
224
+ #### NativeUIShellStatus
225
+
226
+ | Prop | Type |
227
+ | --------------- | ------------------------------------------- |
228
+ | **`state`** | <code>'native' \| 'stopped' \| 'web'</code> |
229
+ | **`projected`** | <code>number</code> |
230
+ | **`updates`** | <code>number</code> |
231
+ | **`reason`** | <code>string</code> |
232
+
233
+
234
+ #### NativeUIShellSuspension
235
+
236
+ | Method | Signature | Description |
237
+ | ---------- | ---------------------------- | ---------------------------------------------------------------------------------------------- |
238
+ | **resume** | () =&gt; Promise&lt;void&gt; | Releases this suspension. Native projection resumes after all active suspensions are released. |
239
+
240
+ </docgen-api>
241
+
127
242
  ## Source layout
128
243
 
129
244
  Each TypeScript module in [`src/native/components`](../src/native/components) declares its Ionic tag and DOM reader. `components/index.ts` combines those exports into discovery selectors and the component type. Shared DOM measurements, item data and SVG rendering live in `src/native/shared`; `runtime.ts` owns synchronization, visibility handoffs and lifecycle events.
@@ -134,5 +249,4 @@ On iOS, [`Components`](../ios/Sources/IonicNativeUIShellPlugin/Components) owns
134
249
 
135
250
  See the [demo and verification guide](../demo/native-ui-shell.md) for browser tests, Simulator tests and an independent SPM consumer built from the npm package.
136
251
 
137
-
138
252
  Search controllers retain their UIKit-managed transition and are excluded from the ordinary control acquisition crossfade.