mirror of
https://github.com/itflow-org/itflow
synced 2026-08-31 20:05:12 +00:00
Make each page refresh feel less Jenky with Smooth Fade Page Transitions
This commit is contained in:
@@ -1380,3 +1380,122 @@ select.select2[multiple]:not(.tomselected) {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===========================================================================
|
||||
CROSS-DOCUMENT VIEW TRANSITIONS
|
||||
===========================================================================
|
||||
Crossfades between full page loads on same-origin navigation. No JS: the
|
||||
browser snapshots the outgoing page, loads the incoming one, and animates
|
||||
between the two snapshots.
|
||||
|
||||
Both documents have to carry the at-rule for a navigation to animate, which
|
||||
is why it lives here - every ITFlow entry point loads this stylesheet, so
|
||||
agent, admin, client portal, guest, login and setup all opt in together.
|
||||
|
||||
Browsers without support ignore the entire block and navigate exactly as
|
||||
they did before. Nothing below is load-bearing.
|
||||
|
||||
Skipped automatically, no handling needed: reloads, address-bar and
|
||||
bookmark navigations, cross-origin redirects (the M365 OAuth callback, the
|
||||
Stripe return legs) and anything that never commits a navigation, such as
|
||||
an authenticated file download. A navigation slower than roughly four
|
||||
seconds is dropped back to an ordinary page load. */
|
||||
@view-transition {
|
||||
navigation: auto;
|
||||
}
|
||||
|
||||
/* Shorter than the 0.25s UA default - this should read as a soft handoff
|
||||
rather than an animation you sit through. Overriding `animation` alone
|
||||
keeps the UA stylesheet's mix-blend-mode: plus-lighter, which is what lets
|
||||
regions that are identical on both pages crossfade without visibly dipping
|
||||
through a lighter frame in the middle. */
|
||||
::view-transition-old(root) {
|
||||
animation: itflow-vt-fade-out 120ms ease both;
|
||||
}
|
||||
|
||||
::view-transition-new(root) {
|
||||
animation: itflow-vt-fade-in 160ms ease both;
|
||||
}
|
||||
|
||||
@keyframes itflow-vt-fade-out {
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes itflow-vt-fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* The header and side nav are deliberately NOT given a view-transition-name.
|
||||
|
||||
The first version of this block did, and froze both with animation:none so
|
||||
they would sit still through the crossfade. That was a mistake. A named
|
||||
element gets its own ::view-transition-group, and a frozen group holds the
|
||||
OLD geometry for the whole transition while the incoming snapshot is
|
||||
stretched to fit it, snapping to its real size only at the end. Geometry
|
||||
here is not constant the way I assumed: .app-header and .app-sidebar are
|
||||
fixed to the viewport, and the viewport width changes by the scrollbar width
|
||||
whenever you navigate between a page long enough to scroll and one that is
|
||||
not. Hence some navigations looking fine and others not.
|
||||
|
||||
Leaving them in the root snapshot is both simpler and better. The UA
|
||||
stylesheet crossfades ::view-transition-old/new with
|
||||
mix-blend-mode: plus-lighter precisely so that regions which are identical
|
||||
on both pages hold steady rather than dipping through a lighter frame in the
|
||||
middle, and the chrome is identical apart from which nav item carries
|
||||
.active. Overriding `animation` alone above keeps that blend mode. */
|
||||
|
||||
/* Keep the layout viewport a constant width.
|
||||
|
||||
This is what was making some navigations judder and others not. AdminLTE
|
||||
sizes .app-wrapper, .app-header, .app-main and .app-footer against 100vw,
|
||||
and 100vw INCLUDES the scrollbar, so the usable width is 100vw minus
|
||||
whatever the scrollbar takes. Nothing reserves that space, so the vertical
|
||||
scrollbar - and with it the layout width - appears and disappears purely on
|
||||
whether a given page happens to be taller than the window.
|
||||
|
||||
For a view transition that is not cosmetic. ::view-transition-group(root) is
|
||||
sized to the viewport, and the UA animates the group's width and height from
|
||||
the outgoing value to the incoming one, stretching BOTH snapshots to fit for
|
||||
the whole duration and snapping at the end. Navigate between a page that
|
||||
scrolls and one that does not and every glyph on the page smears sideways by
|
||||
the scrollbar width and then jumps back.
|
||||
|
||||
The client side nav is where this shows worst, because
|
||||
agent/includes/inc_client_top_head.php renders #clientHeader expanded on
|
||||
client_overview.php and collapsed everywhere else - so moving between
|
||||
Overview and any sibling page flips the scrollbar on and off almost every
|
||||
time.
|
||||
|
||||
scrollbar-gutter reserves the space permanently, so the width never moves.
|
||||
The max-width correction has to come with it: with the gutter always
|
||||
present, an unqualified 100vw now overruns the layout viewport on EVERY
|
||||
page rather than only on tall ones, which would trade an intermittent
|
||||
horizontal jump for a permanent horizontal scrollbar. 100% is the width of
|
||||
the grid area these four already occupy, which is what the rule wanted.
|
||||
AdminLTE made the same correction upstream in 4.8.3. */
|
||||
html {
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.app-wrapper,
|
||||
.app-header,
|
||||
.app-main,
|
||||
.app-footer {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* AdminLTE ships its own reduced-motion handling, but it injects a
|
||||
#adminlte-reduce-motion style block keyed on *, *::before and *::after.
|
||||
The ::view-transition pseudo tree is not a descendant of any element those
|
||||
match, so it is not covered and needs its own opt-out. */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
::view-transition-group(*),
|
||||
::view-transition-old(*),
|
||||
::view-transition-new(*) {
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user