reduce-effects: kill expensive GPU properties, stop freezing functional motion
"Reduce visual effects" was a sledgehammer: body.reduce-effects * forced animation:none + transition-duration:0s on every element. That froze every CSS loading spinner mid-rotation — including the dash-header worker-service spinners (musicbrainz / spotify / deezer / … .active .<svc>-spinner) — which read as BROKEN rather than "off". It also killed cheap hover feedback like the Quick Actions buttons. The actual lag (esp. Firefox, see the #935 sweep) is backdrop-filter / box-shadow / filter re-rasterizing every frame — NOT the animations themselves. Transform- and opacity-only motion (the spinners) composites for ~free. So: keep forcing the expensive properties to none (unchanged — that's the real fix), but drop the blanket animation/transition kills. !important author declarations outrank animation + transition declarations in the cascade, so any keyframe/transition that tries to set blur/shadow/filter is still neutralized even while it runs — the spinner spins, just without the glow. Net: functional spinners stay alive, Quick Actions hover (transform + border-colour) returns, box-shadow transitions are no-ops (shadow forced none), and the GPU-heavy rendering that caused the lag stays gone. The worker-orb CANVAS is unaffected (JS-gated separately) and stays off under reduce-effects, as intended. Static guard test pins the contract: the global rule must keep the expensive-property kills and must NOT reintroduce blanket animation:none / transition:0s.
This commit is contained in:
parent
6a388c43fc
commit
3207310448
2 changed files with 51 additions and 5 deletions
39
tests/test_reduce_effects_css.py
Normal file
39
tests/test_reduce_effects_css.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
"""Guard the reduce-visual-effects CSS contract.
|
||||
|
||||
Performance mode must kill the GPU-EXPENSIVE properties (backdrop-filter / box-shadow
|
||||
/ filter — the real lag, esp. on Firefox) but must NOT blanket-freeze animations:
|
||||
`animation: none` on every element stuck the dash-header worker-service spinners
|
||||
mid-rotation, which read as "broken" (Discord/Boulder). Pins the surgical rule so a
|
||||
future edit can't silently re-freeze the functional spinners or kill cheap hover
|
||||
feedback."""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
_STYLE = Path(__file__).resolve().parent.parent / "webui" / "static" / "style.css"
|
||||
|
||||
|
||||
def _reduce_effects_global_body() -> str:
|
||||
css = _STYLE.read_text(encoding="utf-8")
|
||||
m = re.search(r"body\.reduce-effects \*,.*?\{([^}]*)\}", css, re.DOTALL)
|
||||
assert m, "global 'body.reduce-effects *' rule not found"
|
||||
return m.group(1)
|
||||
|
||||
|
||||
def test_reduce_effects_still_kills_expensive_gpu_properties():
|
||||
body = _reduce_effects_global_body()
|
||||
for prop in ("backdrop-filter: none", "box-shadow: none", "filter: none"):
|
||||
assert prop in body, f"reduce-effects must still force {prop} (the real lag source)"
|
||||
|
||||
|
||||
def test_reduce_effects_does_not_blanket_freeze_animations():
|
||||
body = _reduce_effects_global_body()
|
||||
# `animation: none` on * froze the dash-header worker-service spinners mid-spin;
|
||||
# cheap transform/opacity motion must survive so a spinner still reads as "working".
|
||||
assert "animation: none" not in body, (
|
||||
"blanket 'animation: none' re-freezes the worker spinners — keep motion alive, "
|
||||
"the expensive properties are already neutralized above"
|
||||
)
|
||||
assert "transition-duration: 0s" not in body, (
|
||||
"blanket 'transition-duration: 0s' kills the cheap Quick Actions hover feedback"
|
||||
)
|
||||
|
|
@ -60380,14 +60380,21 @@ tr:hover .enhanced-track-actions-group { opacity: 1; }
|
|||
}
|
||||
.blacklist-entry-remove:hover { background: rgba(239, 83, 80, 0.12); color: #ef5350; }
|
||||
|
||||
/* ── Reduce Visual Effects ── Full performance mode: disables GPU-heavy
|
||||
properties and halts decorative animation globally. */
|
||||
/* ── Reduce Visual Effects ── Performance mode. Kills the GPU-EXPENSIVE
|
||||
properties only — the actual lag (esp. Firefox) is backdrop-filter / box-shadow /
|
||||
filter re-rasterizing every frame. Because !important author declarations outrank
|
||||
animation + transition declarations in the cascade, any keyframe/transition that
|
||||
tries to set these is forced to none even while it runs — so the expensive part is
|
||||
neutralized without freezing the motion itself.
|
||||
|
||||
We deliberately do NOT blanket `animation: none` / `transition-duration: 0s`:
|
||||
that froze every functional loading spinner (the dash-header worker-service
|
||||
spinners read as BROKEN, stuck mid-rotation) and killed cheap transform/colour
|
||||
feedback (e.g. the Quick Actions hover). Transform- and opacity-only motion is
|
||||
composited for ~free, so it stays — a spinner that spins, just without glow. */
|
||||
body.reduce-effects *,
|
||||
body.reduce-effects *::before,
|
||||
body.reduce-effects *::after {
|
||||
animation: none !important;
|
||||
transition-duration: 0s !important;
|
||||
transition-delay: 0s !important;
|
||||
backdrop-filter: none !important;
|
||||
-webkit-backdrop-filter: none !important;
|
||||
box-shadow: none !important;
|
||||
|
|
|
|||
Loading…
Reference in a new issue