Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
cf19f9b
feat(ui): harden shadow boundary foundation
abharms Sep 25, 2026
4d60bab
test(ui): consolidate shadow stylesheet failures
abharms Sep 25, 2026
61c5cda
test(ui): prove shadow recovery across documents
abharms Sep 25, 2026
b34f08f
test(ui): assert document-scoped shadow stylesheet
abharms Sep 25, 2026
2810b30
feat(ui): isolate Bible pickers in Shadow DOM (YPE-5949)
abharms Sep 25, 2026
20269d4
test(ui): deduplicate picker portal evidence (YPE-5949)
abharms Sep 25, 2026
71a96b4
test(ui): strengthen picker boundary evidence (YPE-5949)
abharms Sep 26, 2026
89a61e0
test(ui): stabilize chapter picker search journey (YPE-5949)
abharms Sep 26, 2026
18f8327
test(ui): keep picker search proof deterministic (YPE-5949)
abharms Sep 26, 2026
82c8e56
test(ui): await picker data before search (YPE-5949)
abharms Sep 26, 2026
53d9874
test(ui): isolate picker journey data (YPE-5949)
abharms Sep 26, 2026
50008d3
test(ui): preserve picker initial state coverage (YPE-5949)
abharms Sep 26, 2026
6c29ccc
test(ui): avoid dynamic picker height assertion (YPE-5949)
abharms Sep 26, 2026
fe798d9
docs(ui): align picker rollout records (YPE-5949)
abharms Sep 26, 2026
3940cbe
chore(ui): merge Shadow DOM foundation into YPE-5949
abharms Sep 26, 2026
1cd59d4
Merge branch 'journey-to-the-shadow-dom' into ype-5949-isolate-bible-…
abharms Sep 26, 2026
ec2e4c8
Merge branch 'journey-to-the-shadow-dom' into ype-5949-isolate-bible-…
abharms Sep 26, 2026
684b563
docs(ui): include reader search in Shadow DOM rollout
abharms Sep 27, 2026
ad5620b
docs(ui): refresh Shadow DOM evidence guidance
abharms Sep 27, 2026
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/salty-lizards-dance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
19 changes: 11 additions & 8 deletions docs/adr/0007-prototype-shadow-dom-style-isolation.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ YPE-5356 accepts the architecture for a coordinated major-version rollout
across the compatible public UI boundary defined in the
[production rollout policy](../shadow-dom-rollout-policy.md). Implementation is
split into dependency-ordered component groups, but the package must not publish
a partial boundary. This ADR continues to describe current runtime behavior
until those groups land: only `YouVersionAuthButton` creates an automatic shadow
root.
a partial boundary. In the current stable package, only `YouVersionAuthButton`
creates an automatic shadow root. On the Shadow DOM integration branch,
`BibleChapterPicker.Root` and `BibleVersionPicker.Root` also create automatic
boundaries while the remaining rollout groups and coordinated release are
pending.

The automatic boundary belongs to the SDK-owned top-level component instance.
Compound members and SDK components composed inside another isolated SDK
Expand Down Expand Up @@ -178,11 +180,12 @@ Radix's development-only relationship checks can also emit warnings for valid
IDs inside a shadow root because those checks query the document rather than
the root.

Only `YouVersionAuthButton` is automatically isolated by this prototype.
`BibleVersionPicker` and other public exports do not gain automatic isolation
from the opt-in validation work. The internal `SignInDialog` is validated only
through an opt-in story. Wider automatic isolation requires completing the
linked implementation groups and coordinated major-release gate.
In the current stable package, only `YouVersionAuthButton` is automatically
isolated. On the Shadow DOM integration branch, `BibleChapterPicker.Root` and
`BibleVersionPicker.Root` also use automatic boundaries. The internal
`SignInDialog` remains validated only through an opt-in story. Wider automatic
isolation still requires the linked implementation groups and the coordinated
major-release gate.

