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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changeset/reader-shadows-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
2 changes: 1 addition & 1 deletion .size-limit.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export default [
name: 'ui / Separator only',
path: 'packages/ui/dist/index.js',
import: '{ Separator }',
limit: '21.6 KB',
limit: '21.7 KB',
ignore: ['react', 'react-dom', 'react/jsx-runtime', '@tanstack/react-query'],
},
{
Expand Down
20 changes: 15 additions & 5 deletions docs/shadow-dom-consumer-compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,17 @@ the SDK's Shadow DOM boundary. YPE-5356 incorporates it into the
current stable-package and integration-branch boundary inventory and coordinated
release status.

The executable evidence lives in
`consumer-compatibility.shadow-isolation.stories.tsx`. The existing
`bible-version-picker.shadow-isolation.stories.tsx` suite supplies additional
evidence for shadow-aware queries and relationships that stay within one tree
scope.
The executable evidence is grouped by rollout surface:

- `consumer-compatibility.shadow-isolation.stories.tsx` covers shared consumer
boundary behavior.
- `bible-version-picker.shadow-isolation.stories.tsx` covers shadow-aware picker
queries and relationships that stay within one tree scope.
- `scripture-presentation-shadow-isolation.test.tsx` and
`scripture-presentation.shadow-isolation.stories.tsx` cover `BibleTextView`,
`VerseOfTheDay`, and `BibleCard`.
- `bible-reader-shadow-isolation.test.tsx` and `bible-reader.stories.tsx` cover
the `BibleReader.Root` boundary and consumer composition contract.

## Representative modules

Expand All @@ -26,6 +32,9 @@ scope.
- `BibleTextView`, `VerseOfTheDay`, and `BibleCard` exercise automatic
scripture-presentation boundaries and reuse the owning root for composed
scripture and picker content.
- `BibleReader.Root` exercises a compound application surface whose arbitrary
React children move into the reader root while reader-owned descendants
reuse that boundary.

These modules validate the shared boundary and specific public interfaces they
exercise. They do not establish compatibility for every SDK component.
Expand All @@ -48,6 +57,7 @@ exercise. They do not establish compatibility for every SDK component.
| Concurrent peer popovers inside the same or separate component roots | Unsupported as simultaneous peers | Opening a peer dismisses the current popover through Radix outside interaction. YPE-5356 accepts this single-active-peer behavior; supporting simultaneous peers requires a demonstrated product journey and separate design. |
| Shadow-local picker relationships | Supported in current browser evidence | The chapter and version picker stories verify that each trigger and controlled panel remain in one root and resolve their `aria-controls` relationship. This does not make cross-scope ID references supported. |
| Consumer-supplied picker triggers | Supported within the explicit styling contract | The supplied element remains the interactive trigger. Inline style, ordinary attributes, and SDK-embedded utility classes are preserved. Document/global class rules and document-level token overrides do not cross the root. The SDK does not promise CSS Parts, arbitrary stylesheet injection, or styling of picker internals. |
| Arbitrary `BibleReader.Root` children | Supported inside the reader root; breaking DOM/CSS placement change | Children render in the reader's open shadow root. Their React context and callbacks remain intact, but document-root selectors, global CSS, and document queries no longer reach them. A child that is itself an automatically isolated public component may intentionally create a nested root; reader-owned SDK composition suppresses accidental nesting. YPE-5952 owns the coordinated release documentation. |

## Consumer risks

Expand Down
57 changes: 47 additions & 10 deletions docs/shadow-dom-isolation-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,23 +138,22 @@ state confirmed the selected Untitled Serif value, closed dialog, and restored
trigger focus. This focused browser evidence does not establish
assistive-technology support.

The current light-DOM `BibleReader` suppresses the standalone boundary around
its owned settings. A simulated future reader boundary proves that composition
produces exactly one outer host, no nested settings host, and a settings body in
the outer root. YPE-5952 must document the empty-host client-only contract,
open-root automation, native event retargeting, loss of global-CSS access, and
the supported explicit theme, direction, and callback inputs as part of the
coordinated major release. YPE-5994 has an empty changeset and must not publish
an independent partial rollout.
`BibleReader.Root` suppresses the standalone boundary around its owned settings,
so the settings body remains in the reader root without nesting. YPE-5952 must
document the empty-host client-only contract, open-root automation, native
event retargeting, loss of global-CSS access, and the supported explicit theme,
direction, and callback inputs as part of the coordinated major release.
YPE-5994 has an empty changeset and must not publish an independent partial
rollout.

## YPE-5950 scripture presentation evidence

Standalone `BibleTextView`, `VerseOfTheDay`, and `BibleCard` now each own one
client-only open shadow root and reuse the exact empty server host during
hydration. `VerseOfTheDay` and `BibleCard` suppress the automatic boundary on
their composed `BibleTextView`; `BibleCard` also suppresses it on its composed
version picker. The current light-DOM `BibleReader` suppresses the boundary on
its owned scripture renderer until the reader root lands in YPE-5951.
version picker. `BibleReader` suppresses the standalone `BibleTextView`
boundary so its owned scripture renderer reuses the reader root.

Focused unit coverage verifies the exact host matrix, hydration without nested
or duplicate roots, open-root queries, native event retargeting, the React verse
Expand Down Expand Up @@ -182,6 +181,44 @@ portal path and the application-root retargeted path. The shared-host correction
and click, keyboard, and focus regression coverage are assigned to YPE-6040 and
must land before YPE-5952 releases the coordinated major version.

## YPE-5951 Bible reader evidence

`BibleReader.Root` now owns one client-only open shadow root and reuses the
exact empty server host during hydration. Reader-owned content, toolbar,
search, scripture, chapter and version pickers, theme settings, avatar, verse
actions, and authentication dialogs remain in that root; the chapter and
version picker `Root` components explicitly reuse it. A consumer may still
intentionally render another public isolated component as an arbitrary reader
child, which creates the documented nested-root topology.

Focused unit evidence verifies hydration without recoverable errors, empty
light DOM, no accidental reader-owned roots, direct `BibleReaderSearch`
placement, arbitrary child relocation, and intentional public nesting. The
existing detailed reader behavior suites continue at their reusable
implementation seams. The existing search and verse-selection browser
journeys traverse the reader root, and focused hostile-CSS/constrained-layout
coverage reviews theme, interface and Scripture direction, typography,
scrolling, and geometry in Chromium, Firefox, and Playwright WebKit.

The concrete `examples/vite-react` reader page was also reviewed in its
`h-[calc(100vh-3.5rem)]` shell beneath the example navbar: the reader surface
continues to fill that shell, its toolbar remains above the independently
scrolling scripture pane, and the constrained browser evidence retains usable
toolbar and scripture geometry. Interactive review found no obvious
user-visible performance regression while opening reader-owned settings,
search, verse actions, and auth overlays; each reuses the one reader root and
local overlay rather than adding duplicate automatic hosts. This is a
qualitative product-layout check, not benchmark evidence.

The ADR's client-only first-paint gap is accepted for the reviewed reader
layout: without JavaScript the reader remains absent, and nearby layout can
move after the passive-effect mount. This evidence is qualitative and does not
claim zero layout shift, a benchmark, actual-Safari coverage, or
assistive-technology coverage. YPE-5952 must document that arbitrary
`BibleReader.Root` children move into the open root, so document-root queries
and global CSS no longer reach them. YPE-6040's ancestor React-event dispatch
correction remains out of scope.

## Safari spacing evidence

Actual Safari 26.6.2 exposed a visual gap the initial focused assertions missed:
Expand Down
8 changes: 5 additions & 3 deletions packages/ui/src/components/bible-reader-controlled.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,11 @@ function renderReader(props: Partial<BibleReaderRootProps> = {}, overrides?: Hoo
it('keeps reader-owned scripture in the reader tree without a standalone shadow boundary', async () => {
const { container } = render(
<HookOverrideProvider overrides={defaultOverrides()}>
<BibleReader.Root defaultVersionId={111} defaultBook="JHN" defaultChapter="1">
<BibleReader.Content />
</BibleReader.Root>
<ReuseShadowBoundary>
<BibleReader.Root defaultVersionId={111} defaultBook="JHN" defaultChapter="1">
<BibleReader.Content />
</BibleReader.Root>
</ReuseShadowBoundary>
</HookOverrideProvider>,
);

Expand Down
49 changes: 30 additions & 19 deletions packages/ui/src/components/bible-reader-navigation.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { BibleReader, useBibleReaderContext } from './bible-reader';
import { BibleReaderNavigation } from './bible-reader-navigation';
import { HookOverrideProvider } from '@/test/hook-overrides';
import type { HookOverrides } from '@youversion/platform-react-hooks';
import { ReuseShadowBoundary } from '@/lib/shadow-isolation';

function CurrentDestination() {
const { book, chapter, versionId, verseFocus } = useBibleReaderContext();
Expand All @@ -26,9 +27,15 @@ it('consumes the newest pre-mount request once and supports mounted cross-versio
const jsx = (
<StrictMode>
<HookOverrideProvider overrides={overrides}>
<BibleReader.Root navigation={navigation} defaultVersionId={111} onVersionChange={changed}>
<CurrentDestination />
</BibleReader.Root>
<ReuseShadowBoundary>
<BibleReader.Root
navigation={navigation}
defaultVersionId={111}
onVersionChange={changed}
>
<CurrentDestination />
</BibleReader.Root>
</ReuseShadowBoundary>
</HookOverrideProvider>
</StrictMode>
);
Expand Down Expand Up @@ -102,9 +109,11 @@ it('renders passage-only and full-chapter destinations through the reader fetch'
render(
<StrictMode>
<HookOverrideProvider overrides={overrides}>
<BibleReader.Root navigation={navigation} highlights={[]}>
<BibleReader.Content />
</BibleReader.Root>
<ReuseShadowBoundary>
<BibleReader.Root navigation={navigation} highlights={[]}>
<BibleReader.Content />
</BibleReader.Root>
</ReuseShadowBoundary>
</HookOverrideProvider>
</StrictMode>,
);
Expand Down Expand Up @@ -145,19 +154,21 @@ it('waits for controlled destinations and never revives an activated passage or
const reader = (versionId: number, book: string, chapter: string) => (
<StrictMode>
<HookOverrideProvider overrides={overrides}>
<BibleReader.Root
navigation={navigation}
versionId={versionId}
book={book}
chapter={chapter}
onVersionChange={changed}
onBookChange={changed}
onChapterChange={changed}
highlights={[]}
>
<CurrentDestination />
<BibleReader.Content />
</BibleReader.Root>
<ReuseShadowBoundary>
<BibleReader.Root
navigation={navigation}
versionId={versionId}
book={book}
chapter={chapter}
onVersionChange={changed}
onBookChange={changed}
onChapterChange={changed}
highlights={[]}
>
<CurrentDestination />
<BibleReader.Content />
</BibleReader.Root>
</ReuseShadowBoundary>
</HookOverrideProvider>
</StrictMode>
);
Expand Down
Loading
Loading