Skip to content

Fix home-page SSR->CSR flicker#1287

Merged
milanmajchrak merged 5 commits into
dtq-devfrom
internal/fe-fix-home-page-flicker
Jul 2, 2026
Merged

Fix home-page SSR->CSR flicker#1287
milanmajchrak merged 5 commits into
dtq-devfrom
internal/fe-fix-home-page-flicker

Conversation

@vidiecan

@vidiecan vidiecan commented May 19, 2026

Copy link
Copy Markdown

Summary

Eliminates the visible flicker on /home (reproducible on http://dev-5.pc:84/home; CLS 0.89 measured at t≈1.76s) that comes from Angular 15's lack of provideClientHydration — the whole SSR DOM is torn down and rebuilt on every browser load.

Two compounding causes, both addressed:

  1. CustomEagerThemeModule was commented out in src/themes/eager-themes.module.ts, so every custom-themed wrapper (footer, header, root, ...) was lazy-loaded via webpack code-splitting on the browser — stretching the gap. Re-enabled (the existing custom/eager-theme.module.ts already declares the right set). Bumps initial bundle by ~256 KB, so angular.json budget raised 5 MB → 8 MB.

  2. The fundamental no-hydration rebuild is masked by an inline pre-bootstrap script in src/index.html that:

    • Captures all <style ng-transition="dspace-angular"> blocks into <style data-dspace-ssr-keep> tags Angular won't strip (without this the moved SSR DOM renders unstyled — the original styles are wiped on bootstrap).
    • Moves (not clones) the SSR-rendered <ds-app> children into an absolute-positioned overlay so they keep every live DOM/style detail.
    • Hides the now-empty <ds-app> via a data-dspace-ssr-hidden attribute + CSS rule.
    • Exposes window.__dspaceRemoveSsrOverlay() for AppComponent.removeSsrOverlayWhenStable() to call once ApplicationRef.isStable fires (one rAF + 50 ms pad, then 150 ms fade).
    • 15 s safety fallback in case isStable never fires.

Bots / no-JS clients still get the original SSR <ds-app> (the overlay is JS-added). Real users see continuous SSR-rendered content while CSR rebuilds invisibly underneath, then a 150 ms fade reveals the CSR DOM in its final data-loaded state.

Files

  • src/themes/eager-themes.module.ts — enable CustomEagerThemeModule
  • src/index.html — inline overlay script + supporting <style>
  • src/app/app.component.tsremoveSsrOverlayWhenStable() hook
  • angular.json — bundle budget bump
  • scripts/dspace-deploy.bat + .claude/skills/dspace-deploy/SKILL.md — local dev helper (cmd, no PowerShell), multi-instance safe via the existing compose files
  • .gitignore — ignore the per-instance docker/.env.dspace-* / docker/.override.dspace-*.yml files the helper generates

Test plan

  • CI build passes
  • Local: scripts\dspace-deploy.bat 7 rebuild brings up FE+BE+DB+Solr; http://localhost:4007/home renders fully styled with no white flash on reload
  • Verify in browser DevTools that document.getElementById('__dspace_ssr_overlay') exists during boot and is removed after the app stabilises
  • Confirm view-source: of /home shows the inline <style id="__dspace-ssr-overlay-style"> and the bootstrap script — the page works without JS (overlay is JS-added, so bots / no-JS clients see plain SSR'd <ds-app>)
  • No regression on excluded SSR routes (/search, /browse/*, /admin/* etc) — the script early-returns when <ds-app> has no children

Summary by CodeRabbit

  • New Features

    • Added a smoother page-load experience by masking server-rendered content until the app is ready, reducing visible flicker during startup.
    • Improved handling for the custom theme so it loads more consistently at first render.
  • Bug Fixes

    • Prevented the initial overlay from lingering if the normal removal flow fails.
    • Skips the masking behavior in automation environments to avoid interfering with tests.
  • Tests

    • Added coverage for overlay removal timing and fallback behavior.

Angular 15 has no provideClientHydration; on every browser load Angular
tears down the entire SSR DOM and rebuilds the component tree from scratch.
Measured CLS = 0.89 at t=1.76s on /home (PerformanceObserver on dev-5).
The visible flicker is that ~600ms rebuild window between SSR view and
populated CSR view.

Two compounding causes, addressed in this PR:

1. CustomEagerThemeModule was commented out in src/themes/eager-themes.module.ts,
   so every custom-themed wrapper (footer, header, root, ...) was lazy-loaded via
   webpack code-splitting on the browser, stretching the gap. Re-enable it
   (the existing custom/eager-theme.module.ts already declares the right set).
   Bumps initial bundle by ~256KB; angular.json budget raised from 5MB to 8MB
   to accommodate.

2. The bigger cause - no hydration - is masked by an inline pre-bootstrap script
   in src/index.html that:
     - Captures all <style ng-transition="dspace-angular"> blocks into
       <style data-dspace-ssr-keep> tags Angular won't strip (Angular removes
       the originals on bootstrap, which is why a naive overlay renders
       unstyled).
     - Moves (not clones) the SSR-rendered <ds-app> children into an
       absolute-positioned overlay so they keep every live DOM/style detail.
     - Hides the now-empty <ds-app> via a data-attribute and CSS rule.
     - Exposes window.__dspaceRemoveSsrOverlay() for AppComponent to call
       once ApplicationRef.isStable fires (with one rAF + 50ms pad).
     - 15s safety fallback in case isStable never fires.

Bots and no-JS users still get the original SSR <ds-app> (the overlay is
JS-added). Real users see continuous SSR-rendered content while CSR rebuilds
invisibly underneath, then a 150ms fade reveals the CSR DOM in its final
data-loaded state.

Verified locally via Service Worker that suppresses the removal: overlay's
header height is 80px (proper styling preserved) versus 698px (the unstyled
fallback before this fix's style-preservation step).

Includes a small Windows cmd deploy helper at scripts/dspace-deploy.bat and
matching skill doc at .claude/skills/dspace-deploy/SKILL.md - multi-instance
safe local dev stack via the existing docker compose files.
Copilot AI review requested due to automatic review settings May 19, 2026 00:24

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Eliminates the visible SSR→CSR flicker on /home on this Angular 15 (pre-provideClientHydration) codebase. Achieved by (a) re-enabling the previously commented-out CustomEagerThemeModule so themed wrappers stop being lazy-chunked, and (b) installing an inline pre-bootstrap overlay in index.html that moves the SSR DOM into an absolute-positioned snapshot (with copies of <style ng-transition> blocks Angular would otherwise strip) while Angular invisibly rebuilds the real <ds-app>, then fades the overlay out from AppComponent when ApplicationRef.isStable settles. Adds a Windows-only multi-instance dev helper and bumps the production bundle budget to accommodate the eager-theme increase.

Changes:

  • Re-enable CustomEagerThemeModule in src/themes/eager-themes.module.ts and raise the initial bundle budget in angular.json (5MB → 8MB).
  • Add an inline SSR-mask overlay script + supporting CSS in src/index.html, and a matching removeSsrOverlayWhenStable() hook in AppComponent that runs outside Angular and waits on appRef.isStable.
  • Introduce a Windows cmd dev-helper (scripts/dspace-deploy.bat + .claude/skills/dspace-deploy/SKILL.md) and ignore its generated artifacts (plus various local investigation files) in .gitignore.

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/themes/eager-themes.module.ts Re-enables CustomEagerThemeModule so custom-theme wrappers ship in the main bundle instead of lazy chunks.
src/index.html Adds inline overlay CSS + pre-bootstrap script that snapshots SSR DOM + ng-transition styles and exposes __dspaceRemoveSsrOverlay.
src/app/app.component.ts Adds removeSsrOverlayWhenStable() that, after appRef.isStable, invokes the global overlay-removal callback.
angular.json Raises initial bundle budget warning to 6MB and error to 8MB.
scripts/dspace-deploy.bat New Windows cmd helper that generates env/override files and brings up a namespaced FE+BE+DB+Solr stack per INSTANCE digit.
.claude/skills/dspace-deploy/SKILL.md Documents the deploy helper, URL conventions, and multi-instance contract.
.gitignore Ignores per-instance docker/.env/override files and various local investigation artifacts.

Comment thread angular.json Outdated
Comment thread src/index.html
Comment thread src/index.html
Comment thread src/index.html
Comment thread src/app/app.component.ts Outdated
Comment thread .claude/skills/dspace-deploy/SKILL.md Outdated
Comment thread src/app/app.component.ts Outdated
jm added 2 commits May 19, 2026 02:31
- angular.json: tighten budget back to 5.5MB warn / 6MB error (was 8MB)
- index.html: re-entrancy guard on __dspaceRemoveSsrOverlay (null the
  pointer up-front so the isStable + 15s safety fallback can't double-fade)
- index.html: drop aria-hidden from overlay so screen-reader users get the
  SSR snapshot during boot (ds-app underneath has visibility:hidden which
  already excludes it from a11y tree)
- index.html: console.warn on the overlay-script catch so a silently broken
  flicker fix is at least diagnosable in DevTools
- typings.d.ts: typed Window.__dspaceRemoveSsrOverlay augmentation; drop
  the `as any` cast in AppComponent.removeSsrOverlayWhenStable
- app.component.spec.ts: cover removeSsrOverlayWhenStable (calls the
  global once on isStable=true; no-op when global absent)
- Drop scripts/dspace-deploy.bat + .claude/skills/dspace-deploy/SKILL.md
  from this PR per request (local dev tooling, will live elsewhere)
@vidiecan

Copy link
Copy Markdown
Author

Third take — apologies, the previous two were apples-to-apples but the wrong apples.

Both prior clips used the same FE bundle (this PR's): the BEFORE just had the overlay-installer script stripped at the edge. That isolates Fix B but leaves Fix A (the re-enabled CustomEagerThemeModule) in place — so the only "extra" gap was the lazy-import delay, which on a fast host is sub-100 ms and invisible.

This take is the real-world comparison:

  • BEFORE = http://dev-5.pc:84/home — what production looks like today. No CustomEagerThemeModule import, no overlay. CLS 0.89 at t≈1.76 s in the perf trace.
  • AFTER = http://localhost:4007/home — this PR's FE bundle. Both Fix A and Fix B applied.
  • Both throttled to 400 KB/s + 100 ms RTT (Chrome DevTools Network.emulateNetworkConditions via CDP) — same throttle for both, so any difference is the fix.

flicker-before-after

MP4 (better quality): https://raw.githubusercontent.com/dataquest-dev/dspace-angular/internal/fe-fix-home-page-flicker-evidence/flicker-comparison.mp4?v=3

On the left (BEFORE) you'll see: SSR'd LINDAT page paints → ~600 ms gap where the page goes to a stripped/half-built state as Angular tears down <ds-app> → final populated CSR repaints. On the right (AFTER): continuous content, the overlay masks the rebuild and 150 ms-fades when ApplicationRef.isStable fires.

Use ?v=3 on the URL above to dodge the CDN cache (was serving the old 526 KB GIF for me, the new one is 611 KB).

The overlay holds the SSR-rendered children alongside <ds-app>'s CSR-rendered
children during the masking window. Cypress's cy.get(selector) sees both
copies, so unique-id selectors return 2 elements and cy.click() fails. The
overlay is purely a UX smoothing layer (no behaviour to E2E-validate), so
short-circuit when window.Cypress is present. Browser users are unaffected.
@milanmajchrak

Copy link
Copy Markdown
Collaborator

PR Review Summary: Home Page SSR to CSR Flicker Fix

Problem This PR Solves

This PR fixes a visible flicker on the home page during app startup.

In simple terms, the page is rendered twice:

  1. The server sends a ready-looking page (SSR), so users see content fast.
  2. Angular in the browser then rebuilds that page (CSR) to make it fully interactive.
  3. During that rebuild, users can briefly see a bad transition:
    • complete page
    • blank or partially rebuilt page
    • complete page again

That visual jump is the issue this PR addresses.

How the PR Fixes It

The fix uses a temporary overlay to hide the rebuild phase.

  1. Early startup script creates an overlay from the already rendered SSR content.
  2. The real app container is hidden while Angular rebuilds in the background.
  3. Once Angular reports the app is stable, the overlay is removed.
  4. Removal happens with a short fade-out so the transition feels smooth.

Result: users keep seeing a stable page instead of a flicker.

Why the Timeouts Are There

The timeouts are intentional and part of the UX fix:

  • Short delay after stability signal:
    ensures the first stable paint is fully committed before removing the overlay.
  • Short delay for fade-out cleanup:
    allows the opacity transition to finish before removing elements.
  • Long fallback timeout:
    guarantees the overlay is removed even if the stable signal never arrives.

These are not test-only hacks. They are defensive UI timing controls to avoid flicker and prevent overlay lock-in.

Verdict

The approach is technically valid for Angular 15 environments where full hydration is not available.

The timeouts are justified and expected in this type of anti-flicker strategy.

vidiecan pushed a commit that referenced this pull request Jun 10, 2026
Five small changes that make `docker/docker-compose.yml` + `docker-compose-rest.yml`
the same artifact for the pyinfra-driven production deploy AND for a developer
running the stack on their own machine. The prior gap was that compose's defaults
encoded production-only assumptions; local-dev had to override them in a separate
file, which was the friction this fixes.

Defaults preserve every existing production behaviour bit-for-bit (verified with
`docker compose -f ... -f ... config` against an INSTANCE-only env: same subnet,
same proxies trusted range, same host_ip 127.0.0.1, same dspace.ui.url default,
same CORS default that already matched dspace.ui.url implicitly).

Changes:

1. Dockerfile: also copy `docker/dspace-ui.json` to `/app/dspace-ui.json` so the
   locally-built image matches Dockerfile.dist's layout. Compose's hardcoded
   `pm2-runtime start dspace-ui.json` (no `docker/` prefix) was ENOENT-looping
   on locally-built containers. The Dockerfile CMD now matches compose's
   entrypoint too.

2. docker-compose.yml: add `extra_hosts: host.docker.internal:host-gateway` to
   the FE service. No-op on production (BE is reached via dev-5.pc DNS, not
   host.docker.internal). On local dev it lets the FE container's SSR reach a
   BE port published on the host machine.

3. docker-compose-rest.yml: parameterise the dspacenet subnet via
   `${DSPACE_SUBNET_PREFIX:-172.2${INSTANCE}}.0.0/16`. Default unchanged.
   Local dev can move off the crowded 172.2X range with a one-line env override.
   `proxies.trusted.ipranges` follows the same prefix automatically.

4. docker-compose-rest.yml: add explicit
   `rest__P__cors__P__allowed__D__origins: ${REST_CORS_ALLOWED_ORIGINS:-${UI_URL:-...}}`
   to the dspace service env. Default value matches the implicit
   `${dspace.ui.url}` fallback that `dspace/config/modules/rest.cfg` already
   computes, so production CORS is byte-identical. Local dev can extend with
   multiple browser origins (localhost + host.docker.internal + LAN IP) so the
   preflight succeeds regardless of which hostname resolves the BE first.

5. New `docker/.env.local.example`: documented starter for local-dev. Sets the
   four env vars above (HOST_IP=0.0.0.0, DSPACE_HOST=host.docker.internal,
   DSPACE_SUBNET_PREFIX, REST_CORS_ALLOWED_ORIGINS) plus image set and INSTANCE.
   Production deploys do not use this file — pyinfra still templates its own
   .env from devops/infra/pyinfra/assets/dspace/dspace-envs/v7/.env.j2.

Background: I wrote the diagnosis up in detail when investigating the home-page
flicker (separate PR #1287). The death-by-a-thousand-cuts of unstated assumptions
in the compose files is what made starting the stack locally a slog. This PR
removes the slog without touching the production deploy path.
jr-rk added a commit that referenced this pull request Jun 20, 2026
…ckports

Aligns ZCU-PUB's initial budget with the value the root fix (#1287) and the
other customer backports use, so the budget block is identical across
instances. The custom theme is already eager here, so this only widens the
headroom; the build already passes under the previous 5mb error ceiling.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…1318, #1321)

The original overlay removal waited for ApplicationRef.isStable, which is held
hostage by post-login/admin zone activity (page looks rendered but is not
interactive - dspace-customers#725). Replace it with the final mechanism used
across the customer backports: keep the SSR snapshot until the routed <ds-app>
DOM has settled (MutationObserver + quiet window, content-height / #main-content
check, 10s cap), and make the overlay a purely visual mask so the live app stays
interactive underneath. index.html / app.component.ts / spec / typings are synced
to the final version; the existing ngAfterViewInit delay(0) is preserved.

Keeping the trunk on the final mechanism means new customer branches cut from
dtq-dev start correct instead of re-inheriting the old isStable approach.

Ref: #1318, #1321

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR introduces an SSR anti-flicker overlay: an inline script in index.html masks SSR content and exposes a global removal hook, typed in typings.d.ts, invoked by new AppComponent logic that detects DOM settle and auth/theme readiness before removing the overlay. It also increases build budgets, enables a custom eager theme module, adds tests, and updates .gitignore.

Changes

SSR Overlay Feature

Layer / File(s) Summary
Global overlay hook typing
src/typings.d.ts
Adds __dspaceRemoveSsrOverlay?: (() => void) | null to the global Window interface.
Inline SSR overlay bootstrap
src/index.html
Adds overlay CSS, an inline script that clones SSR content into a masking overlay, marks ds-app hidden via a data attribute, defines window.__dspaceRemoveSsrOverlay with fade-out removal and a 15s fallback, and wraps setup in try/catch.
AppComponent reveal and DOM-settle pipeline
src/app/app.component.ts
Adds NgZone injection, destroyed$ teardown, and new methods removeSsrOverlayWhenContentVisible, routedPageReadyToReveal$, dsAppDomSettled$, plus mutation/content helpers and ngOnDestroy.
Overlay removal tests
src/app/app.component.spec.ts
Adds a test suite validating overlay removal timing against auth/theme readiness and DOM quiet windows, plus a no-op case when the global hook is missing.

Estimated code review effort: 4 (Complex) | ~60 minutes

Unrelated build/config/theme updates

Layer / File(s) Summary
Ignore rules
.gitignore
Adds ignore patterns for local dspace-deploy.bat artifacts and Playwright MCP capture outputs.
Production bundle budgets
angular.json
Increases maximumWarning to 5.5mb and maximumError to 6mb.
Custom eager theme enablement
src/themes/eager-themes.module.ts
Enables the previously commented-out CustomEagerThemeModule import and adds it to NgModule imports, with an explanatory comment.

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant IndexHtmlScript
  participant AppComponent
  participant Store as NgRx Store/ThemeService

  Browser->>IndexHtmlScript: load page, run inline script
  IndexHtmlScript->>IndexHtmlScript: clone SSR ds-app content into overlay
  IndexHtmlScript->>Browser: mark ds-app data-dspace-ssr-hidden, define window.__dspaceRemoveSsrOverlay
  AppComponent->>Store: subscribe to auth.blocking and isThemeLoading$
  Store-->>AppComponent: emit readiness state
  AppComponent->>AppComponent: dsAppDomSettled$ observes MutationObserver quiet window
  AppComponent->>AppComponent: routedPageReadyToReveal$ combines readiness + DOM settle
  AppComponent->>Browser: call window.__dspaceRemoveSsrOverlay() after next frame
  Browser->>Browser: fade out and remove overlay + kept styles
Loading

Estimated code review effort: 4 (Complex) | ~60 minutes

Suggested labels: enhancement, needs-review

Suggested reviewers: dataquest-dev/dspace-angular maintainers familiar with SSR and AppComponent lifecycle

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the main user-facing change: fixing the home-page SSR-to-CSR flicker.
Description check ✅ Passed The description covers the problem, implementation summary, and test plan, though it doesn't follow every template heading exactly.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/app/app.component.ts`:
- Around line 218-221: Add a short TypeDoc/JSDoc comment for the new public
lifecycle method ngOnDestroy in AppComponent. Update the method definition to
include a concise description of its cleanup responsibility, matching the
existing documentation style used in app.component.ts for public members.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 07cf77ff-fb89-4a99-b803-593edd3d347c

📥 Commits

Reviewing files that changed from the base of the PR and between cb7e1bb and 22368a8.

📒 Files selected for processing (7)
  • .gitignore
  • angular.json
  • src/app/app.component.spec.ts
  • src/app/app.component.ts
  • src/index.html
  • src/themes/eager-themes.module.ts
  • src/typings.d.ts

Comment thread src/app/app.component.ts
milanmajchrak pushed a commit that referenced this pull request Jul 2, 2026
* Backport of Fix home-page SSR->CSR flicker

* Fix: always unhide app when removing SSR anti-flicker overlay

The overlay remover bailed out via `if (!el) return;` before unhiding
<ds-app>, so if the overlay node went missing (browser extension, race,
external script) the app stayed visibility:hidden forever -> blank page,
plus the kept SSR styles leaked. Unhide the app and clean up the kept
styles unconditionally, before checking for the overlay node.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Chore: drop accidentally committed build/spec logs

_build.log and _spec.log are local deploy-tooling output that should
never have been tracked. Remove them and gitignore /_*.log.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Test: isolate isStable override and cover the no-rAF overlay path

Make the ApplicationRef.isStable override in the removeSsrOverlayWhenStable
suite configurable and restore the original descriptor in afterEach, so the
patched observable can't leak onto the shared TestBed instance. Add a test
for the requestAnimationFrame-absent fallback branch of the remover.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Refactor: align eager-themes.module.ts with the other backport instances

Bind the custom eager theme to the CustomEagerThemeModule alias used by the
other customer backports so this file is byte-identical across instances.
The same ./custom/eager-theme.module is still imported eagerly - no runtime,
build, or bundle-size change; the custom theme stays eager (which also keeps
the untyped-item theming working, ref DSpace#1897).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Build: raise initial bundle budget to 5.5mb/6mb to match the other backports

Aligns ZCU-PUB's initial budget with the value the root fix (#1287) and the
other customer backports use, so the budget block is identical across
instances. The custom theme is already eager here, so this only widens the
headroom; the build already passes under the previous 5mb error ceiling.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Refactor: remove SSR overlay on content-visible instead of isStable

Propagates VSB-TUO's fix #1317 to this instance. The overlay was removed when
ApplicationRef.isStable settled, but isStable can be delayed for seconds by
post-login admin zone activity (auth work, background polling, third-party
scripts) - during which the live app stays hidden under the SSR mask and the
page renders but is non-interactive (dataquest-dev/dspace-customers#725).

Switch removal to the same condition root.component.html uses to show real
content: !isAuthenticationBlocking && !isThemeLoading. Drop the now-unused
ApplicationRef injection and the 50ms pad; keep the 15s hard fallback as a
catastrophic safety net. Tests and the theme-service mock updated to match.

Ref: #1317

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Backport final SSR-overlay mechanism from VSB-TUO (#1318, #1321)

Supersedes the #1317 content-visible trigger backported earlier. That gate
(!isAuthenticationBlocking && !isThemeLoading) still revealed a half-built page
on hard reload, so VSB-TUO's #1318/#1321 keep the snapshot until the routed
<ds-app> DOM has SETTLED (MutationObserver + quiet window, with a content
height / #main-content check and a 10s cap). The overlay is now a purely
visual mask, so the live app stays interactive underneath while it rebuilds
(closes dspace-customers#725 - "looks rendered but not clickable").

index.html, app.component.ts, spec and typings are synced to VSB-TUO's final
version; the VSB-only ngAfterViewInit delay(0) is omitted (these instances
don't carry it).

Ref: #1318, #1321

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@milanmajchrak milanmajchrak merged commit 5c9c6bc into dtq-dev Jul 2, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants