Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
69841bf
feat(ui): prototype automatic Shadow DOM style isolation (#347)
cameronapak Aug 19, 2026
a794931
Merge branch 'main' into journey-to-the-shadow-dom
aharms24 Aug 24, 2026
3c25b80
feat(ui): prototype shadow-local top-layer popovers (#357)
abharms Aug 25, 2026
e8bad1b
feat(ui): validate shadow DOM dialog top layer (#362)
abharms Aug 27, 2026
a161758
Merge branch 'main' into journey-to-the-shadow-dom
aharms24 Aug 28, 2026
bc7fc0e
Merge branch 'journey-to-the-shadow-dom' of https://github.com/youver…
aharms24 Aug 28, 2026
8b04908
fix(ui): harden Shadow DOM CSS isolation (YPE-5352) (#367)
abharms Aug 31, 2026
c764982
fix(ui): route verse actions through shadow portals (#369)
abharms Sep 1, 2026
19c8a0b
Merge branch 'main' into journey-to-the-shadow-dom
aharms24 Sep 2, 2026
3dec29a
Merge remote-tracking branch 'origin/journey-to-the-shadow-dom' into …
aharms24 Sep 2, 2026
7504630
test(ui): validate Shadow DOM consumer compatibility (YPE-5436) (#380)
abharms Sep 9, 2026
0222ae9
test(ui): prove nested overlays through the production shadow seam (#…
cameronapak Sep 9, 2026
dae9a49
fix(ui): address shadow DOM review regressions (YPE-5713) (#388)
abharms Sep 10, 2026
0e595c3
Merge branch 'main' into journey-to-the-shadow-dom
aharms24 Sep 10, 2026
2daf44d
fix(ui): enforce Shadow DOM custom property contract (YPE-5400) (#394)
abharms Sep 14, 2026
e91afcc
test(ui): prove Shadow DOM SSR hydration contract (#399)
abharms Sep 14, 2026
8c240ed
test(ui): validate realistic Shadow DOM usage (YPE-5437) (#408)
abharms Sep 16, 2026
358839f
Merge branch 'main' into journey-to-the-shadow-dom
ampagent Sep 17, 2026
1344034
fix(ci): unblock journey pull requests (#418)
abharms Sep 21, 2026
4b05279
fix(ui): restore shadow dialog focus after rapid reopen (YPE-5889) (#…
abharms Sep 21, 2026
b9c21f3
test(ui): consolidate Storybook DOM waits (#415)
abharms Sep 21, 2026
baa9b30
test(ui): add cross-browser Shadow DOM coverage (#420)
abharms Sep 24, 2026
7068c41
docs(ui): define Shadow DOM rollout policy (#423)
abharms Sep 25, 2026
1201b45
feat(ui): harden Shadow DOM rollout foundation (YPE-5947) (#424)
abharms Sep 26, 2026
7b3dbe2
Merge remote-tracking branch 'origin/main' into journey-to-the-shadow…
abharms Sep 26, 2026
4fbd7db
test(ui): keep auth shadow query type-safe
abharms Sep 26, 2026
a75cf71
feat(ui): isolate Bible pickers in Shadow DOM (#426)
abharms Sep 28, 2026
311649c
feat(ui): isolate leaf components (YPE-5948) (#427)
abharms Sep 29, 2026
33d8227
test(ui): catch shadow stylesheet cache poisoning (#429)
abharms Sep 30, 2026
44ee7e5
feat(ui): preserve standalone theme settings layout (YPE-5994) (#428)
abharms Oct 1, 2026
8e53dd9
Merge branch 'main' into journey-to-the-shadow-dom
abharms Oct 1, 2026
542e17f
feat(ui): isolate scripture presentation components (YPE-5950) (#431)
abharms Oct 5, 2026
c037af9
feat(ui): isolate Bible reader (YPE-5951) (#433)
abharms Oct 7, 2026
61fb7f5
Merge branch 'main' into journey-to-the-shadow-dom
abharms Oct 8, 2026
caaf04f
fix(ui): deduplicate shadow ancestor events (#439)
abharms Oct 8, 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/all-humans-leave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
2 changes: 2 additions & 0 deletions .changeset/bitter-pigs-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
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 @@
---
---
5 changes: 5 additions & 0 deletions .changeset/calm-browsers-test.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@youversion/platform-react-ui': patch
---

Preserve SDK spacing in Safari inside Shadow DOM and document roots by scoping the host CSS reset and inlining the fixed Tailwind spacing scale.
2 changes: 2 additions & 0 deletions .changeset/cool-settings-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
2 changes: 2 additions & 0 deletions .changeset/flat-shadows-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
5 changes: 5 additions & 0 deletions .changeset/friendly-verses-scroll.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@youversion/platform-react-ui': patch
---

Keep verse actions open when touch selection moves to another verse, restore focus for initially anchored or conditionally mounted verse actions, and keep reader settings scrollable in constrained popovers. Clarify the Shadow DOM prototype's document-owned font and root-sizing dependencies without expanding its rollout scope.
2 changes: 2 additions & 0 deletions .changeset/full-flies-worry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
5 changes: 5 additions & 0 deletions .changeset/fuzzy-spiders-check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@youversion/platform-react-ui': patch
---

Audit the compiled Shadow DOM stylesheet custom-property contract and fail builds on unexplained ambient references.
2 changes: 2 additions & 0 deletions .changeset/leaf-shadows-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
2 changes: 2 additions & 0 deletions .changeset/little-fans-burn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
7 changes: 7 additions & 0 deletions .changeset/prototype-shadow-dom-isolation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@youversion/platform-core': major
'@youversion/platform-react-hooks': major
'@youversion/platform-react-ui': major
---

Prototype automatic Shadow DOM style isolation on `YouVersionAuthButton`, plus a shadow-local native top-layer strategy for isolated Popovers. The spike also constrains Popovers to Radix's available collision height.
4 changes: 4 additions & 0 deletions .changeset/prove-production-shadow-overlays.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
---

Prove nested and concurrent overlays through the production ShadowRootHost seam (YPE-5355).
5 changes: 5 additions & 0 deletions .changeset/quiet-dialogs-return.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@youversion/platform-react-ui': patch
---

Fix focus restoration for opt-in Shadow DOM dialogs by preserving the original opener across a close/reopen during exit, while ignoring disconnected targets and targets moved out of their captured root.
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 @@
---
---
5 changes: 5 additions & 0 deletions .changeset/safe-shadows-fallback.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@youversion/platform-react-ui': patch
---

Keep isolated UI content rendered with local styles when constructable stylesheet setup fails.
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 @@
---
---
2 changes: 2 additions & 0 deletions .changeset/scripture-shadows-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
5 changes: 5 additions & 0 deletions .changeset/shadow-aware-verse-actions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@youversion/platform-react-ui': patch
---

Keep verse action popovers in their component's shadow-local portal and restore focus after dismissal.
2 changes: 2 additions & 0 deletions .changeset/six-shrimps-stare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
2 changes: 2 additions & 0 deletions .changeset/tidy-shadow-events.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
5 changes: 5 additions & 0 deletions .changeset/tidy-shadows-reset.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@youversion/platform-react-ui': patch
---

Harden Shadow DOM style isolation so text direction is the only intentionally inherited CSS property and host custom properties cannot alter known SDK spacing or radius values. Document-root font sizing still affects the prototype's rem-based dimensions.
5 changes: 5 additions & 0 deletions .changeset/validate-shadow-dialog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@youversion/platform-react-ui': patch
---

Validate shadow-local top-layer Dialog behavior, including focus containment and modal background inertness.
150 changes: 149 additions & 1 deletion .github/scripts/major-release-signoff.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,58 @@ extract_step() {
}

extract_step "Resolve PR context" > "$TMP/context.sh"
extract_step "Restore release tooling from the base branch" > "$TMP/restore-tooling.sh"
extract_step "Compute release preview" > "$TMP/preview.sh"
extract_step "Decide whether a signoff is required" > "$TMP/decision.sh"
extract_step "Regenerate and verify release contents" > "$TMP/verify.sh"
extract_step "Post an unresolved status" > "$TMP/unresolved.sh"

TOOLING_SOURCE="$TMP/tooling-source"
TOOLING_WORK="$TMP/tooling-work"
git init --quiet "$TOOLING_SOURCE"
git -C "$TOOLING_SOURCE" config commit.gpgsign false
git -C "$TOOLING_SOURCE" config user.name test
git -C "$TOOLING_SOURCE" config user.email test@example.com
mkdir -p "$TOOLING_SOURCE/scripts" "$TOOLING_SOURCE/.changeset" \
"$TOOLING_SOURCE/packages/ui" "$TOOLING_SOURCE/examples/demo" "$TOOLING_SOURCE/tools/config"
for file in scripts/preview-release.mjs package.json pnpm-lock.yaml \
pnpm-workspace.yaml .changeset/config.json .npmrc packages/ui/package.json \
examples/demo/package.json tools/config/package.json; do
printf 'main %s\n' "$file" > "$TOOLING_SOURCE/$file"
done
git -C "$TOOLING_SOURCE" add -A
git -C "$TOOLING_SOURCE" commit --quiet -m main
git -C "$TOOLING_SOURCE" branch -M main
git -C "$TOOLING_SOURCE" switch --quiet -c journey
mkdir -p "$TOOLING_SOURCE/packages/pr-only"
printf 'journey-only importer\n' > "$TOOLING_SOURCE/packages/pr-only/package.json"
for file in scripts/preview-release.mjs package.json pnpm-lock.yaml \
pnpm-workspace.yaml .changeset/config.json packages/ui/package.json; do
printf 'journey %s\n' "$file" > "$TOOLING_SOURCE/$file"
done
rm "$TOOLING_SOURCE/.npmrc"
printf 'journey pnpm hook\n' > "$TOOLING_SOURCE/.pnpmfile.cjs"
git -C "$TOOLING_SOURCE" add -A
git -C "$TOOLING_SOURCE" commit --quiet -m journey
TOOLING_HEAD=$(git -C "$TOOLING_SOURCE" rev-parse HEAD)
git -C "$TOOLING_SOURCE" switch --quiet main
git clone --quiet "$TOOLING_SOURCE" "$TOOLING_WORK"
git -C "$TOOLING_WORK" checkout --quiet "$TOOLING_HEAD"

if (cd "$TOOLING_WORK" && bash "$TMP/restore-tooling.sh") >/dev/null 2>&1 &&
[ "$(git -C "$TOOLING_WORK" rev-parse HEAD)" = "$TOOLING_HEAD" ] &&
[ "$(cat "$TOOLING_WORK/package.json")" = 'main package.json' ] &&
[ "$(cat "$TOOLING_WORK/pnpm-lock.yaml")" = 'main pnpm-lock.yaml' ] &&
[ "$(cat "$TOOLING_WORK/packages/ui/package.json")" = 'main packages/ui/package.json' ] &&
[ "$(cat "$TOOLING_WORK/.npmrc")" = 'main .npmrc' ] &&
[ ! -e "$TOOLING_WORK/packages/pr-only/package.json" ] &&
[ ! -e "$TOOLING_WORK/.pnpmfile.cjs" ]; then
pass "restores the complete release dependency graph from trusted main"
else
fail "restores the complete release dependency graph from trusted main" \
"expected main-owned workspace manifests and install inputs with PR-only importers removed"
fi

mkdir "$TMP/bin"
# The context step backs off between retries; real delays would add ~9s per error case.
cat > "$TMP/bin/sleep" <<'EOF'
Expand Down Expand Up @@ -376,7 +424,7 @@ run_decision_case "failed ordinary previews remain blocked, not major" \
'blocked=release preview did not succeed (failure)' false false skipped '' failure ''

extract_step "Restore release tooling from the base branch" > "$TMP/restore.sh"
PREVIEW_REPO="$TMP/preview-repo"
PREVIEW_REPO="$TMP/restore-preview-repo"
git init --quiet --initial-branch=main "$PREVIEW_REPO"
git -C "$PREVIEW_REPO" config commit.gpgsign false
git -C "$PREVIEW_REPO" config user.name test
Expand Down Expand Up @@ -440,6 +488,106 @@ else
fail "generated release identity always stays out of the ordinary preview" "preview does not use generated release identity"
fi

PREVIEW_REMOTE="$TMP/preview-remote.git"
PREVIEW_REPO="$TMP/preview-repo"
git init --quiet --bare "$PREVIEW_REMOTE"
git init --quiet "$PREVIEW_REPO"
git -C "$PREVIEW_REPO" config commit.gpgsign false
git -C "$PREVIEW_REPO" config user.name test
git -C "$PREVIEW_REPO" config user.email test@example.com
git -C "$PREVIEW_REPO" remote add origin "$PREVIEW_REMOTE"
mkdir -p "$PREVIEW_REPO/scripts" "$PREVIEW_REPO/.changeset" \
"$PREVIEW_REPO/packages/core" "$PREVIEW_REPO/packages/hooks" "$PREVIEW_REPO/packages/ui"
cp "$ROOT/scripts/preview-release.mjs" "$PREVIEW_REPO/scripts/preview-release.mjs"
cp "$ROOT/.changeset/config.json" "$PREVIEW_REPO/.changeset/config.json"
cat > "$PREVIEW_REPO/package.json" <<'EOF'
{"name":"preview-fixture","private":true,"packageManager":"pnpm@11.10.0"}
EOF
cat > "$PREVIEW_REPO/pnpm-workspace.yaml" <<'EOF'
packages:
- "packages/*"
EOF
for package in core hooks ui; do
case "$package" in
core) name='@youversion/platform-core' ;;
hooks) name='@youversion/platform-react-hooks' ;;
ui) name='@youversion/platform-react-ui' ;;
esac
printf '{"name":"%s","version":"1.0.0"}\n' "$name" > "$PREVIEW_REPO/packages/$package/package.json"
done
git -C "$PREVIEW_REPO" add -A
git -C "$PREVIEW_REPO" commit --quiet -m main
git -C "$PREVIEW_REPO" branch -M main
git -C "$PREVIEW_REPO" push --quiet origin main

git -C "$PREVIEW_REPO" switch --quiet -c target
cat > "$PREVIEW_REPO/.changeset/target-major.md" <<'EOF'
---
"@youversion/platform-core": major
---

Major change inherited from the target branch.
EOF
git -C "$PREVIEW_REPO" add -A
git -C "$PREVIEW_REPO" commit --quiet -m 'target major'
git -C "$PREVIEW_REPO" push --quiet origin target
PREVIEW_BASE_SHA=$(git -C "$PREVIEW_REPO" rev-parse HEAD)

git -C "$PREVIEW_REPO" switch --quiet -c pr
printf 'non-major PR change\n' > "$PREVIEW_REPO/README.md"
git -C "$PREVIEW_REPO" add README.md
git -C "$PREVIEW_REPO" commit --quiet -m 'non-major PR change'
git -C "$PREVIEW_REPO" push --quiet origin pr
ln -s "$ROOT/node_modules" "$PREVIEW_REPO/node_modules"
PREVIEW_BIN="$TMP/preview-bin"
mkdir "$PREVIEW_BIN"
cat > "$PREVIEW_BIN/pnpm" <<EOF
#!/usr/bin/env bash
set -euo pipefail
[ "\$1" = exec ] && [ "\$2" = changeset ] && [ "\$3" = status ]
output=\${4#--output=}
printf '%s\n' \
'{"releases":[{"name":"@youversion/platform-core","type":"major","oldVersion":"1.0.0","newVersion":"2.0.0"}]}' \
> "\$output"
EOF
chmod +x "$PREVIEW_BIN/pnpm"

run_preview_case() {
local name="$1" expected_introduced="$2" expected_output="$3"
local output="$TMP/preview-output"
local error="$TMP/preview-error"
local preview="$PREVIEW_REPO/preview.json"
local head_sha
head_sha=$(git -C "$PREVIEW_REPO" rev-parse HEAD)
: > "$output"
rm -f "$preview"
if (cd "$PREVIEW_REPO" && \
PATH="$PREVIEW_BIN:$PATH" BASE_SHA="$PREVIEW_BASE_SHA" HEAD_SHA="$head_sha" \
GITHUB_OUTPUT="$output" \
bash "$TMP/preview.sh") >/dev/null 2> "$error" &&
jq -e ".introduced_major == $expected_introduced" "$preview" >/dev/null &&
grep -Fxq "is_major=$expected_output" "$output"; then
pass "$name"
else
fail "$name" \
"expected introduced_major=$expected_introduced and is_major=$expected_output; preview: $(cat "$preview" 2>/dev/null || true); output: $(tr '\n' ' ' < "$output"); error: $(tr '\n' ' ' < "$error")"
fi
}

run_preview_case "stacked PRs ignore a major inherited from their target branch" false 0

cat > "$PREVIEW_REPO/.changeset/pr-major.md" <<'EOF'
---
"@youversion/platform-react-ui": major
---

Major change introduced by the pull request.
EOF
git -C "$PREVIEW_REPO" add .changeset/pr-major.md
git -C "$PREVIEW_REPO" commit --quiet -m 'PR major'
git -C "$PREVIEW_REPO" push --quiet origin pr
run_preview_case "stacked PRs require signoff for a newly introduced major" true 1

if grep -Fq 'Generated release PR; major signoff is enforced on source PRs.' "$WORKFLOW"; then
pass "generated releases publish an explicit lifecycle-aware success"
else
Expand Down
24 changes: 17 additions & 7 deletions .github/workflows/major-release-signoff.yml
Original file line number Diff line number Diff line change
Expand Up @@ -269,16 +269,24 @@ jobs:

- name: Restore release tooling from the base branch
# The PR supplies changeset *data* only. The detector that reads it, the dependency
# set it runs under, and the changesets config all come from `main`, so a branch
# cannot rewrite the check that gates it. This runs before pnpm is set up, since
# `pnpm/action-setup` reads `packageManager` out of package.json.
# graph it runs under, and the changesets config all come from trusted `main`, so a
# branch cannot rewrite the check that gates it. This runs before pnpm is set up,
# since `pnpm/action-setup` reads `packageManager` out of package.json.
run: |
set -euo pipefail
git fetch --no-tags --force origin main:refs/heads/main
# All of main's tooling, or none of it. The detector, the dependency set it runs
# under, and the changesets config have to agree, so restoring some from main and
# taking the rest from the PR gives a detector without its own dependencies.
if git cat-file -e main:scripts/preview-release.mjs 2>/dev/null; then
# Frozen workspace installs validate every importer. Remove PR-only importers,
# then restore every manifest selected by main's workspace configuration so its
# lockfile and dependency graph remain internally consistent.
git ls-files |
grep -E '^(packages|examples|tools)/[^/]+/package[.]json$' |
while IFS= read -r f; do
git cat-file -e "main:$f" 2>/dev/null || rm -f "$f"
done
for f in scripts/preview-release.mjs package.json pnpm-lock.yaml \
pnpm-workspace.yaml .changeset/config.json; do
git cat-file -e "main:$f" 2>/dev/null && git checkout main -- "$f"
Expand Down Expand Up @@ -318,17 +326,19 @@ jobs:
- name: Compute release preview
id: preview
env:
BASE_SHA: ${{ needs.context.outputs.base_sha }}
HEAD_SHA: ${{ needs.context.outputs.head_sha }}
run: |
set -euo pipefail
# `changeset status` resolves `baseBranch` (main) as a LOCAL ref and fails with
# "Failed to find where HEAD diverged from main" if it is missing. A PR checkout
# has only origin/main, so create the local branch too.
git fetch --no-tags --force origin main:refs/heads/main
# Merge-base, not the PR's base sha: on a synchronize event the base
# branch may have moved, and diffing against its tip would attribute
# someone else's changesets to this PR.
BASE=$(git merge-base main "$HEAD_SHA")
git fetch --no-tags --force origin "$BASE_SHA"
# Compare from the common ancestor of the immutable PR endpoints. This excludes
# major changesets inherited from a stacked PR's base without attributing later
# movement on that base branch to the PR being evaluated.
BASE=$(git merge-base "$BASE_SHA" "$HEAD_SHA")
node scripts/preview-release.mjs --base "$BASE" --head "$HEAD_SHA" > preview.json
cat preview.json
# `introduced_major`, not `is_major`: a major already pending on main
Expand Down
34 changes: 34 additions & 0 deletions .github/workflows/storybook.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,37 @@ jobs:
YVP_APP_KEY: ${{ secrets.YVP_APP_KEY }}
STORYBOOK_YOUVERSION_API_HOST: ${{ secrets.YVP_API_HOST }}
STORYBOOK_YOUVERSION_APP_KEY: ${{ secrets.STORYBOOK_YOUVERSION_APP_KEY }}

shadow-browser-tests:
name: Shadow DOM (${{ matrix.browser }})
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.56.1-noble
strategy:
fail-fast: false
matrix:
browser: [firefox, webkit]
env:
VITEST_BROWSER: ${{ matrix.browser }}
STORYBOOK_YOUVERSION_APP_KEY: ${{ secrets.STORYBOOK_YOUVERSION_APP_KEY }}
STORYBOOK_AUTH_REDIRECT_URL: ${{ secrets.STORYBOOK_AUTH_REDIRECT_URL }}
steps:
- uses: actions/checkout@v5

- name: Setup pnpm
uses: pnpm/action-setup@v4

- uses: actions/setup-node@v6
with:
node-version: 24

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build packages
run: pnpm build

- name: Run Shadow DOM browser tests
run: cd packages/ui && pnpm run test:shadow-browser
env:
HOME: /root
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: '19 KB',
limit: '21.7 KB',
ignore: ['react', 'react-dom', 'react/jsx-runtime', '@tanstack/react-query'],
},
{
Expand Down
Loading