---
id: portfolio.ds.project-carousel-navigation
name: "Project Carousel Navigation"
type: subcomponent
status: active
relations: [{"type":"composes","target":"portfolio.ds.icon-and-label-button"},{"type":"composes","target":"portfolio.ds.project-carousel-timer-button"},{"type":"related_to","target":"portfolio.ds.breakpoints"},{"type":"related_to","target":"portfolio.ds.project-hero-carousel"},{"type":"related_to","target":"portfolio.ds.spacing"}]
sources: ["apps/portfolio-site/src/design-system/docs/project-carousel-navigation.astro","apps/portfolio-site/src/lib/designSystemRegistry.ts","packages/design-system/src/components/ProjectCarouselNavigation.astro"]
catalog: ["apps/portfolio-site/src/design-system/docs/project-carousel-navigation.astro"]
previews: ["apps/portfolio-site/src/design-system/docs/project-carousel-navigation.astro"]
---
# Project Carousel Navigation

## Purpose

Responsive Project selector and all-Projects navigation shell for Project Hero Carousel.

## Contract

- Belongs directly to `Project Hero Carousel` and composes `Project Carousel Timer Button` plus one `Icon and Label Button` link.
- Accepts the complete Project set as stable `id` and visible `label` data; it does not source, sort, or localize Projects.
- Accepts one parent-owned `selectedProjectId` and forwards controlled progress data only to the matching Timer Button.
- Dispatches bubbling `project-carousel-select` with `detail.projectId` when a Project button is activated.
- Never mutates selected Project, progress, carousel media, URL, or auto-advance state internally.
- Accepts localized `seeAllLabel`, `selectionLabel`, and locale-aware `seeAllHref` inputs.
- Uses one all-Projects link. It is not duplicated between responsive layouts.
- Does not accept, render, or preview carousel media; Project Hero Carousel owns both media and responsive placement of this navigation sibling.
- Wide layouts present the wrapping Project selector list and all-Projects link in one row for parent placement above the carousel.
- Project selectors use a `16px` (`--space-4`) horizontal button gap, retain intrinsic widths, wrap onto additional rows, and never stretch. Wrapped rows use zero flex row-gap because each button contributes `8px` block padding on both touching edges; the resulting visible text-to-text interval is the same `16px` as the button gap.
- The wide-layout gap between the complete Project selector list and the all-Projects action is `32px` (`--space-8`), twice the spacing between adjacent selector buttons.
- Mobile hides the selector list and leaves the left-aligned all-Projects link as the only control so Project Hero Carousel can place navigation below the media.
- Tablet, Desktop, and Ultra-wide use the wide composition; Mobile behavior begins at the canonical `640px` boundary.
- Preview breakpoint behavior mirrors runtime CSS without relying on the browser viewport.

## Use

- Composing Project navigation around Project Hero Carousel.
- Keeping wide direct selection and Mobile all-Projects navigation in one responsive component.

## Do Not Use

- Do not use as generic tabs or site navigation.
- Do not implement auto-advance, arrows, swipe, media translation, or scroll motion here.
- Do not place carousel media inside this component.
- Do not pass Project-specific visuals or descriptions into navigation data.

## Props And Events

| API | Purpose |
| --- | --- |
| `projects` | Stable Project ids and visible labels, with optional disabled state. |
| `selectedProjectId` | Parent-owned selected Project id. |
| `progress` | Parent-owned progress forwarded to the selected Timer Button. |
| `indicator` | Timer Button indicator presentation. |
| `paused` | Parent-owned pause state forwarded to Timer Buttons. |
| `progressHidden` | Parent-owned progress visibility forwarded to Timer Buttons. |
| `seeAllHref` | Locale-aware Projects index URL. |
| `seeAllLabel` | Localized all-Projects command. |
| `selectionLabel` | Localized accessible name for the Project selector group. |
| `project-carousel-select` | Bubbling event with `detail.projectId`. |

## Accessibility

- Project selectors retain native button keyboard behavior and `aria-pressed` selection semantics from Project Carousel Timer Button.
- The selector group has a localized accessible name.
- The all-Projects command remains a native link.
- Project Hero Carousel owns responsive sibling order: navigation precedes media on wide layouts and follows it on Mobile.
- Selection remains exposed semantically and does not depend on color or timer position alone.

## Verification

- Verify wrapping with long labels and at least two rows without horizontal overflow.
- Verify exactly one selected Timer Button and one all-Projects link.
- Verify Mobile hides selectors and leaves exactly one all-Projects link.
- Verify the selection event reports the activated stable id without mutating the component.
- Run `scripts/design-system-graph --check --no-write`.