The detailed experimental evidence remains in the
[Shadow DOM isolation validation plan](../shadow-dom-isolation-plan.md). The
Expand Down
33 changes: 17 additions & 16 deletions docs/shadow-dom-consumer-compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

This contract records cross-browser evidence for consumer-facing behavior at
the SDK's Shadow DOM boundary. YPE-5356 incorporates it into the
[production rollout policy](shadow-dom-rollout-policy.md); this contract alone
does not enable automatic isolation beyond `YouVersionAuthButton`.
[production rollout policy](shadow-dom-rollout-policy.md). On the Shadow DOM
integration branch, `YouVersionAuthButton`, `BibleChapterPicker.Root`, and
`BibleVersionPicker.Root` create automatic boundaries; the coordinated stable
release is still pending.

The executable evidence lives in
`consumer-compatibility.shadow-isolation.stories.tsx`. The existing
Expand All @@ -19,8 +21,9 @@ scope.
public event and forwarded-ref props.
- `Textarea`, rendered through the internal opt-in `ShadowRootHost`, isolates a
native form control without adding a production behavior or public wrapper.
- `BibleVersionPicker`, also rendered through the opt-in host, exercises a
composed public module with shadow-local floating content.
- `BibleChapterPicker.Root` and `BibleVersionPicker.Root` exercise automatic
compound-component boundaries with shadow-local floating content. Their
trigger, content, and language members reuse the owning root.

These modules validate the shared boundary and specific public interfaces they
exercise. They do not establish compatibility for every SDK component.
Expand All @@ -40,7 +43,8 @@ exercise. They do not establish compatibility for every SDK component.
| An automatically isolated component is nested inside another open SDK shadow root | Supported for basic rendering, traversal, and composed events | `NestedRootsRequireTraversalAndRetargetAtEveryBoundary` verifies recursive root traversal and target retargeting to the inner host in the outer scope and to the outer host in the document scope. Consumers must traverse every root explicitly. |
| Nested overlays inside shadow roots | Supported in current browser evidence | YPE-5355 verifies nested dialog and popover stacking, focus, inertness, dismissal, and restoration through the shared shadow-local portal infrastructure. Repeat component-specific validation during rollout. |
| 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 ID relationships inside `BibleVersionPicker` | Supported in current browser evidence | `TopLayerEscapesClippingAndPreservesSemantics` verifies that the trigger and controlled panel remain in one root and Chromium, Firefox, and Playwright WebKit resolve their `aria-controls` relationship. This does not make cross-scope ID references supported. |
| 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. |

## Consumer risks

Expand All @@ -55,7 +59,7 @@ an automation and styling boundary rather than a security boundary. Selectors
that depend on internal markup remain fragile even when they traverse the root.

The focused Shadow DOM suite runs in Chromium, Firefox, and Playwright WebKit.
All 22 current stories returned assertion-level success in local Safari 26.6.2
The 22-story pre-picker-rollout baseline returned assertion-level success in local Safari 26.6.2
through SafariDriver when each ran in a fresh browser session. A single
long-lived SafariDriver session stalled on the sign-in dialog and verse action
popover stories after 20 successes, so isolated sessions are required for this
Expand All @@ -64,14 +68,11 @@ assistive technologies remain unverified. Reflected ARIA element properties
demonstrate DOM relationship resolution, not announcements or other
assistive-technology behavior.

## Follow-up work outside this ticket
## Follow-up work

No production defect is fixed by this validation ticket. If a selected rollout
component must participate in an outer native form or consume external labeling
relationships, create a component-specific implementation ticket for an
explicit public contract rather than relying on cross-scope browser behavior.
The current ticket's actual-Safari smoke is recorded above. Recurring Safari and
deferred assistive-technology validation, consumer-facing release documentation,
and production implementation are assigned by the
[production rollout policy](shadow-dom-rollout-policy.md). No runtime behavior
or Jira issue is created by this compatibility document.
If a rollout component must participate in an outer native form or consume
external labeling relationships, it needs an explicit public contract rather
than cross-scope browser behavior. Recurring Safari, deferred assistive-
technology validation, release documentation, and the coordinated stable
release remain assigned by the
[production rollout policy](shadow-dom-rollout-policy.md).
35 changes: 19 additions & 16 deletions docs/shadow-dom-isolation-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,18 @@ This is a working plan, not approval for package-wide rollout.

