From c5b8a623a9515878b44ec6adbac3670c375ab3c9 Mon Sep 17 00:00:00 2001 From: Austin Date: Mon, 21 Sep 2026 09:10:14 -0500 Subject: [PATCH 1/6] docs(ui): define Shadow DOM rollout policy Reconcile YPE-5356 research into a coordinated package-wide rollout plan and focused implementation tickets. --- .changeset/bright-shadows-plan.md | 2 + docs/shadow-dom-rollout-policy.md | 213 ++++++++++++++++++++++++++++++ 2 files changed, 215 insertions(+) create mode 100644 .changeset/bright-shadows-plan.md create mode 100644 docs/shadow-dom-rollout-policy.md diff --git a/.changeset/bright-shadows-plan.md b/.changeset/bright-shadows-plan.md new file mode 100644 index 00000000..a845151c --- /dev/null +++ b/.changeset/bright-shadows-plan.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/docs/shadow-dom-rollout-policy.md b/docs/shadow-dom-rollout-policy.md new file mode 100644 index 00000000..c6e66c6a --- /dev/null +++ b/docs/shadow-dom-rollout-policy.md @@ -0,0 +1,213 @@ +# Shadow DOM Production Rollout Policy + +## Status and intent + +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. + +The rollout is coordinated at release time, not implemented in one change. +Focused component or component-group tickets may land independently on the +Shadow DOM integration branch, but the package must not release a partial public +boundary. Runtime feature flags and a phased-release framework are unnecessary. + +## Decisions + +- Automatic isolation is applied at an SDK-owned top-level component boundary, + not to every exported React function. Compound members and implementation + children stay in their owning root's tree. +- Public components that compose other included SDK components must reuse their + outer SDK boundary. The implementation must not create accidental nested + roots merely because both public exports support automatic isolation. +- The client-only SSR contract is accepted for the included boundary, subject + to a focused first-paint review in each implementation ticket. A component + that needs server-rendered or no-JavaScript content must be excluded or use a + separately approved host strategy. +- Automatic isolation is a breaking rendered-DOM change and ships in a major + release. React props need not change, but document queries, native event + targets, ref timing, global styling, and cross-tree relationships can change. +- Real assistive-technology validation is deferred. Automated keyboard and DOM + semantics remain required, and release notes must not imply verified screen- + reader behavior. + +## Public component boundary + +The inventory follows `packages/ui/src/components/index.ts`. Types, constants, +and helper functions are not component rollout targets. + +| Public export | Disposition | Automatic boundary | Required implementation or reason | +| --- | --- | --- | --- | +| `YouVersionAuthButton` | Included; already prototyped | The button | Preserve its current ref and event contract and include it in the final release checks. | +| `BibleChapterPicker.Root`, `.Trigger`, `.Content` | Included as one compound component | `Root` only | Keep context, trigger, content, and shadow-local popover in one tree. Audit consumer-supplied trigger children, callbacks, focus, and picker geometry. | +| `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. | +| `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. | +| `VerseOfTheDay` | Included | The card | Audit loading/error states, Web Share and clipboard callbacks, scripture direction, and first-paint geometry. Its internal `BibleTextView` reuses the card boundary. | +| `BibleCard` | Included | The card | Audit loading/error states, optional version picker, footnotes, highlights, sizing, and first paint. Internal picker and scripture components reuse the card boundary. | +| `ProfileAvatar` | Included | The avatar | Audit image loading, fallback labeling, consumer props, ref behavior inherited from Radix, and compact inline layout. | +| `Separator` | Included | The separator | Audit orientation, decorative semantics, consumer props, and flex/grid sizing through a `display: contents` host. | +| `Textarea` | Excluded | None | Native outer-form ownership, serialization, and external `