diff --git a/README.md b/README.md index af55dc70..add2e728 100644 --- a/README.md +++ b/README.md @@ -31,14 +31,14 @@ The same Library screen with Native UI off and on, captured while dragging the s ## Choose what your app needs -| Your goal | Start here | -| ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | -| Style your Ionic Web UI | [Set up the theme](packages/theme-ios27/docs/setup.md) | -| Render Ionic controls natively with your existing Web appearance | [Set up Native UI](packages/capacitor-native-ui/docs/setup.md) | -| Use native buttons without Ionic | [HTML setup](packages/capacitor-native-ui/docs/framework-independent.md) | -| Add a vertical rail to Ionic while keeping your theme | [Ionic rail setup](packages/theme-ios27/docs/iphone-duo-with-original-theme.md#try-it-in-your-existing-ionic-app) | -| Add a vertical rail without Ionic | [HTML rail setup](packages/capacitor-native-ui/docs/framework-independent.md) | -| Add custom SwiftUI or Compose content | [App-local extensions (experimental)](packages/capacitor-native-ui/docs/native-ui-extensions.md) | +| Your goal | Start here | +| ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| Style your Ionic Web UI | [Set up the theme](packages/theme-ios27/docs/setup.md) | +| Render Ionic controls natively with your existing Web appearance | [Set up Native UI](packages/capacitor-native-ui/docs/setup.md) | +| Use native buttons without Ionic | [HTML setup](packages/capacitor-native-ui/docs/framework-independent.md) | +| Add a vertical rail to Ionic while keeping your theme | [Ionic rail setup](packages/capacitor-native-ui/docs/ionic-vertical-bars.md#try-it-in-your-existing-ionic-app) | +| Add a vertical rail without Ionic | [HTML rail setup](packages/capacitor-native-ui/docs/framework-independent.md) | +| Add custom SwiftUI or Compose content | [App-local extensions (experimental)](packages/capacitor-native-ui/docs/native-ui-extensions.md) | Already using the iOS theme? [Add Native UI to your theme](packages/theme-ios27/docs/add-native-ui.md) as your next step. @@ -46,7 +46,7 @@ Both packages support Ionic 8.8.1 or later within Ionic 8 or 9. Native UI requir ## Explore further -- [iPhone Duo layout](packages/theme-ios27/docs/iphone-duo.md) · [Vertical Bars with an existing theme](packages/theme-ios27/docs/iphone-duo-with-original-theme.md). +- [iPhone Duo layout](packages/theme-ios27/docs/iphone-duo.md) · [Vertical Bars with an existing theme](packages/capacitor-native-ui/docs/ionic-vertical-bars.md). - [Theme migration](packages/theme-ios27/docs/migration.md) · [Native UI migration](packages/capacitor-native-ui/docs/migration.md). Use standard native buttons without Ionic through the [plain HTML adapter](packages/capacitor-native-ui/docs/framework-independent.md), or keep Ionic markup and enable only [Vertical Bars](packages/capacitor-native-ui/docs/configuration.md). Both use the same native renderers. diff --git a/demo/src/app/docs/docs-content.generated.ts b/demo/src/app/docs/docs-content.generated.ts index 40800cd1..f5c0b98d 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 packages/theme-ios27/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 submit buttons use the Ionic color's contrast value for their foreground. Their directional edge treatment follows the iOS 27 prominent-button appearance and does not require an additional brightness color.

\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

Preferred overlay actions

\n

For iOS alerts and action sheets, set role: 'preferred' on a button to give it a filled --ion-color-primary background and --ion-color-primary-contrast text and icons. While pressed, the background uses --ion-color-primary-shade. This is a theme convention using Ionic's custom button roles; it does not automatically select or invoke the action. Dismissal reports the role as preferred.