## Current scope

- `YouVersionAuthButton` is the only component automatically isolated by the
current prototype.
- `BibleVersionPicker` validates shadow-local inline and native top-layer
floating content through opt-in stories.
- In the current stable package, `YouVersionAuthButton` is the only component
with automatic isolation. On the Shadow DOM integration branch,
`BibleChapterPicker.Root` and `BibleVersionPicker.Root` also create automatic
boundaries.
- The picker roots validate shadow-local native top-layer floating content
through their public runtime boundaries. A historical inline negative
control demonstrated clipping beyond a constrained ancestor before that
story was removed after strategy selection.
- The shared Dialog and Popover primitives support opt-in shadow-local portals.
- `VerseActionPopover` uses the shared portal-state infrastructure while
retaining its specialized direct Radix composition.
- `BibleVersionPicker` and other public exports do not automatically create
Shadow DOM boundaries.
- Other public exports do not yet automatically create Shadow DOM boundaries.
- The internal `SignInDialog` is validated only through an opt-in
`ShadowRootHost` story.
- Nested and concurrent overlays within and across component shadow roots were
Expand Down Expand Up @@ -61,8 +64,8 @@ functions; assistive-technology checks remain open.
| Host CSS isolation | Hostile-CSS demos and focused browser coverage exercise element selectors, direction inheritance, vertical writing and typography resets, hostile custom properties, universal `!important` rules, host attacks, and generated pseudo-content. | Validated in Chromium, Firefox, Playwright WebKit, and local Safari 26.6.2 | Repeat against each component selected for rollout. |
| SSR and hydration | Focused React coverage verifies reuse of the exact empty server host, matching hydration without recoverable errors or duplicate content, and a null forwarded ref before the passive-effect mount. | Validated for the client-only prototype | Decide per rollout component whether a possibly empty first paint, layout shift, and no-JavaScript absence are acceptable. |
| Component behavior | Auth button interaction works through the React portal; Strict Mode does not attach the root twice. | Validated for the prototype | Audit component-specific refs, events, and consumer integrations during rollout. |
| Owner-document handling | Focused coverage mounts into a same-origin iframe and verifies document-compatible stylesheet construction. | Validated in Chromium, Firefox, Playwright WebKit, and local Safari 26.6.2 | Verify stylesheet failure recovery. |
| Inline floating content | The picker negative control preserves tree-scope relationships but demonstrates clipping beyond a constrained ancestor. | Validated as a negative control | None; clipping is why inline placement is not the selected escaping strategy. |
| Owner-document handling | Focused coverage mounts into a same-origin iframe and verifies document-compatible stylesheet construction. YPE-5947 also covers construction, replacement, and adoption failure recovery. | Validated in Chromium, Firefox, Playwright WebKit, and local Safari 26.6.2 | None. |
| Inline floating content | A historical picker negative control preserved tree-scope relationships but demonstrated clipping beyond a constrained ancestor. | Validated historically; the story was removed after strategy selection. | None; clipping is why inline placement is not the selected escaping strategy. |
| Native top-layer floating content | Picker stories verify clipping escape, hit testing, collision handling, hostile-CSS isolation, and resolved `aria-controls` relationships. | Validated in Chromium, Firefox, Playwright WebKit, and local Safari 26.6.2 | Verify assistive-technology behavior and repeat actual-Safari checks for significant platform changes. |
| Portal lifecycle | Unit and browser coverage exercise lazy creation, exit-animation retention, cleanup, immediate reopen behavior, and the direct-Radix `VerseActionPopover` consumer. | Validated for shared primitives and the known bypass | Repeat the consumer audit when adding another direct overlay primitive. |
| Dialog relationships | Browser coverage resolves title and description relationships inside the component tree. | Validated in Chromium, Firefox, Playwright WebKit, and an isolated local Safari 26.6.2 run | Verify announcements with real assistive technology. |
Expand Down Expand Up @@ -131,7 +134,7 @@ generated `--tw-*` names, and the local `--spacing` compatibility alias.

