fix(ssr-overlay): render admin-sidebar gutter on the server so logged-in reload doesn't shift#1333
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
5b91e72 to
75fca50
Compare
…n reload doesn't shift Follow-up to #1321 (the anti-flicker SSR overlay, merged into `customer/vsb-tuo`). ## Problem With the overlay in place, an authenticated user's hard reload still showed the whole page jump right by the admin-sidebar width when the SSR snapshot was removed. (Anonymous users were already smooth — they have no sidebar.) ## Root cause (independent of the overlay; the overlay just makes it a visible "reveal") The `.outer-wrapper` left gutter came from the `@slideSidebarPadding` Angular animation, whose width is read via `cssService.getVariable('--ds-admin-sidebar-*')`. That store is populated only in the browser (`AppComponent.storeCSSVariables` -> `getComputedStyle`); on the server it stays empty, `skipWhile(!val)` blocks forever, and the animation renders `outer-wrapper { padding-left: 0 }`. The browser then resolves the real width (e.g. 55px), so the authenticated page (and the SSR snapshot, which is just that server HTML) jumps right on reveal. ## Fix Drive the gutter from a CSS class (`ds-admin-sidebar-{hidden,unpinned,pinned}`, set from a small `sidebarPaddingState$`) whose `padding-left` is `var(--ds-admin-sidebar-fixed-element-width)` / `--ds-admin-sidebar-total-width`. CSS resolves those custom properties identically on the server (the snapshot) and the browser (the live app), so they always match — no hardcoded pixel width, fully theme-overridable and viewport/media-query aware. The `@slideSidebarPadding` animation (which can't take a `var()` value and is stripped to empty on SSR) is no longer used here; the unused import/registration is dropped from the base and custom-theme root components. Non-admins are unaffected ('hidden' -> 0). The pin/unpin slide is preserved via `transition: padding-left`, but GATED behind a `ds-admin-sidebar-animate` class enabled only after the first browser paint (`gutterTransitionEnabled`). This stops the initial SSR->CSR gutter resolution — which happens behind the overlay — from animating and leaking a 300ms slide right as the overlay is removed (the overlay's settle detector watches DOM mutations, not style changes). Addresses review feedback (3 independent SSR/DSpace reviewers). ## Verification (authenticated admin, real UA, throttled hard reload, local SSR build) - SSR HTML renders `<div class="outer-wrapper ds-admin-sidebar-unpinned">` — a class, no inline px, and no `ds-admin-sidebar-animate` (transition correctly off on the server / initial paint). - snapshot vs settled `#main-content`: desktop 55/55 -> shift 0px; mobile (375px) 55/55 -> shift 0px; the live app gains `ds-admin-sidebar-animate` only after first paint. Refs: dspace-customers#725, #1321 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
75fca50 to
13d73fa
Compare
Review summaryReviewed for SSR correctness, DSpace regressions, and "does it actually solve the flicker / can it break anything". Verdict: correct, no layout regression, solves the reported symptom for the right reason. Why the SSR snapshot matches the live app (the core guarantee):
Review feedback addressed in
Verified (authenticated admin, real UA, throttled hard reload, local SSR build): SSR renders Honest residuals (pre-existing, not introduced here, out of scope):
|
Rebuilds this follow-up on the current customer/vsb-tuo base (which now carries the final overlay mechanism #1318/#1321 and #1333). Drops the duplicate dspace eager-theme import (imported once as DSpaceEagerThemeModule and again, unaliased, as EagerThemeModule) so this file matches the canonical form on TUL/SAV/ZCU-DATA/ ZCU-PUB: dspace eager theme once + custom eager theme once. The earlier blank-page guard from this branch is intentionally dropped: #1321 removed the ds-app visibility:hidden rule (the overlay is now a purely visual mask), so the early-return can no longer hide the app, and the other customers took VSB-TUO's #1321 index.html verbatim. Keeping a guard only here would re-diverge index.html. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…r backports (#1320) Rebuilds this follow-up on the current customer/vsb-tuo base (which now carries the final overlay mechanism #1318/#1321 and #1333). Drops the duplicate dspace eager-theme import (imported once as DSpaceEagerThemeModule and again, unaliased, as EagerThemeModule) so this file matches the canonical form on TUL/SAV/ZCU-DATA/ ZCU-PUB: dspace eager theme once + custom eager theme once. The earlier blank-page guard from this branch is intentionally dropped: #1321 removed the ds-app visibility:hidden rule (the overlay is now a purely visual mask), so the early-return can no longer hide the app, and the other customers took VSB-TUO's #1321 index.html verbatim. Keeping a guard only here would re-diverge index.html. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…n reload shift) (#1355) Backport of #1333 (originally landed on customer/vsb-tuo). Problem: for an authenticated user, a hard reload shifted the whole page right by the admin-sidebar width when the SSR snapshot was removed. The .outer-wrapper left gutter was produced by the @slideSidebarPadding animation, whose width is read from a browser-only CSS-variable store; on the server that store is empty so it renders padding-left:0, then the browser resolves the real width and the page jumps. Fix: drive the gutter from a CSS class (ds-admin-sidebar-{hidden,unpinned,pinned}, set from a small sidebarPaddingState$) whose padding-left resolves from the admin-sidebar width custom properties in CSS. CSS resolves those identically on the server (the SSR snapshot) and the browser (the live app), so there is no shift and no hardcoded width. The pin/unpin slide is preserved via transition:padding-left, gated behind ds-admin-sidebar-animate (enabled only after the first browser paint) so the initial SSR->CSR gutter resolution does not animate. Translated to this branch's root.component (Angular 18 standalone generation, base + themed root components), not a cherry-pick. Refs: dataquest-dev/dspace-customers#717, #1333 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…load shift) (#1354) Backport of #1333 (originally landed on customer/vsb-tuo). Problem: for an authenticated user, a hard reload shifted the whole page right by the admin-sidebar width when the SSR snapshot was removed. The .outer-wrapper left gutter was produced by the @slideSidebarPadding animation, whose width is read from a browser-only CSS-variable store; on the server that store is empty so it renders padding-left:0, then the browser resolves the real width and the page jumps. Fix: drive the gutter from a CSS class (ds-admin-sidebar-{hidden,unpinned,pinned}, set from a small sidebarPaddingState$) whose padding-left resolves from the admin-sidebar width custom properties in CSS. CSS resolves those identically on the server (the SSR snapshot) and the browser (the live app), so there is no shift and no hardcoded width. The pin/unpin slide is preserved via transition:padding-left, gated behind ds-admin-sidebar-animate (enabled only after the first browser paint) so the initial SSR->CSR gutter resolution does not animate. Translated to this branch's root.component (older DSpace 7.5 generation, empty root scss recreated, gutter var --ds-collapsed/total-sidebar-width), not a cherry-pick. Refs: dataquest-dev/dspace-customers#717, #1333 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…n reload shift) (#1353) Backport of #1333 (originally landed on customer/vsb-tuo). Problem: for an authenticated user, a hard reload shifted the whole page right by the admin-sidebar width when the SSR snapshot was removed. The .outer-wrapper left gutter was produced by the @slideSidebarPadding animation, whose width is read from a browser-only CSS-variable store; on the server that store is empty so it renders padding-left:0, then the browser resolves the real width and the page jumps. Fix: drive the gutter from a CSS class (ds-admin-sidebar-{hidden,unpinned,pinned}, set from a small sidebarPaddingState$) whose padding-left resolves from the admin-sidebar width custom properties in CSS. CSS resolves those identically on the server (the SSR snapshot) and the browser (the live app), so there is no shift and no hardcoded width. The pin/unpin slide is preserved via transition:padding-left, gated behind ds-admin-sidebar-animate (enabled only after the first browser paint) so the initial SSR->CSR gutter resolution does not animate. Translated to this branch's root.component (older DSpace 7.6.1 generation, gutter var --ds-collapsed/total-sidebar-width), not a cherry-pick. Refs: dataquest-dev/dspace-customers#717, #1333 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…in reload shift) (#1352) Backport of #1333 (originally landed on customer/vsb-tuo). Problem: for an authenticated user, a hard reload shifted the whole page right by the admin-sidebar width when the SSR snapshot was removed. The .outer-wrapper left gutter was produced by the @slideSidebarPadding animation, whose width is read from a browser-only CSS-variable store; on the server that store is empty so it renders padding-left:0, then the browser resolves the real width and the page jumps. Fix: drive the gutter from a CSS class (ds-admin-sidebar-{hidden,unpinned,pinned}, set from a small sidebarPaddingState$) whose padding-left resolves from the admin-sidebar width custom properties in CSS. CSS resolves those identically on the server (the SSR snapshot) and the browser (the live app), so there is no shift and no hardcoded width. The pin/unpin slide is preserved via transition:padding-left, gated behind ds-admin-sidebar-animate (enabled only after the first browser paint) so the initial SSR->CSR gutter resolution does not animate. Translated to this branch's root.component (older DSpace 7.6.1 generation, gutter var --ds-collapsed/total-sidebar-width), not a cherry-pick. Refs: dataquest-dev/dspace-customers#717, #1333 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…load shift) (#1351) Backport of #1333 (originally landed on customer/vsb-tuo). Problem: for an authenticated user, a hard reload shifted the whole page right by the admin-sidebar width when the SSR snapshot was removed. The .outer-wrapper left gutter was produced by the @slideSidebarPadding animation, whose width is read from a browser-only CSS-variable store; on the server that store is empty so it renders padding-left:0, then the browser resolves the real width and the page jumps. Fix: drive the gutter from a CSS class (ds-admin-sidebar-{hidden,unpinned,pinned}, set from a small sidebarPaddingState$) whose padding-left resolves from the admin-sidebar width custom properties in CSS. CSS resolves those identically on the server (the SSR snapshot) and the browser (the live app), so there is no shift and no hardcoded width. The pin/unpin slide is preserved via transition:padding-left, gated behind ds-admin-sidebar-animate (enabled only after the first browser paint) so the initial SSR->CSR gutter resolution does not animate. Translated to this branch's root.component (older DSpace 7.6.1 generation, gutter var --ds-collapsed/total-sidebar-width), not a cherry-pick. Refs: dataquest-dev/dspace-customers#717, #1333 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…eload shift) (#1350) Backport of #1333 (originally landed on customer/vsb-tuo). Problem: for an authenticated user, a hard reload shifted the whole page right by the admin-sidebar width when the SSR snapshot was removed. The .outer-wrapper left gutter was produced by the @slideSidebarPadding animation, whose width is read from a browser-only CSS-variable store; on the server that store is empty so it renders padding-left:0, then the browser resolves the real width and the page jumps. Fix: drive the gutter from a CSS class (ds-admin-sidebar-{hidden,unpinned,pinned}, set from a small sidebarPaddingState$) whose padding-left resolves from the admin-sidebar width custom properties in CSS. CSS resolves those identically on the server (the SSR snapshot) and the browser (the live app), so there is no shift and no hardcoded width. The pin/unpin slide is preserved via transition:padding-left, gated behind ds-admin-sidebar-animate (enabled only after the first browser paint) so the initial SSR->CSR gutter resolution does not animate. Translated to this branch's root.component (identical to the vsb-tuo pre-#1333 generation; applied verbatim), not a cherry-pick. Refs: dataquest-dev/dspace-customers#717, #1333 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Follow-up to #1321 (the anti-flicker SSR overlay, already merged into
customer/vsb-tuo).Problem
With the overlay in place, an authenticated user's hard reload still showed the whole page jump right by the admin-sidebar width when the SSR snapshot was removed. (Anonymous users were already smooth — they have no sidebar.)
Root cause (independent of the overlay; the overlay just makes it a visible "reveal")
The
.outer-wrapperleft gutter came from the@slideSidebarPaddingAngular animation, whose width is read viacssService.getVariable('--ds-admin-sidebar-*'). That store is populated only in the browser (AppComponent.storeCSSVariables→getComputedStyle); on the server it stays empty,skipWhile(!val)blocks forever, and the animation rendersouter-wrapper { padding-left: 0 }. The browser then resolves the real width (e.g. 55px), so the authenticated page (and the SSR snapshot, which is just that server HTML) jumps right on reveal.Fix — no hardcoded width
Drive the gutter from a CSS class (
ds-admin-sidebar-{hidden,unpinned,pinned}, set from a smallsidebarPaddingState$) whosepadding-leftisvar(--ds-admin-sidebar-fixed-element-width)/--ds-admin-sidebar-total-width. CSS resolves those custom properties identically on the server (the snapshot) and the browser (the live app), so they always match — theme-overridable and viewport/media-query aware, with zero magic pixels — and atransition: padding-leftkeeps the pin/unpin slide. The@slideSidebarPaddinganimation isn't used here anymore (it can't take avar()value — Angular strips it to empty on SSR — which is exactly why the pure-CSS route is needed). Non-admins are unaffected (hidden→ padding 0).Verification (authenticated admin, real UA, throttled hard reload, local SSR build)
<div class="outer-wrapper … ds-admin-sidebar-unpinned">(a class, no inline px).#main-content:Refs: dspace-customers#725, #1321
🤖 Generated with Claude Code