mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-12 12:06:16 -08:00
fix(dashboard): keep the hero height stable across slides (#1855)
* fix(dashboard): keep the hero height stable across slides The hero laid out only the shown slide, so every automatic rotation between slides of different heights resized the banner and moved every rail below it by 7px, every 8 s on an idle dashboard (a layout-shift score of 0.005 per rotation cycle). Every slide's content now sits in the same grid cell at the bottom of the banner. The hero is therefore as tall as its tallest slide whichever one is shown, and it still grows for an unusually full slide. Inactive slides are inert and visibility: hidden, and carry no test hooks. Enter on the hero follows the shown slide's primary action. The rotation dots keep one fixed width. The active pill is the same 18px bar with its clip-path opened, so a slide change no longer moves the neighbouring dots or the pill itself. The legibility E2E now records which nodes shift. It allows under 0.001 in all, with nothing inside the hero moving. It measures 0 on this branch and fails on master (0.0054). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(dashboard): guard the hero rotation at the narrow width too The rotation guard ran only at the wide width, before any slide was enriched. It now runs a second unattended rotation after the contrast pass, at the narrow width, where slides wrap the most and every slide carries a rating and a two-line overview. On master's hero that second rotation also fails, because its dots move. measureBackdropTextContrast clipped its screenshot to the range of the element's line boxes. A title cut by the 2-line clamp has line boxes below the visible box, so the probe measured the pill row there and scored about 1.0. The Xtream mock gives the recently-added slide a different title per run, so this failed whenever a long one came up. The clip is now the line boxes intersected with the element's own box. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7bd7ff5ac7
commit
f6ad98255e
8 files changed
+241
-65
No files matched your search
@@ -108,6 +108,16 @@ Render rules:
|
||||
`clamp(320px, 42vh, 520px)` so the first rail starts above the fold, and uses
|
||||
`--app-content-bg` as its scrim so it dissolves into the page in both themes.
|
||||
|
||||
Height: that clamp is a floor, not a fixed height. Every slide's content is
|
||||
laid out in the same grid cell at the bottom of the banner, so it is as tall
|
||||
as its tallest slide whichever one is shown, and an unusually full slide or a
|
||||
long translation grows it instead of being cut. Only the active slide is
|
||||
shown; the others are `inert` and `visibility: hidden`. When only the shown
|
||||
slide was in flow, each automatic rotation between slides of different
|
||||
heights resized the banner and moved every rail below it, every 8 s on an idle
|
||||
dashboard. Late data (TMDB extras, the live slide's first EPG answer, the next
|
||||
programme) can still grow the tallest slide, once, when it arrives.
|
||||
|
||||
Slides (`pickDashboardHeroSources`, at most four, stable order, each title
|
||||
once):
|
||||
|
||||
@@ -161,9 +171,11 @@ Semantics: the page has one stable, visually hidden `h1` ("Dashboard",
|
||||
`dashboard-page-heading`); each slide title is an `h2`, like the rail titles.
|
||||
Slide changes are announced by one polite live region
|
||||
(`dashboard-hero-announcement`, position and title) that lives outside the
|
||||
re-created slide and is silent while the slides rotate on their own. A
|
||||
slides and is silent while the slides rotate on their own. A
|
||||
slide's progress bar is named after its title (a live slide: the programme)
|
||||
and a title's reads "N% watched". The dots are 24px targets (WCAG 2.5.8).
|
||||
and a title's reads "N% watched". The dots are 26 × 24px targets (WCAG 2.5.8)
|
||||
of one fixed width; the active pill is the same 18px bar with its
|
||||
`clip-path` opened, so a slide change moves no dot.
|
||||
|
||||
Rotation is the active dot's CSS fill animation (8 s); its `animationend`
|
||||
advances. The fill animates `transform` only (a bar sliding in under the
|
||||
@@ -176,10 +188,14 @@ region: ←/→ switch slides and Enter follows the primary action. The active s
|
||||
by id, so a late live slide never moves the user off the current one. Test
|
||||
hooks: `dashboard-hero`, `dashboard-hero-slide` (`data-hero-kind`),
|
||||
`dashboard-hero-dot`, `dashboard-hero-pause`,
|
||||
`dashboard-hero-primary-action`, `dashboard-hero-secondary-action`.
|
||||
`dashboard-hero-primary-action`, `dashboard-hero-secondary-action`. The
|
||||
slide hooks mark the shown slide only; the inert slides carry none.
|
||||
`dashboard-hero-rotation.e2e.ts` drives the real fill animation (with a
|
||||
shortened `--hero-rotation-ms`) to prove its `animationend` still advances
|
||||
and that pause holds the slide.
|
||||
and that pause holds the slide. `dashboard-hero-legibility.e2e.ts` sums the
|
||||
layout shifts of an unattended rotation, at the wide width and again at the
|
||||
narrow one once every slide carries a rating and an overview: under 0.001 in
|
||||
all, and none inside the hero.
|
||||
|
||||
## Rail Contract
|
||||
|
||||
|
||||
Reference in new issue
Block a user