\n
buttons: [\n  { text: \'Cancel\', role: \'cancel\' },\n  { text: \'Continue\', role: \'preferred\' },\n];
\n\n

Choose the role by action semantics:

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
RoleAppearance and behavior
preferredFilled primary action; dismissal reports preferred. Does not automatically select or invoke it.
No role or defaultNormal text color.
cancelRetains Ionic's cancellation behavior.
selectedRetains selection state.
destructiveUses --ios-theme-destructive-color.
confirmNot treated as preferred. Use preferred for a recommended action, not simply any confirmation.
\n

Floating iPad sheets

\n

Choose the sheet's scrolling behavior with expandToScroll:

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
SettingiPad appearanceScrolling and resizing
falseFloating lower corners and a 20px bottom gap.Content scrolls within the current breakpoint; dragging the handle resizes the sheet. Ionic sizes the visible page at each breakpoint so CSS can style it.
true (default)Ionic's bottom-attached layout.Scroll-to-expand behavior.
\n

Tab bar position

\n

Choose a class on an iOS ion-tab-bar to position the whole bar within its safe area:

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
ClassPlacement
tab-bar-position-startStart of the reading direction.
tab-bar-position-centerCenter.
tab-bar-position-endEnd of the reading direction.
NoneExisting placement unchanged.
\n

All three classes support slot="top" and slot="bottom", preserve width and press animation, and resolve start/end in RTL.

\n
<ion-tab-bar slot="bottom" class="tab-bar-position-center">\n  <ion-tab-button tab="home">Home</ion-tab-button>\n  <ion-tab-button tab="settings">Settings</ion-tab-button>\n</ion-tab-bar>
\n\n

These classes do not reposition a separate ion-fab; leave room for it when choosing the bar's position.

\n

Support iPhone Duo

\n

iPhone Duo support, including standalone Vertical Bars, is stable starting with version 2.0.0.

\n

iPhone Duo support — the vertical system rail, hinge posture, and the posture-driven split-pane layout — is independent of the iOS 27 theme and the full Native UI. See iPhone Duo support for the complete setup, including device-layout monitoring without a projection runtime.

\n

See Vertical Bars for rail layout classes, control eligibility and native button appearance.

\n

For standalone setup while keeping your existing theme, see iPhone Duo with your existing theme.

\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-style-glass to give a segment the same glass surface and selected indicator treatment as the tab bar. The class preserves the segment's existing dimensions and text colors, supports scrollable segments, and respects Ionic's public --background property.

\n
<ion-segment class="segment-style-glass" value="available">\n  <ion-segment-button value="available">Available</ion-segment-button>\n  <ion-segment-button value="away">Away</ion-segment-button>\n</ion-segment>
\n\n

For colored segments, use Ionic's color property (for example, color="primary" or color="secondary"). Ionic uses the palette's base color for the softly tinted track while keeping the selected surface and labels neutral. A surrounding colored toolbar only supplies colors when the segment has no color of its own. The optional moving glass inherits the selected surface color, and custom Ionic palettes work without additional registration.

\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

Segment size and press behavior

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
PlacementMinimum heightContainer behavior
In content32pxCompact, flat background and indicator; outer bounds stay fixed.
Inside ion-toolbar48pxGlass container; outer container scales while pressed.
.segment-expand, including in a toolbar32pxCompact, flat background and indicator; outer bounds stay fixed.
\n

The optional moving glass lens is independent of the container's background.

\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

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'; + '

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 submit buttons use the Ionic color's contrast value for their foreground. Their directional edge treatment follows the iOS 27 prominent-button appearance and does not require an additional brightness color.

\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

Preferred overlay actions

\n

For iOS alerts and action sheets, set role: 'preferred' on a button to give it a filled --ion-color-primary background and --ion-color-primary-contrast text and icons. While pressed, the background uses --ion-color-primary-shade. This is a theme convention using Ionic's custom button roles; it does not automatically select or invoke the action. Dismissal reports the role as preferred.

\n
buttons: [\n  { text: \'Cancel\', role: \'cancel\' },\n  { text: \'Continue\', role: \'preferred\' },\n];
\n\n

Choose the role by action semantics:

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
RoleAppearance and behavior
preferredFilled primary action; dismissal reports preferred. Does not automatically select or invoke it.
No role or defaultNormal text color.
cancelRetains Ionic's cancellation behavior.
selectedRetains selection state.
destructiveUses --ios-theme-destructive-color.
confirmNot treated as preferred. Use preferred for a recommended action, not simply any confirmation.
\n

Floating iPad sheets

\n

Choose the sheet's scrolling behavior with expandToScroll:

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
SettingiPad appearanceScrolling and resizing
falseFloating lower corners and a 20px bottom gap.Content scrolls within the current breakpoint; dragging the handle resizes the sheet. Ionic sizes the visible page at each breakpoint so CSS can style it.
true (default)Ionic's bottom-attached layout.Scroll-to-expand behavior.
\n

Tab bar position

\n

Choose a class on an iOS ion-tab-bar to position the whole bar within its safe area:

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
ClassPlacement
tab-bar-position-startStart of the reading direction.
tab-bar-position-centerCenter.
tab-bar-position-endEnd of the reading direction.
NoneExisting placement unchanged.
\n

All three classes support slot="top" and slot="bottom", preserve width and press animation, and resolve start/end in RTL.

\n
<ion-tab-bar slot="bottom" class="tab-bar-position-center">\n  <ion-tab-button tab="home">Home</ion-tab-button>\n  <ion-tab-button tab="settings">Settings</ion-tab-button>\n</ion-tab-bar>
\n\n

These classes do not reposition a separate ion-fab; leave room for it when choosing the bar's position.

\n

Support iPhone Duo

\n

iPhone Duo support, including standalone Vertical Bars, is stable starting with version 2.0.0.

\n

iPhone Duo support — the vertical system rail, hinge posture, and the posture-driven split-pane layout — is independent of the iOS 27 theme and the full Native UI. See iPhone Duo support for the complete setup, including device-layout monitoring without a projection runtime.

\n

See Vertical Bars for rail layout classes, control eligibility and native button appearance.

\n

For standalone setup while keeping your existing theme, see iPhone Duo with your existing theme.

\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-style-glass to give a segment the same glass surface and selected indicator treatment as the tab bar. The class preserves the segment's existing dimensions and text colors, supports scrollable segments, and respects Ionic's public --background property.

\n
<ion-segment class="segment-style-glass" value="available">\n  <ion-segment-button value="available">Available</ion-segment-button>\n  <ion-segment-button value="away">Away</ion-segment-button>\n</ion-segment>
\n\n

For colored segments, use Ionic's color property (for example, color="primary" or color="secondary"). Ionic uses the palette's base color for the softly tinted track while keeping the selected surface and labels neutral. A surrounding colored toolbar only supplies colors when the segment has no color of its own. The optional moving glass inherits the selected surface color, and custom Ionic palettes work without additional registration.

\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

Segment size and press behavior

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
PlacementMinimum heightContainer behavior
In content32pxCompact, flat background and indicator; outer bounds stay fixed.
Inside ion-toolbar48pxGlass container; outer container scales while pressed.
.segment-expand, including in a toolbar32pxCompact, flat background and indicator; outer bounds stay fixed.
\n

The optional moving glass lens is independent of the container's background.

\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

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 c5fe6a41..6d4c544c 100644 --- a/demo/src/app/docs/docs-page.component.html +++ b/demo/src/app/docs/docs-page.component.html @@ -49,7 +49,7 @@

What would you like to build?

>
  • - Add a side rail to Ionic with your existing theme
  • diff --git a/demo/src/native-ui-html.spec.ts b/demo/src/native-ui-html.spec.ts index 57544766..d1f34d95 100644 --- a/demo/src/native-ui-html.spec.ts +++ b/demo/src/native-ui-html.spec.ts @@ -94,6 +94,40 @@ test('Web rail forwards source form behavior, updates state, rejects stale click expect(options.rail!.host.children.length).toBe(0); }); +test('Web rail follows source order without replacing proxies or application-owned host content', async () => { + const { root, options } = mount( + '' + + '', + ); + const host = options.rail!.host; + const heading = document.createElement('h2'); + heading.textContent = 'Actions'; + host.append(heading); + const handle = createHTMLWebProjection(document, options); + handles.push(handle); + const proxies = () => Array.from(host.querySelectorAll('button')); + const [a, b] = proxies(); + const sourceB = root.lastElementChild as HTMLButtonElement; + const click = vi.fn(); + sourceB.addEventListener('click', click); + b.focus(); + root.prepend(sourceB); + await vi.waitFor(() => expect(proxies()).toEqual([b, a])); + expect(document.activeElement).toBe(b); + b.click(); + expect(click).toHaveBeenCalledTimes(1); + + const sourceC = sourceB.cloneNode(true) as HTMLButtonElement; + sourceC.removeAttribute('data-native-ui-web-source'); + sourceC.textContent = 'C'; + sourceC.getBoundingClientRect = sourceB.getBoundingClientRect; + root.prepend(sourceC); + await vi.waitFor(() => expect(proxies().map((proxy) => proxy.textContent)).toEqual(['C', 'B', 'A'])); + expect(proxies().slice(1)).toEqual([b, a]); + expect(host.firstElementChild).toBe(heading); + expect(document.activeElement).toBe(b); +}); + test('routerless back delegates to the source and suspension returns ownership to the Web', async () => { const { source, options } = mount(''); const back = vi.fn(); diff --git a/packages/capacitor-native-ui/README.md b/packages/capacitor-native-ui/README.md index 8888a6f4..23938b53 100644 --- a/packages/capacitor-native-ui/README.md +++ b/packages/capacitor-native-ui/README.md @@ -23,14 +23,14 @@ Projection changes presentation; it does not translate arbitrary HTML or CSS int ## Choose what to build -| Goal | Start here | -| --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | -| Render existing Ionic controls natively | [Ionic setup](docs/setup.md) | -| Use native buttons without Ionic | [HTML setup](docs/framework-independent.md) | -| Add a side rail to Ionic while keeping your theme | [Vertical Bars step by step](https://github.com/rdlabo-dev/ionic-theme-ios27/blob/main/packages/theme-ios27/docs/iphone-duo-with-original-theme.md) | -| Add a side rail without Ionic | [HTML rail setup](docs/framework-independent.md) | -| Add native controls to an existing iOS 27 theme app | [Add Native UI to your theme](https://github.com/rdlabo-dev/ionic-theme-ios27/blob/main/packages/theme-ios27/docs/add-native-ui.md) | -| Render custom SwiftUI or Android Compose content | [App-local extensions (experimental)](docs/native-ui-extensions.md) | +| Goal | Start here | +| --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | +| Render existing Ionic controls natively | [Ionic setup](docs/setup.md) | +| Use native buttons without Ionic | [HTML setup](docs/framework-independent.md) | +| Add a side rail to Ionic while keeping your theme | [Vertical Bars step by step](docs/ionic-vertical-bars.md) | +| Add a side rail without Ionic | [HTML rail setup](docs/framework-independent.md) | +| Add native controls to an existing iOS 27 theme app | [Add Native UI to your theme](https://github.com/rdlabo-dev/ionic-theme-ios27/blob/main/packages/theme-ios27/docs/add-native-ui.md) | +| Render custom SwiftUI or Android Compose content | [App-local extensions (experimental)](docs/native-ui-extensions.md) | ## Install from npm diff --git a/packages/capacitor-native-ui/docs/framework-independent.md b/packages/capacitor-native-ui/docs/framework-independent.md index 393ae8f9..92421c8f 100644 --- a/packages/capacitor-native-ui/docs/framework-independent.md +++ b/packages/capacitor-native-ui/docs/framework-independent.md @@ -8,7 +8,7 @@ Start with [how projection works](../README.md#how-projection-works): the HTML e **Want only Vertical Bars?** Follow steps 1–2 to install and mark your HTML buttons, then use the [Web rail configuration](#optional-web-rail) in place of step 3. Remove explicit markers from buttons you want to leave on the Web. Style the rail host in your application and build with step 4 for iOS. You do not need Ionic, `@rdlabo/ionic-theme-ios27`, or `vertical-bars.css`. -For Vertical Bars in an app using `ion-tabs`, `ion-toolbar` or other Ionic components with a custom theme, use the [Ionic Vertical Bars setup](../../theme-ios27/docs/iphone-duo-with-original-theme.md#try-it-in-your-existing-ionic-app) instead. The framework (Angular, React or Vue) alone does not determine which adapter to use. +For Vertical Bars in an app using `ion-tabs`, `ion-toolbar` or other Ionic components with a custom theme, use the [Ionic Vertical Bars setup](ionic-vertical-bars.md#try-it-in-your-existing-ionic-app) instead. The framework (Angular, React or Vue) alone does not determine which adapter to use. ## 1. Install the plugin diff --git a/packages/theme-ios27/docs/iphone-duo-with-original-theme.md b/packages/capacitor-native-ui/docs/ionic-vertical-bars.md similarity index 74% rename from packages/theme-ios27/docs/iphone-duo-with-original-theme.md rename to packages/capacitor-native-ui/docs/ionic-vertical-bars.md index 77d8b3c7..3975d7ae 100644 --- a/packages/theme-ios27/docs/iphone-duo-with-original-theme.md +++ b/packages/capacitor-native-ui/docs/ionic-vertical-bars.md @@ -6,17 +6,17 @@ title: Add Vertical Bars while keeping your theme Keep your app's existing appearance and add Vertical Bars. Choose the setup for your app's components, whether you use Angular, React, Vue or another framework. -Read [how projection works](../../capacitor-native-ui/README.md#how-projection-works) for the relationship between source DOM, projected controls and actions. Then choose your setup below. +Read [how projection works](../README.md#how-projection-works) for the relationship between source DOM, projected controls and actions. Then choose your setup below. ## Choose your setup -| Your app | Start here | What you add | -| ------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | -| Ionic components with your own theme, Ionic's default styles, or another theme | [Ionic setup below](#try-it-in-your-existing-ionic-app) | Standalone rail CSS and the Ionic Native UI adapter. Keep your existing theme. | -| No Ionic components (plain HTML, React, Vue, Angular, etc.) | [HTML setup](../../capacitor-native-ui/docs/framework-independent.md), then its [Web rail configuration](../../capacitor-native-ui/docs/framework-independent.md#optional-web-rail) | Only `@rdlabo/capacitor-native-ui`, using its `/core` entry. Your app supplies the rail layout. | -| Already using the iOS 27 theme and Native UI | [Connect device placement](iphone-duo.md#project-controls-into-the-rail) | Reuse your existing runtime. Do not start another one. | +| Your app | Start here | What you add | +| ------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | +| Ionic components with your own theme, Ionic's default styles, or another theme | [Ionic setup below](#try-it-in-your-existing-ionic-app) | Standalone rail CSS and the Ionic Native UI adapter. Keep your existing theme. | +| No Ionic components (plain HTML, React, Vue, Angular, etc.) | [HTML setup](framework-independent.md), then its [Web rail configuration](framework-independent.md#optional-web-rail) | Only `@rdlabo/capacitor-native-ui`, using its `/core` entry. Your app supplies the rail layout. | +| Already using the iOS 27 theme and Native UI | [Connect device placement](../../theme-ios27/docs/iphone-duo.md#project-controls-into-the-rail) | Reuse your existing runtime. Do not start another one. | -**Without Ionic, follow the HTML guide instead of the steps below.** You do not need this theme package, `vertical-bars.css`, `ion-app`, or Ionic navigation animations. Explicitly marked HTML buttons and back buttons are supported; arbitrary HTML tabs are not automatically converted. A browser preview requires an application-styled `webHost`; native rail presentation also requires supported iOS hardware and software. +**Without Ionic, follow the HTML guide instead of the steps below.** You do not need `@rdlabo/ionic-theme-ios27`, `vertical-bars.css`, `ion-app`, or Ionic navigation animations. Explicitly marked HTML buttons and back buttons are supported; arbitrary HTML tabs are not automatically converted. A browser preview requires an application-styled `webHost`; native rail presentation also requires supported iOS hardware and software. The rest of this page is for **Ionic apps**. Both Ionic `ios` and `md` modes are supported. Tabs and supported toolbar actions move to the side; content and horizontal controls keep their existing appearance. @@ -74,7 +74,7 @@ const ionicConfig = { Merge this option into your existing Ionic configuration before initialization: pass it to Angular's `provideIonicAngular()`, React's `setupIonicReact()`, or Vue's `IonicVue` plugin options. Keep your existing theme stylesheet imports. No iOS 27 theme stylesheet is required. -#### Use this package's iOS animation +#### Use the theme's iOS animation If you already use the iOS 27 transition, keep this configuration. It includes the native adapter and excludes the horizontal back-button effect in vertical layouts; no additional wrapper is needed. Importing this JavaScript entry point does not load the theme stylesheets. @@ -120,11 +120,11 @@ const rail = await enableNativeUI({ controls: 'verticalOnly' }); Use your existing Ionic click handlers, routing, and form associations. All button fills (`default`, `clear`, `solid`, and `outline`) use the same `icon-only` rule, including submit buttons. Actions without that slot remain horizontal. Add `.ios-theme-horizontal-only` to an `ion-buttons` group or individual `ion-button` to keep an action in the horizontal toolbar. -When the application owner is disposed, call `await rail.destroy()` to restore the original controls and release the runtime. If you already use `enableNativeUI()`, keep that runtime and follow the [shared placement guide](iphone-duo.md#project-controls-into-the-rail). +When the application owner is disposed, call `await rail.destroy()` to restore the original controls and release the runtime. If you already use `enableNativeUI()`, keep that runtime and follow the [shared placement guide](../../theme-ios27/docs/iphone-duo.md#project-controls-into-the-rail). ### Optional: choose native button appearance -See [Choose button appearance](../../capacitor-native-ui/docs/vertical-bars.md#choose-button-appearance) for appearance settings and local overrides. +See [Choose button appearance](vertical-bars.md#choose-button-appearance) for appearance settings and local overrides. The default is `system`: SwiftUI styles vertical buttons and tints their icons. If your existing theme should supply their fill and colors, replace the startup call in step 4 with: @@ -135,15 +135,15 @@ const rail = await enableNativeUI({ }); ``` -The `solid` default suits ordinary Ionic buttons. Buttons inside `ion-buttons` still default to clear; set `fill="solid"` explicitly to project their background. For one-off exceptions, use the [local projection settings](../../capacitor-native-ui/docs/vertical-bars.md#override-individual-buttons-or-groups). These settings affect native vertical buttons only; Web clones keep their existing appearance. +The `solid` default suits ordinary Ionic buttons. Buttons inside `ion-buttons` still default to clear; set `fill="solid"` explicitly to project their background. For one-off exceptions, use the [local projection settings](vertical-bars.md#override-individual-buttons-or-groups). These settings affect native vertical buttons only; Web clones keep their existing appearance. ### If the preview does not appear -| What you see | What to check | -| ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| No space at the side | Load `vertical-bars.css` and put the class on `ion-app`. | -| Space appears, but controls stay horizontal | Start `enableNativeUI({ controls: 'verticalOnly' })` after mounting the app root. Use existing tabs or `slot="icon-only"` actions in a fixed header/footer toolbar. | -| One action stays horizontal | Check for `slot="icon-only"` on the icon and a fixed toolbar outside scrolling content. Explicitly excluded controls and controls in centered modals stay horizontal; `fill` and `type="submit"` do not prevent movement. See [control requirements](../../capacitor-native-ui/docs/vertical-bars.md#toolbar-actions). | +| What you see | What to check | +| ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| No space at the side | Load `vertical-bars.css` and put the class on `ion-app`. | +| Space appears, but controls stay horizontal | Start `enableNativeUI({ controls: 'verticalOnly' })` after mounting the app root. Use existing tabs or `slot="icon-only"` actions in a fixed header/footer toolbar. | +| One action stays horizontal | Check for `slot="icon-only"` on the icon and a fixed toolbar outside scrolling content. Explicitly excluded controls and controls in centered modals stay horizontal; `fill` and `type="submit"` do not prevent movement. See [control requirements](vertical-bars.md#toolbar-actions). | ## Connect an iPhone Duo @@ -154,9 +154,9 @@ npm install @erkamyaman/capacitor-foldable npx cap sync ios ``` -Use Capacitor 8.5 or later and build with Xcode 27.1 or newer for actual rail placement and hinge posture on iOS 27.1. Keep your existing SPM or CocoaPods integration; see [Native UI setup](../../capacitor-native-ui/docs/setup.md#4-build-sync-and-run). Keep this package's `vertical-bars.css`; the device plugin's `ionic-tabs.css` is not needed with our rail projection. +Use Capacitor 8.5 or later and build with Xcode 27.1 or newer for actual rail placement and hinge posture on iOS 27.1. Keep your existing SPM or CocoaPods integration; see [Native UI setup](setup.md#4-build-sync-and-run). Keep the theme's `vertical-bars.css`; the device plugin's `ionic-tabs.css` is not needed with our rail projection. -Replace the browser-only startup with the [device placement setup](iphone-duo.md#project-controls-into-the-rail). That setup sends initial values and `barPlacementChange` events to `setVerticalBarsLayout({ edge: verticalBarEdge, inset })`, with subscriptions and cleanup kept in your application. +Replace the browser-only startup with the [device placement setup](../../theme-ios27/docs/iphone-duo.md#project-controls-into-the-rail). That setup sends initial values and `barPlacementChange` events to `setVerticalBarsLayout({ edge: verticalBarEdge, inset })`, with subscriptions and cleanup kept in your application. Pass Foldable's reported logical edge and measured inset to `setVerticalBarsLayout`. The helper resolves RTL. A null edge restores the ordinary Web layout; Native UI detects native rail availability independently. @@ -166,8 +166,8 @@ On supported iOS, controls in the rail use native SwiftUI rendering; your custom If your existing theme needs only a posture-driven split pane or a layout switch, do not start a projection runtime or add `.ios-theme-vertical-bars`. Pass `Foldable.getFoldState()` results and `foldStateChange` events to `applyFoldStateClasses(root, fold)`, removing the listener when finished. There is no separate start/stop monitoring call. -See [Read the device layout](iphone-duo.md#read-the-device-layout) for the subscription example, null values, and monitoring lifetime. See [Adapt the split pane](iphone-duo.md#adapt-the-split-pane) for the opt-in width rules and half-open state. +See [Read the device layout](../../theme-ios27/docs/iphone-duo.md#read-the-device-layout) for the subscription example, null values, and monitoring lifetime. See [Adapt the split pane](../../theme-ios27/docs/iphone-duo.md#adapt-the-split-pane) for the opt-in width rules and half-open state. ## Shared layout rules and API -Safe-area handling, overlays, RTL, control eligibility, Web simulation, and the handle API are documented in [Vertical Bars](../../capacitor-native-ui/docs/vertical-bars.md). Read that reference when you need to customize this setup; it is not an additional installation step. +Safe-area handling, overlays, RTL, control eligibility, Web simulation, and the handle API are documented in [Vertical Bars](vertical-bars.md). Read that reference when you need to customize this setup; it is not an additional installation step. diff --git a/packages/capacitor-native-ui/docs/migration.md b/packages/capacitor-native-ui/docs/migration.md index 1a2a7e51..19f9397b 100644 --- a/packages/capacitor-native-ui/docs/migration.md +++ b/packages/capacitor-native-ui/docs/migration.md @@ -105,8 +105,8 @@ The Ionic transition adapter now belongs to `@rdlabo/ionic-theme-ios27`. Replace Use `withNativeUITransition()` to keep your existing Ionic navigation animation while coordinating Native UI controls. -- If you already import `iosTransitionAnimation` from `@rdlabo/ionic-theme-ios27`, keep the [package animation setup](https://github.com/rdlabo-dev/ionic-theme-ios27/blob/main/packages/theme-ios27/docs/iphone-duo-with-original-theme.md#use-this-packages-ios-animation); no configuration change is needed. It now uses the shared adapter internally; do not add another wrapper. -- If you use Ionic's default animation without a `navAnimation` option, follow [Keep Ionic's default animation](https://github.com/rdlabo-dev/ionic-theme-ios27/blob/main/packages/theme-ios27/docs/iphone-duo-with-original-theme.md#keep-ionics-default-animation). The example selects Ionic's standard iOS or MD builder from the transition mode. +- If you already import `iosTransitionAnimation` from `@rdlabo/ionic-theme-ios27`, keep the [package animation setup](ionic-vertical-bars.md#use-the-themes-ios-animation); no configuration change is needed. It now uses the shared adapter internally; do not add another wrapper. +- If you use Ionic's default animation without a `navAnimation` option, follow [Keep Ionic's default animation](ionic-vertical-bars.md#keep-ionics-default-animation). The example selects Ionic's standard iOS or MD builder from the transition mode. - If you use a custom navigation animation with Native UI or the standalone Vertical Control Area, wrap your existing builder when configuring Ionic: ```diff @@ -122,7 +122,7 @@ Merge this option into your existing Ionic configuration before initialization. Use the adapter only for navigation; leave modal and popover animations unchanged. Your builder must create a fresh `Animation` for each navigation because Ionic destroys it afterward. Keep lifecycle events for control registration and transitions without animation. If the custom builder animates a horizontal back button separately, exclude that effect while `.ios-theme-vertical-bars` is active. -See [Connect your navigation animation](https://github.com/rdlabo-dev/ionic-theme-ios27/blob/main/packages/theme-ios27/docs/iphone-duo-with-original-theme.md#3-connect-your-navigation-animation) for the setup and supported scope. +See [Connect your navigation animation](ionic-vertical-bars.md#3-connect-your-navigation-animation) for the setup and supported scope. ## Startup options diff --git a/packages/capacitor-native-ui/docs/native-ui.md b/packages/capacitor-native-ui/docs/native-ui.md index e9b0c73e..9d1bc642 100644 --- a/packages/capacitor-native-ui/docs/native-ui.md +++ b/packages/capacitor-native-ui/docs/native-ui.md @@ -301,7 +301,7 @@ Custom host animation or transition declarations on the FAB, list or button keep ## Transitions and recovery -The built-in `iosTransitionAnimation` waits for native retirement before starting the Web animation. Interactive progress and completion/cancellation are queued while that retirement is in progress. Stationary shared tabs are retained. First render and transitions without an animation builder are covered by the startup runtime and Ionic lifecycle events. Default Ionic and custom navigation builders can use the same integration through `withNativeUITransition()`, exported by `@rdlabo/ionic-theme-ios27`. See [Connect your navigation animation](https://github.com/rdlabo-dev/ionic-theme-ios27/blob/main/packages/theme-ios27/docs/iphone-duo-with-original-theme.md#3-connect-your-navigation-animation) for the setup. +The built-in `iosTransitionAnimation` waits for native retirement before starting the Web animation. Interactive progress and completion/cancellation are queued while that retirement is in progress. Stationary shared tabs are retained. First render and transitions without an animation builder are covered by the startup runtime and Ionic lifecycle events. Default Ionic and custom navigation builders can use the same integration through `withNativeUITransition()`, exported by `@rdlabo/ionic-theme-ios27`. See [Connect your navigation animation](ionic-vertical-bars.md#3-connect-your-navigation-animation) for the setup. Ordinary native/Web handoffs share a 180ms opacity crossfade, including Vertical Bars in both directions. Outgoing visuals cannot receive input or accessibility focus. Reduce Motion and modal presentation use immediate handoffs; search controllers keep their UIKit-managed transition. @@ -413,7 +413,7 @@ In Vertical Bars, automatic selection of fixed-toolbar `ion-button` actions requ **Native button appearance:** Native vertical buttons default to `buttonProjection: 'system'`. Use `source` to project Ionic fill and colors, with `data-projection="source|system"` or the equivalent `ios-theme-projection-source` / `ios-theme-projection-system` classes for local exceptions. These settings affect native vertical `ion-button` and `ion-menu-button` actions only. Horizontal controls and Web clones keep their existing behavior. See [Choose button appearance](vertical-bars.md#choose-button-appearance) for priority and fill rules. -For standalone setup while keeping your existing theme, see [iPhone Duo with your existing theme](https://github.com/rdlabo-dev/ionic-theme-ios27/blob/main/packages/theme-ios27/docs/iphone-duo-with-original-theme.md). +For standalone setup while keeping your existing theme, see [iPhone Duo with your existing theme](ionic-vertical-bars.md). ## Native UI API diff --git a/packages/capacitor-native-ui/docs/setup.md b/packages/capacitor-native-ui/docs/setup.md index d7afc70d..f544eefc 100644 --- a/packages/capacitor-native-ui/docs/setup.md +++ b/packages/capacitor-native-ui/docs/setup.md @@ -178,7 +178,7 @@ Run the iOS app from Xcode on an iOS 26+ device or simulator. Supported fixed co ## Next steps -- [Vertical Bars only](https://github.com/rdlabo-dev/ionic-theme-ios27/blob/main/packages/theme-ios27/docs/iphone-duo-with-original-theme.md): a step-by-step rail setup with your existing theme. +- [Vertical Bars only](ionic-vertical-bars.md): a step-by-step rail setup with your existing theme. - [Configuration](configuration.md): choose controls and change rail settings at runtime. - [Custom SwiftUI or Compose content](native-ui-extensions.md): create an app-local extension. - [Migration](migration.md): update older package names, imports, and native projects. diff --git a/packages/capacitor-native-ui/docs/vertical-bars.md b/packages/capacitor-native-ui/docs/vertical-bars.md index 2de27f2f..8045da2d 100644 --- a/packages/capacitor-native-ui/docs/vertical-bars.md +++ b/packages/capacitor-native-ui/docs/vertical-bars.md @@ -8,7 +8,7 @@ Vertical Bars moves eligible Ionic controls or explicitly marked HTML buttons in Choose your setup before using this reference: -- **Ionic with a custom or default theme:** [Ionic rail setup](../../theme-ios27/docs/iphone-duo-with-original-theme.md#try-it-in-your-existing-ionic-app). +- **Ionic with a custom or default theme:** [Ionic rail setup](ionic-vertical-bars.md#try-it-in-your-existing-ionic-app). - **No Ionic:** [HTML setup](framework-independent.md) and its [Web rail configuration](framework-independent.md#optional-web-rail). Use `/core` and your own layout; the Ionic setup below does not apply. - **Existing iOS 27 theme and Native UI:** [Connect device placement](../../theme-ios27/docs/iphone-duo.md#project-controls-into-the-rail) using your existing runtime. @@ -38,7 +38,7 @@ const rail = await enableNativeUI({ controls: 'verticalOnly' }); Configure your Web rail layout and [apply device placement](#use-with-capacitor-foldable) for an iPhone Duo. The application owns placement and cleanup: call `await rail.destroy()` when its owner is disposed. If you already use `enableNativeUI()`, keep that runtime; it includes Vertical Bars. Do not start both. -The `/vertical-bars` entry point requires `@capacitor/core`, including in browser builds. Using only the theme’s rail CSS needs no native runtime. For native setup and navigation transitions, follow the [existing-theme guide](https://github.com/rdlabo-dev/ionic-theme-ios27/blob/main/packages/theme-ios27/docs/iphone-duo-with-original-theme.md). +The `/vertical-bars` entry point requires `@capacitor/core`, including in browser builds. Using only the theme’s rail CSS needs no native runtime. For native setup and navigation transitions, follow the [existing-theme guide](ionic-vertical-bars.md). On supported Capacitor iOS, eligible controls use native SwiftUI rendering. Web, Android and unavailable native projection use Web clones. A browser preview can verify layout and actions, but cannot compare native button appearance. diff --git a/packages/capacitor-native-ui/src/native/html.ts b/packages/capacitor-native-ui/src/native/html.ts index f353b1b1..615c0542 100644 --- a/packages/capacitor-native-ui/src/native/html.ts +++ b/packages/capacitor-native-ui/src/native/html.ts @@ -147,6 +147,17 @@ export const createHTMLWebProjection = (doc: Document, options: NativeUIRuntimeO } } for (const source of owned.keys()) if (!wanted.has(source)) restore(source); + const clones = Array.from(wanted, (source) => owned.get(source)!.clone); + const order = Array.from(host.children).filter((child): child is HTMLButtonElement => clones.includes(child as HTMLButtonElement)); + const focused = clones.find((clone) => clone === doc.activeElement); + clones.forEach((clone, index) => { + if (order[index] === clone) return; + host.insertBefore(clone, order[index]); + order.splice(order.indexOf(clone), 1); + order.splice(index, 0, clone); + }); + // Moving an existing button can blur it; retain keyboard focus across source reordering. + if (focused && doc.activeElement !== focused) focused.focus({ preventScroll: true }); // Ignore our own synchronous DOM writes; later application mutations still schedule a refresh. observer.takeRecords(); }; diff --git a/packages/theme-ios27/README.md b/packages/theme-ios27/README.md index 33ce8736..c1a78969 100644 --- a/packages/theme-ios27/README.md +++ b/packages/theme-ios27/README.md @@ -17,13 +17,13 @@ A theme for Ionic apps that brings iOS 27 Liquid Glass and motion to the Web. Ca ## What would you like to do? -| Goal | Guide | -| ----------------------------------------------------- | -------------------------------------------------------------------- | -| Style an existing Ionic app | [Get started](docs/setup.md) | -| Select iOS 26/27 styles and optionally use MD3 | [Adaptive themes](docs/adaptive-themes.md) | -| Add native iOS controls after setting up the theme | [Add Native UI](docs/add-native-ui.md) | -| Add a side rail to Ionic while keeping your own theme | [Vertical Bars step by step](docs/iphone-duo-with-original-theme.md) | -| Upgrade an existing installation | [Migration](docs/migration.md) | +| Goal | Guide | +| ----------------------------------------------------- | -------------------------------------------------------------------------------- | +| Style an existing Ionic app | [Get started](docs/setup.md) | +| Select iOS 26/27 styles and optionally use MD3 | [Adaptive themes](docs/adaptive-themes.md) | +| Add native iOS controls after setting up the theme | [Add Native UI](docs/add-native-ui.md) | +| Add a side rail to Ionic while keeping your own theme | [Vertical Bars step by step](../capacitor-native-ui/docs/ionic-vertical-bars.md) | +| Upgrade an existing installation | [Migration](docs/migration.md) | ## Get started diff --git a/packages/theme-ios27/docs/iphone-duo.md b/packages/theme-ios27/docs/iphone-duo.md index a59ab94f..b5280808 100644 --- a/packages/theme-ios27/docs/iphone-duo.md +++ b/packages/theme-ios27/docs/iphone-duo.md @@ -6,7 +6,7 @@ title: iPhone Duo support Adapt your Ionic app to iPhone Duo: place navigation and actions in its vertical system rail, and adjust your split pane as the device opens and closes. Existing Ionic markup remains the source of labels, icons, routing, and click handlers. -**New here?** Start with [iPhone Duo with your existing theme](iphone-duo-with-original-theme.md) to preview the side layout in Chrome. This page explains device events, placement and split panes. [Vertical Bars](../../capacitor-native-ui/docs/vertical-bars.md) documents control projection and the runtime API. +**New here?** Start with [iPhone Duo with your existing theme](../../capacitor-native-ui/docs/ionic-vertical-bars.md) to preview the side layout in Chrome. This page explains device events, placement and split panes. [Vertical Bars](../../capacitor-native-ui/docs/vertical-bars.md) documents control projection and the runtime API. iPhone Duo support is stable starting with version 2.0.0. The real system rail and hinge reporting require iOS 27.1 or later and an app built with Xcode 27.1 or newer. @@ -27,7 +27,7 @@ For the native package's integration, including application-owned CSS and plain ## Choose what to adopt -To keep your existing theme and add only the standalone support, follow [iPhone Duo with your existing theme](iphone-duo-with-original-theme.md). This page covers the shared device-layout rules. +To keep your existing theme and add only the standalone support, follow [iPhone Duo with your existing theme](../../capacitor-native-ui/docs/ionic-vertical-bars.md). This page covers the shared device-layout rules. | Goal | Stylesheet | Runtime | | ------------------------------------------ | ------------------- | ---------------------------------------------- | diff --git a/packages/theme-ios27/docs/setup.md b/packages/theme-ios27/docs/setup.md index 5d316979..85720a93 100644 --- a/packages/theme-ios27/docs/setup.md +++ b/packages/theme-ios27/docs/setup.md @@ -88,5 +88,5 @@ You should see an inset group with rounded edges. Switch the system between ligh - [Customize styles and components](features.md). - [Add Native UI](add-native-ui.md) for supported controls in a Capacitor iOS app. Theme installation alone does not enable native controls. -- [Add Vertical Bars with an existing theme](iphone-duo-with-original-theme.md). +- [Add Vertical Bars with an existing theme](../../capacitor-native-ui/docs/ionic-vertical-bars.md). - [Migrate an existing app](migration.md). diff --git a/packages/theme-ios27/docs/special-markup.md b/packages/theme-ios27/docs/special-markup.md index a44b73ed..d545046e 100644 --- a/packages/theme-ios27/docs/special-markup.md +++ b/packages/theme-ios27/docs/special-markup.md @@ -78,7 +78,7 @@ iPhone Duo support — the vertical system rail, hinge posture, and the posture- See [Vertical Bars](https://github.com/rdlabo-dev/ionic-theme-ios27/blob/main/packages/capacitor-native-ui/docs/vertical-bars.md) for rail layout classes, control eligibility and native button appearance. -For standalone setup while keeping your existing theme, see [iPhone Duo with your existing theme](https://github.com/rdlabo-dev/ionic-theme-ios27/blob/main/packages/theme-ios27/docs/iphone-duo-with-original-theme.md). +For standalone setup while keeping your existing theme, see [iPhone Duo with your existing theme](https://github.com/rdlabo-dev/ionic-theme-ios27/blob/main/packages/capacitor-native-ui/docs/ionic-vertical-bars.md). ## Two-line inset list items diff --git a/packages/theme-ios27/docs/vertical-bars-layout.md b/packages/theme-ios27/docs/vertical-bars-layout.md index ef025799..9f62eb51 100644 --- a/packages/theme-ios27/docs/vertical-bars-layout.md +++ b/packages/theme-ios27/docs/vertical-bars-layout.md @@ -10,7 +10,7 @@ npm install @rdlabo/ionic-theme-ios27 @use '@rdlabo/ionic-theme-ios27/dist/css/vertical-bars.css'; ``` -The layout behavior below assumes this stylesheet is loaded. For native runtime setup and device placement, follow the [integration guide](https://github.com/rdlabo-dev/ionic-theme-ios27/blob/main/packages/theme-ios27/docs/iphone-duo-with-original-theme.md). +The layout behavior below assumes this stylesheet is loaded. For native runtime setup and device placement, follow the [integration guide](https://github.com/rdlabo-dev/ionic-theme-ios27/blob/main/packages/capacitor-native-ui/docs/ionic-vertical-bars.md). ## Reserve the vertical rail