---
id: portfolio.ds.project-hero-carousel
name: "Project Hero Carousel"
type: pattern
status: active
relations: [{"type":"composes","target":"portfolio.ds.project-hero-item"},{"type":"composes","target":"portfolio.ds.carousel-arrow"},{"type":"composes","target":"portfolio.ds.project-carousel-navigation"},{"type":"related_to","target":"portfolio.ds.spacing"}]
sources: ["packages/design-system/src/components/ProjectHeroCarousel.astro","apps/portfolio-site/src/design-system/docs/project-hero-carousel.astro","apps/portfolio-site/src/lib/designSystemRegistry.ts"]
catalog: ["apps/portfolio-site/src/design-system/docs/project-hero-carousel.astro"]
previews: ["apps/portfolio-site/src/design-system/docs/project-hero-carousel.astro"]
---
# Project Hero Carousel

## Purpose

Independent looping Project composition used by the Homepage. The legacy Project Hero remains available in the Design System until its cleanup Story is accepted.

## Contract

- Composes accepted Project Hero Item, Carousel Arrow and Project Carousel Navigation without modifying their standalone behavior.
- Accepts ordered Project data and localized navigation labels/URLs; never sorts or fetches content.
- Embla owns dragging, snapping and loop geometry. This parent owns selection, progress, pause and manual takeover.
- The carousel owns Project media lifecycle. Every slide server-renders a lightweight project-specific WebP placeholder so loading never reveals the debug surface. Before viewport entry no video tier is requested. Once the carousel intersects, tier batches download for all Projects. On Tablet, Desktop and Ultra-wide, the selected slide and its immediate looping neighbors attach and play the committed tier; Mobile plays only the selected slide. Distant sources are detached.
- Video remains transparent above the WebP placeholder until its `playing` event, then appears over `120ms ease-out`; reduced motion removes the transition and keeps the placeholder static.
- Media quality advances through carousel-wide barriers. Every Project renders its WebP immediately; the carousel then downloads the complete Mobile batch for every Project and commits Mobile to all items at once. It repeats the same all-ready commit for Tablet, Desktop, and Ultra-wide, stopping at the current named breakpoint. A tier must never appear one card at a time.
- Downloaded batches are converted to local object URLs before commit, so playback never depends on a partially streamed promotion. Active and adjacent slides reuse the committed tier without restarting when their role changes; distant slides retain the committed tier metadata and their WebP without spending decoder or animation resources.
- Carousel items use their responsive poster as the production fallback and do not embed the realtime SVG fallback tree in every slide. Standalone video-animation components keep their source/fallback child behavior.
- Wide active media is `16:9`; Mobile media is `4:3`. The active item occupies the same inner width as Project Carousel Navigation between the left and right Block Spacing boundaries. Slides use a `16px` gap.
- Project Carousel Navigation uses the canonical Block Spacing inset on both its left and right edges.
- Mobile uses `4:3` media, copy, then left-aligned all-Projects navigation. No arrows or Project selectors.
- Underline is the default progress presentation: it fills for 8000ms, holds for 350ms, fades for 150ms, then advances. The optional cursor travels for 8000ms.
- Hover pauses elapsed time. Hidden pages and offscreen carousels pause. Manual arrows, selection, keyboard, drag or horizontal wheel permanently hide progress and disable auto-advance until remount.
- The progress scheduler runs only in automatic `running` mode, updates only the selected timer button per frame, and cancels its animation frame while paused, offscreen, document-hidden, moving, reduced-motion, or under manual takeover.
- Pointer hover pauses only inside the active central Project card, where the complete underline track becomes solid semantic black. Project picker, See All, adjacent slide slivers and surrounding carousel space do not pause. Leaving the active card restores the `16%` unfilled track and resumes from the preserved progress position; non-hover pause reasons do not apply this visual state.
- Reduced motion disables automatic advancement and uses immediate manual transitions.
- The component owns its scroll-linked motion layer: the complete navigation-and-media composition eases from `0` to `-128px` inside a stable clipped box while its top edge completes a one-viewport entry, then holds that offset. Reduced Motion fixes this layer at `0`.
- Mobile reserves `64px` above the moving composition so the `4:3` card remains visible while the parallax layer travels upward.
- Zero Projects render nothing. One Project has no automatic progress or arrows. Two Projects repeat physical slides for loop coverage but keep two logical selectors.
- Preview uses abstract Projects and existing media assets, not production copy or ordering.

## Media Loading Architecture

`Project Hero Carousel` owns one ordered media pipeline for the complete composition; individual `Project Hero Item` and video-wrapper instances must not independently promote their quality.

1. SSR exposes one project-specific WebP per card. No video tier is requested before the carousel enters the viewport.
2. The parent resolves the terminal tier from the named Design System breakpoint, never from device-pixel ratio: Mobile `0`, Tablet `1`, Desktop `2`, Ultra-wide `3`.
3. For each tier in ascending order, the parent fetches every unique Project asset and waits for the complete batch. A partial batch is never committed.
4. The complete batch becomes same-origin object URLs and one `project-media-tier-change` event commits that tier to every item simultaneously. The previous batch is revoked after consumers switch.
5. Mobile attaches and plays only the selected item. Larger breakpoints attach and play the selected item plus its immediate looping neighbors. Distant items retain only WebP and committed-tier metadata.
6. Selection changes reuse already committed object URLs. Shared active/adjacent items keep playing without source restart; newly adjacent items start from the committed local asset.
7. Leaving the viewport or hiding the document detaches playback sources. Reduced Motion keeps the static WebP path and does not start the tier pipeline.

This architecture intentionally trades sequential network transfer through the terminal breakpoint for atomic visual upgrades and bounded decoder work. Generic standalone video-animation components keep container-measured autonomous source selection; the barrier pipeline is specific to this carousel composition.

## Use

- Review the new Project gallery independently in the Design System.
- Supply production content only after the owner authorizes integration.

## Do Not Use

- Do not remove the legacy Project Hero Design System artifact outside its dedicated cleanup Story.
- Do not move carousel state into the controlled navigation children.
- Do not wrap the component in a second Project-specific scroll-motion container.

## Accessibility

- Native buttons and links, labeled region, arrow/Home/End keyboard navigation.
- Only the selected slide is interactive; inactive physical slides are inert and hidden from accessibility APIs.
- Manual selection is announced politely. Automatic changes are not repeatedly announced.
- Keyboard focus stops automatic advancement and exposes arrow controls.

## Verification

- Check loop boundaries, selection synchronization, timer handoff, hover pause and permanent manual takeover.
- Confirm that only lightweight WebP placeholders load before viewport entry, playback is limited to the breakpoint-specific active window, and every source is unloaded when the carousel leaves the viewport.
- Throttle a tier batch and confirm every Project keeps its WebP until the entire tier is ready, without exposing the debug background or partially committing that tier.
- Confirm every Project requests Mobile before any Tablet request, every Tablet request completes before Desktop is committed, and the pipeline stops at Mobile, Tablet, Desktop, or Ultra-wide according to the current breakpoint.
- Confirm all Project videos report the same committed tier after each barrier. Mobile must have one playing selected video; larger breakpoints must have three playing videos: selected, previous and next. Distant Projects must have no attached source or playing decoder.
- Confirm that Project carousel HTML does not contain one realtime SVG fallback tree per slide, while standalone video-animation previews retain their runtime fallback template.
- Check mobile preview and real narrow viewport, touch gestures and reduced motion.
- Run build, localization and Design System graph checks.
