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/bright-shadows-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
47 changes: 33 additions & 14 deletions docs/adr/0007-prototype-shadow-dom-style-isolation.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
# ADR 0007: Prototype automatic Shadow DOM style isolation

Status: Proposed (validated experimentally; not approved for production rollout)
Status: Accepted for coordinated rollout planning; production expansion is not yet shipped

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.

The automatic boundary belongs to the SDK-owned top-level component instance.
Compound members and SDK components composed inside another isolated SDK
component reuse their owning boundary instead of creating accidental nested
roots. Excluded exports and their reasons are defined in the rollout policy.

Host applications can apply unlayered global CSS, including Tailwind preflight,
that outranks the UI package's layered styles. Resets, stronger selectors,
Expand Down Expand Up @@ -119,11 +132,12 @@ available after the shadow content mounts and points to the real component
element inside the shadow root, not to the light-DOM host. Before that mount,
the component cannot receive focus or interaction.

This contract must be accepted separately for every component selected for
automatic isolation. A component that requires server content, no-JavaScript
content, or a stable first-paint footprint cannot use this host unchanged.
YPE-5356 owns that rollout policy, including whether a component needs reserved
space, a product timing budget, or a different SSR strategy.
This contract is reviewed separately for every implementation group selected
for automatic isolation. YPE-5356 accepts it as the shared starting point, but a
component that requires server content, no-JavaScript content, or a stable
first-paint footprint cannot use this host unchanged. Its implementation ticket
must exclude it or define reserved space, a product timing budget, or a
separately approved SSR strategy.

## Consequences

Expand Down Expand Up @@ -154,9 +168,11 @@ that closes and reopens during retained exit presence also preserves and restore
its original opener; disconnected targets and targets moved out of their captured
root (into the light DOM, another shadow root, or another document) are ignored.

These observations do not select or design production overlay coordination.
YPE-5356 owns deciding whether and how to support concurrent peers. The detailed
Chromium evidence and remaining validation live in the rollout plan.
YPE-5356 accepts single-active-peer popover dismissal rather than introducing
cross-root overlay coordination without a demonstrated product journey. The
focus-restoration defects found by YPE-5355 were resolved by YPE-5889/PR 414 and
remain covered as regressions. The detailed cross-browser evidence and remaining
validation live in the rollout plan.

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
Expand All @@ -165,8 +181,11 @@ 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. Any wider rollout requires a separate decision and
change.

The detailed evidence, unresolved audits, and rollout gates live in the
[Shadow DOM isolation validation and rollout plan](../shadow-dom-isolation-plan.md).
through an opt-in story. Wider automatic isolation requires completing the
linked implementation groups and coordinated major-release gate.

The detailed experimental evidence remains in the
[Shadow DOM isolation validation plan](../shadow-dom-isolation-plan.md). The
[production rollout policy](../shadow-dom-rollout-policy.md) records the public
component boundary, accepted limitations, implementation order, and release
gates without representing that planned behavior as shipped.
37 changes: 9 additions & 28 deletions docs/shadow-dom-consumer-compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
## Purpose