| Name or namespace | Classification and ownership |
| --- | --- |
| `--yv-*` | SDK-owned properties. The README's documented overrides are supported consumer inputs for light-DOM components under `[data-yv-sdk]`. They are not a public document-level override API for the automatically isolated `YouVersionAuthButton`. |
| `--yv-*` | SDK-owned properties. The README's documented overrides are supported consumer inputs for light-DOM components under `[data-yv-sdk]`. They are not a public document-level override API for automatically isolated components. |
| `--tw-*` | Tailwind and `tw-animate-css` implementation state that is declared or initialized in the compiled stylesheet. It is not a supported consumer input. |
| `--spacing` | SDK-owned local compatibility alias for `--yv-spacing`, used by `tw-animate-css`. The fixed Tailwind spacing scale is inlined into generated utilities. |
| Authored `--font-*`, `--color-*`, and `--radius-*` theme aliases | Compile-time Tailwind inputs that produce utilities backed by `--yv-*` values. They are not runtime consumer inputs. |
Expand Down Expand Up @@ -182,8 +185,8 @@ separately in YPE-5749.
size; review that accepted sizing input for each rollout component.
- Repeat the documented event, ref, nested-root, and shadow-aware automation
checks for every public component selected for rollout.
- Verify stylesheet construction and adoption failure recovery beyond the
current feature fallback.
- Preserve YPE-5947's stylesheet construction, replacement, and adoption
failure-recovery coverage.
- Preserve YPE-5437's realistic-usage fixture as the shared-host regression
check. Its one-machine mount comparison is diagnostic, so selected rollout
components still need user-visible performance review in their intended
Expand All @@ -207,11 +210,11 @@ separately in YPE-5749.
same-page JavaScript from inspecting or mutating the root.
- The focused Shadow DOM browser suite runs in Chromium, Firefox, and Playwright
WebKit. Playwright WebKit is not a substitute for testing actual Safari. All
22 current stories returned explicit success events in local Safari 26.6.2
when each ran in a fresh SafariDriver session. A single long-lived session
returned success for 20/22 and left the sign-in and verse-action play
functions pending. The cause of that session-dependent stall is unresolved;
repeat actual-Safari validation in isolated sessions.
22-story pre-picker-rollout baseline returned explicit success events in
local Safari 26.6.2 when each story ran in a fresh SafariDriver session. A
single long-lived session returned success for 20/22 and left the sign-in and
verse-action play functions pending. The cause of that session-dependent
stall is unresolved; repeat actual-Safari validation in isolated sessions.
- Browser DOM relationship reflection is not a substitute for VoiceOver, NVDA,
or other real assistive-technology verification.

Expand Down
23 changes: 16 additions & 7 deletions docs/shadow-dom-rollout-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
YPE-5356 approves a coordinated package-wide rollout plan for compatible public
UI components. It does not claim that the rollout has shipped. Until every
included implementation group and the release gate below are complete, the
runtime behavior remains the prototype recorded in
[ADR 0007](adr/0007-prototype-shadow-dom-style-isolation.md): only
`YouVersionAuthButton` creates an automatic shadow boundary.
runtime behavior on the Shadow DOM integration branch remains an in-progress
implementation of the prototype recorded in
[ADR 0007](adr/0007-prototype-shadow-dom-style-isolation.md):
`YouVersionAuthButton`, `BibleChapterPicker.Root`, and
`BibleVersionPicker.Root` create automatic shadow boundaries. The picker member
exports reuse their owning root rather than creating independent boundaries.

