From 4808d4726f9d336dc60b2238b89230f75d5d943b Mon Sep 17 00:00:00 2001 From: Niek Nijland Date: Sun, 4 Oct 2026 18:11:37 +0300 Subject: [PATCH] Homepage: restyle the highlights gallery after Apple's - Slides drop their border and use the surface grey of the other cards. The caption sits at the top of the card, on a blurred copy of the media that fills it, and the media fits below with rounded corners. - The page indicator is a grey pill of dots; the current slide's dot stretches into a bar that fills while autoplay shows it. - The previous and next arrows are gone. The play/pause button stays, as WCAG 2.2.2 requires, as a round grey button beside the dots. - The current slide follows the scroll as soon as it passes halfway, rather than once the track settles. As slides scroll, their media dims, shrinks and lags behind, and the backdrop swells and drifts further. Co-Authored-By: Claude Opus 5.5 --- docs/src/components/home/MediaBackdrop.astro | 10 +- .../home/presentations/AutoplayControl.astro | 9 +- .../home/presentations/Gallery.astro | 210 +++++++++--------- .../components/home/presentations/Tabs.astro | 2 +- docs/src/lib/utils/autoplay.ts | 5 +- docs/src/lib/utils/presentationIndex.ts | 29 ++- docs/tests/build-output.test.js | 4 +- docs/tests/presentation-index.test.js | 44 ++-- 8 files changed, 164 insertions(+), 149 deletions(-) diff --git a/docs/src/components/home/MediaBackdrop.astro b/docs/src/components/home/MediaBackdrop.astro index cb877e56..0d3e1525 100644 --- a/docs/src/components/home/MediaBackdrop.astro +++ b/docs/src/components/home/MediaBackdrop.astro @@ -5,9 +5,12 @@ import type { HomepageMedia } from "@/lib/homepageItems"; interface Props { media: HomepageMedia; + // Fills the whole frame instead of matching the media's own shape, for + // frames that hold more than the media, such as a caption. + cover?: boolean; } -const { media } = Astro.props; +const { media, cover = false } = Astro.props; // A small copy is enough once it's blurred. Videos use their poster, which // the page loads anyway. @@ -27,5 +30,8 @@ const src = aria-hidden="true" loading="lazy" decoding="async" - class="pointer-events-none absolute inset-0 size-full scale-110 object-contain opacity-40 blur-3xl" + class:list={[ + "pointer-events-none absolute inset-0 size-full scale-110 opacity-40 blur-3xl", + cover ? "object-cover" : "object-contain", + ]} /> diff --git a/docs/src/components/home/presentations/AutoplayControl.astro b/docs/src/components/home/presentations/AutoplayControl.astro index b86869d1..0a9750fd 100644 --- a/docs/src/components/home/presentations/AutoplayControl.astro +++ b/docs/src/components/home/presentations/AutoplayControl.astro @@ -2,10 +2,12 @@ interface Props { // What the button plays, for its label: "Pause Network features". name: string; + // Round, borderless and grey, to sit beside the gallery's dot pill. + round?: boolean; class?: string; } -const { name, class: className } = Astro.props; +const { name, round = false, class: className } = Astro.props; --- @@ -40,41 +38,55 @@ const arrowButtonClass = aria-roledescription="slide" aria-label={`${index + 1} of ${items.length}`} data-current={index === 0 ? "" : undefined} - class="group flex max-w-full shrink-0 basis-[min(880px,84vw)] snap-start flex-col overflow-hidden rounded-lg border border-border bg-card transition-colors duration-500 ease-smooth motion-reduce:transition-none data-current:border-[color-mix(in_srgb,var(--chapter)_45%,var(--color-border))]" + style={`--slide-offset: ${Math.min(index, 1)}; --slide-distance: ${Math.min(index, 1)}`} + class="group relative flex aspect-square max-w-full shrink-0 basis-[min(880px,84vw)] snap-start flex-col overflow-hidden rounded-lg bg-surface sm:aspect-[3/2]" > - {/* The frame has a fixed shape so every slide lines up, and the - media letterboxes inside it rather than cropping. 4:3 on phones - splits the difference between the landscape and square media; - 16:9 from sm fits most of it exactly. Neighbouring slides dim - their media only, so their captions stay readable. */} -
- - + {/* The card has a fixed shape so every slide lines up. A blurred + copy of the media fills it, so the caption sits on the media's + own colours, and the media letterboxes under the caption rather + than cropping. As a slide scrolls away from its resting place + (`--slide-offset`, set by the script below), its media dims, + shrinks and lags behind, and the backdrop swells and drifts + further; the captions stay readable. */} +
+
-

- - {item.title} - +

+ + {item.title}. + {" "} {item.description}

+ {/* The media shrinks to fit rather than filling the space, so its + rounded corners sit on its own edges. A video has no size until + its poster loads, so it holds 16:9 until then. */} +
+
+ +
+
)) } - +

-
+ +
{ items.map((item, index) => ( )) }
- - - +