This contract records cross-browser evidence for consumer-facing behavior at
the SDK's Shadow DOM boundary. It is input to YPE-5356's production rollout
policy, not approval for automatic isolation beyond `YouVersionAuthButton`.
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`.

The executable evidence lives in
`consumer-compatibility.shadow-isolation.stories.tsx`. The existing
Expand Down Expand Up @@ -37,7 +38,8 @@ exercise. They do not establish compatibility for every SDK component.
| An ordinary document or Storybook-canvas selector finds SDK internals | Unsupported | DOM selector APIs do not cross a shadow boundary. `document.querySelector` and Testing Library queries rooted at the document need explicit open-root traversal. Automation behavior is tool-specific: [Playwright locators pierce open roots by default](https://playwright.dev/docs/locators#locate-in-shadow-dom), except for XPath locators, while closed roots remain inaccessible. |
| A consumer traverses an open root and queries after attachment | Supported with timing and access constraints | Wait for the host's open `shadowRoot`, then query within it. The contract depends on the prototype's open-root policy and does not make internals a stable semantic API; prefer public refs, roles, and component callbacks where available. |
| 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 or concurrent overlays inside shadow roots | Unsupported by this contract | YPE-5355 owns stacking, focus, inertness, dismissal, and restoration. Basic nested-root evidence here does not change that overlay boundary. |
| 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. |

## Consumer risks
Expand All @@ -62,35 +64,14 @@ assistive technologies remain unverified. Reflected ARIA element properties
demonstrate DOM relationship resolution, not announcements or other
assistive-technology behavior.

## Input for YPE-5356

The rollout policy should treat automatic isolation as a compatibility change
and require a component-specific audit before each rollout. In particular, it
must:

- identify consumers that rely on native outer-form participation, external
labels or ARIA ID references, document-rooted queries, synchronous refs, or
unretargeted native events;
- prefer rollout candidates whose public callbacks, refs, and internal labels
already avoid those cross-scope dependencies;
- define consumer automation guidance around roles, public refs, and
tool-specific shadow behavior: [Playwright locators pierce open roots by
default](https://playwright.dev/docs/locators#locate-in-shadow-dom), while DOM
selector APIs need explicit traversal after root attachment and internal
rendering;
- preserve Firefox and WebKit coverage, define when to repeat actual-Safari
validation, and define required assistive-technology evidence rather than
treating browser DOM results as universal; and
- preserve YPE-5355's separate ownership of nested and concurrent overlay
behavior.

## Follow-up work outside this ticket

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
assistive-technology validation, consumer-facing rollout documentation, and any
production implementation belong to YPE-5356 or separately authorized follow-up
tickets. No new Jira issue is created by this document.
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.
73 changes: 36 additions & 37 deletions docs/shadow-dom-isolation-plan.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Shadow DOM Isolation Validation and Rollout Plan

YPE-5356 reconciles this research into the
[Shadow DOM production rollout policy](shadow-dom-rollout-policy.md). This file
remains the detailed evidence inventory; the policy owns the approved public
boundary, implementation groups, accepted limitations, and coordinated release
gate. Neither document changes the current integration-branch prototype by
itself.

## Why this doc exists

[ADR 0007](adr/0007-prototype-shadow-dom-style-isolation.md) records the durable
Expand All @@ -25,8 +32,9 @@ This is a working plan, not approval for package-wide rollout.
- Nested and concurrent overlays within and across component shadow roots were
exercised through the real shared `ShadowRootHost` implementation (YPE-5355).
The Shadow DOM ADR records the architectural boundary; the results below record
the supported contract and the remaining peer-dismissal gap. Broader component
rollout and peer-overlay coordination remain with YPE-5356.
the supported contract and the remaining peer-dismissal limitation. The
production policy accepts single-active-peer dismissal and assigns broader
component rollout to focused follow-up work.

## Nested and concurrent overlay evidence

Expand Down Expand Up @@ -59,12 +67,12 @@ functions; assistive-technology checks remain open.
| 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. |
| Dialog keyboard containment | Browser coverage exercises initial focus, programmatic escape redirection, forward and reverse traversal, radio-group collapsing, negative `tabindex`, and wraparound. | Validated in Chromium, Firefox, Playwright WebKit, and an isolated local Safari 26.6.2 run | Verify assistive-technology behavior. |
| Dialog modal lifetime | Coverage verifies inert background content while open and through staggered Content and Overlay exit animations. YPE-5355 also exercises both unmount orders for overlapping popover and dialog exits. | Validated for order-independent teardown | YPE-5356 owns peer concurrency across component roots. Verify assistive-technology behavior and repeat actual-Safari checks for significant platform changes. |
| Dialog modal lifetime | Coverage verifies inert background content while open and through staggered Content and Overlay exit animations. YPE-5355 also exercises both unmount orders for overlapping popover and dialog exits. | Validated for order-independent teardown | The rollout policy accepts single-active-peer dismissal. Verify assistive-technology behavior and repeat actual-Safari checks for significant platform changes. |
| Dialog dismissal and restoration | Coverage exercises Escape, backdrop click, full-viewport hit testing, overlay-only focus, and restoration after both modal nodes unmount. | Validated in Chromium, Firefox, Playwright WebKit, and isolated local Safari 26.6.2 runs | Verify real screen-reader behavior. |
| Consumer form participation | Browser coverage verifies that a light-DOM form does not own or serialize a native control inside an SDK shadow root. | Unsupported across tree scopes | Use an explicit component contract if a rollout target requires outer-form participation. |
| Consumer labels and ARIA ID references | Browser coverage verifies that external native labels, `aria-labelledby`, and `aria-describedby` relationships do not resolve to controls inside the root. | Unsupported across tree scopes | Keep relationships in one tree scope; verify real assistive technology separately. |
| Consumer events, refs, and automation | Coverage verifies native retargeting, the auth button's React handler and forwarded ref, open-root queries, and effect-driven attachment timing. | Supported with documented constraints | Repeat for each public component selected for rollout. |
| Nested shadow roots | Coverage verifies basic rendering, recursive queries, and event retargeting at each boundary. | Supported for the validated basics | Peer-overlay coordination remains with YPE-5356; verify assistive-technology behavior and repeat actual-Safari checks for significant platform changes. |
| Nested shadow roots | Coverage verifies basic rendering, recursive queries, and event retargeting at each boundary. | Supported for the validated basics | Single-active-peer dismissal is accepted; verify assistive-technology behavior and repeat actual-Safari checks for significant platform changes. |
| Realistic same-page usage | YPE-5437 mounts, removes, and re-adds a 12-component mix in Normal and Strict Mode. Chromium, Firefox, Playwright WebKit, and local Safari 26.6.2 coverage verifies exact host counts, rendered scripture content, and one shared stylesheet object across roots and remounts. A production-build comparison found a small warm-run mount-cost difference on one machine. | No shared-host blocker found | Repeat user-visible performance and compatibility checks for each component selected for rollout. |

Actual Safari 26.6.2 exposed a visual gap the initial focused assertions missed:
Expand Down Expand Up @@ -92,20 +100,21 @@ input padding while the host button remains overridden.
No other production direct-overlay bypass was found. The inventory therefore
produced no equivalent low-risk migration and no materially different case that
requires follow-up work. Extending the controller already owned by
`ShadowRootHost` is a candidate seam for added overlay coordination, not an ADR
decision. YPE-5356 owns whether and how to implement that coordination.
`ShadowRootHost` remains a possible seam if a product journey later requires
concurrent peer popovers. YPE-5356 does not require that speculative
coordination for the coordinated rollout.

## Blocking production-readiness decisions
## Production-readiness decisions

- Decide whether isolation is enabled per component instance, per public export,
or package-wide.
- Apply automatic isolation at the SDK-owned top-level component boundary.
Compound members and composed SDK children reuse the owning boundary rather
than creating accidental nested roots.
- Apply [ADR 0007's client-only SSR and hydration contract](adr/0007-prototype-shadow-dom-style-isolation.md#ssr-and-hydration-contract)
per rollout component. YPE-5356 decides whether its first-paint, layout, and
no-JavaScript limitations are acceptable for that component.
- Resolve the YPE-5355 peer-dismissal gap before shipping concurrent peer
overlays (YPE-5356). The decision must consider trigger-time peer dismissal
and overlay order; ADR 0007 records the gap but does not select a coordination design.
Recurring actual-Safari and assistive-technology coverage still remain.
per rollout group. Each implementation ticket reviews whether its first-paint,
layout, and no-JavaScript limitations are acceptable for that component.
- Accept single-active-peer popover dismissal. Concurrent peer overlays require
a demonstrated product journey and a separate design. Recurring actual-Safari
and assistive-technology coverage still remain.
- Keep the YPE-5400 custom-property contract and compiled-stylesheet prevention
guard green as component styles change. The audit below closes the known
ambient dependency; `all: initial` still does not reset custom properties.
Expand Down Expand Up @@ -209,27 +218,17 @@ separately in YPE-5749.
## Research handoff and completion gate

YPE-5356 is the convergence point for the Shadow DOM research. Its foundational
evidence comes from YPE-5298, YPE-5310, YPE-5352, and YPE-5353. It must not be
completed until the final findings from YPE-5354, YPE-5355, YPE-5400, YPE-5436,
and [YPE-5437](ype-5437-shadow-dom-realistic-usage.md) have been reconciled into
the rollout policy and these durable Shadow DOM documents. Any conflicts and
accepted limitations must be recorded rather than left implicit.
evidence comes from YPE-5298, YPE-5310, YPE-5352, and YPE-5353. The final
findings from YPE-5354, YPE-5355, YPE-5400, YPE-5436,
[YPE-5437](ype-5437-shadow-dom-realistic-usage.md), and YPE-5946 are reconciled
into the rollout policy and these durable Shadow DOM documents. Conflicts and
accepted limitations are recorded rather than left implicit.

Every component rollout ticket produced by YPE-5356 must link back to that
policy and repeat the compatibility matrix for its selected component. Its gates
must cover browser and assistive-technology behavior, customization,
performance, and stylesheet failure recovery in addition to the component's
forms, labels, ARIA relationships, events, refs, queries, and overlays.

## Rollout sequence

1. Maintain YPE-5400's completed custom-property inventory and prevention guard.
2. Reconcile YPE-5354's SSR/hydration decision, YPE-5355's overlay findings,
YPE-5436's consumer contract, and YPE-5437's realistic-usage result in
YPE-5356.
3. Select the next public component and add component-specific compatibility,
browser, and accessibility coverage before enabling isolation.
4. Publish consumer guidance for DOM queries, automation, customization, forms,
accessibility, and the loss of global CSS styling.
5. Repeat the validation matrix for each component rather than assuming that the
infrastructure proof covers its component-specific behavior.
policy and apply the compatibility matrix to its selected component. Its gates
cover browser and assistive-technology claims, customization, performance, and
stylesheet failure recovery in addition to the component's forms, labels, ARIA
relationships, events, refs, queries, and overlays.

The dependency-ordered rollout sequence and release gate are defined in the
[production rollout policy](shadow-dom-rollout-policy.md#implementation-plan).
Loading
Loading