The rollout is coordinated at release time, not implemented in one change.
Focused component or component-group tickets may land independently on the
Expand Down Expand Up @@ -47,6 +50,7 @@ component rollout targets.
| `BibleVersionPicker.Root`, `.Trigger`, `.Content` | Included as one compound component | `Root` only | Promote the validated opt-in host to the public root and audit custom trigger styling, storage, focus, and native top-layer behavior. |
| `BibleLanguagePickerContent`, `BibleVersionPickerLanguageTrigger` | Included transitively | No independent boundary | Both require `BibleVersionPicker.Root` context and stay inside that root. Direct use outside the root is already unsupported. |
| `BibleReader.Root`, `.Content`, `.Toolbar` | Included as one compound component | `Root` only | Keep reader content, toolbar, pickers, settings, verse actions, and dialogs in one boundary. Audit consumer children, scrolling, selection, overlays, focus, refs, and first paint. |
| `BibleReaderSearch` | Included transitively | No independent boundary | It requires `BibleReader.Root` context and stays inside that root whether rendered by the toolbar or directly by a consumer. Preserve its shadow-local popover, controlled and host-owned modes, navigation, dismissal, and focus behavior. |
| `BibleThemeSettingsContent` | Included | Its standalone mount, or the owning reader boundary | Preserve its Expo DOM callback contract and avoid a nested boundary when rendered by `BibleReader`. |
| `BibleTextView` | Included | Its standalone mount, or the owning card/reader boundary | Preserve scripture rendering, footnote portals, selection callbacks, and reader stylesheet behavior without nesting roots inside composed SDK components. |
| `FootnoteContent` | Included | Its standalone mount, or the owning scripture boundary | Treat it as a leaf when used alone and reuse the enclosing `BibleTextView` boundary otherwise. |
Expand Down Expand Up @@ -122,15 +126,19 @@ exported function. Each ticket links to YPE-5356 and this policy.
only boundaries for their compound exports.
- Validate custom trigger children, context, storage, search inputs, focus,
collision handling, and shadow-local top-layer behavior.
- Retain document-root `rem` scaling as the accepted sizing input; do not add
a picker-specific root-font reset.
4. **YPE-5950: Scripture presentation**
- Roll out standalone `BibleTextView`, `VerseOfTheDay`, and `BibleCard`.
- Validate reader styles, scripture direction, footnotes, highlights,
sharing, loading/error states, picker composition, sizing, and first paint.
5. **YPE-5951: Bible reader**
- Roll out `BibleReader.Root` as the boundary for reader content, toolbar,
pickers, settings, verse actions, permission dialogs, and sign-in dialogs.
- Validate selection, scrolling, native-host callback modes, nested overlay
order, focus restoration, and user-visible performance in intended layouts.
`BibleReaderSearch`, pickers, settings, verse actions, permission dialogs,
and sign-in dialogs.
- Validate selection, search navigation and dismissal, scrolling, native-host
callback modes, nested overlay order, focus restoration, and user-visible
performance in intended layouts.
6. **YPE-5952: Coordinated release**
- Land all included groups, complete the package and component gates, update
consumer documentation, and publish the behavior as one major release.
Expand All @@ -139,7 +147,8 @@ YPE-5947 adds no public Shadow DOM configuration and does not expand the current
automatic boundary beyond `YouVersionAuthButton`. Its independently releasable
runtime effect is a patch-level resilience fix for stylesheet installation; the
additional automatic component boundaries remain part of the coordinated major
release.
release. The picker boundaries described above are implemented on the Shadow DOM
integration branch but are not yet a stable-package release contract.

Excluded components are not hidden work in these groups. `Textarea` needs a
separately approved form contract, and standalone `VerseActionPopover` needs an
Expand Down
Loading
Loading