Skip to content

Reel

Edit on GitHub

A horizontally scrolling container for browsing collections of cards or content frames.

@volvo-cars/css v2.5.1@volvo-cars/react-headless v0.25.1

Apply .reel to the scroll container and .snap-start to each frame. Add useReel when navigation controls or active-frame tracking are required.

Frame 1
Frame 2
Frame 3
Frame 4
Frame 5
Frame 6
Frame 7
Frame 8
Show codeHide code
import { useReel } from '@volvo-cars/react-headless';
import { useRef } from 'react';
export function BasicReel() {
const ref = useRef<HTMLDivElement>(null);
const { reelProps } = useReel({ ref });
return (
<section
ref={ref}
className="reel gap-x-gridded-element"
aria-label="Example frames"
{...reelProps}
>
{Array.from({ length: 8 }, (_, i) => i + 1).map((frame) => (
<div
key={frame}
className="snap-start flex items-center justify-center bg-secondary p-24 shape-default"
style={{ minWidth: '12rem', minHeight: '8rem' }}
>
Frame {frame}
</div>
))}
</section>
);
}

Spread previousButtonProps and nextButtonProps onto icon buttons to add back/forward navigation. The hook manages disabled state and hides the buttons from assistive technology since the scroll container is already keyboard-navigable.

Reels work well for browsing product cards. Set a width on each card and use flex-shrink-0 to prevent them from collapsing. The last card should always be partially visible to indicate more content.

Add the .scrollbar-none utility class to hide the native scrollbar. Only use this when you provide other visual indicators (like navigation arrows) that more content is available off-screen.

The .reel class provides horizontal scrolling with snap points without JavaScript. This is useful for server-rendered content or simple layouts that don’t need navigation controls.

Frame 1
Frame 2
Frame 3
Frame 4
Frame 5
Frame 6
Frame 7
Frame 8
Show codeHide code
<section class="reel gap-x-gridded-element" aria-label="Example frames">
<div
class="snap-start flex items-center justify-center bg-secondary p-24 shape-default"
style="min-width: 12rem; min-height: 8rem"
>
Frame 1
</div>
<div
class="snap-start flex items-center justify-center bg-secondary p-24 shape-default"
style="min-width: 12rem; min-height: 8rem"
>
Frame 2
</div>
<div
class="snap-start flex items-center justify-center bg-secondary p-24 shape-default"
style="min-width: 12rem; min-height: 8rem"
>
Frame 3
</div>
<div
class="snap-start flex items-center justify-center bg-secondary p-24 shape-default"
style="min-width: 12rem; min-height: 8rem"
>
Frame 4
</div>
<div
class="snap-start flex items-center justify-center bg-secondary p-24 shape-default"
style="min-width: 12rem; min-height: 8rem"
>
Frame 5
</div>
<div
class="snap-start flex items-center justify-center bg-secondary p-24 shape-default"
style="min-width: 12rem; min-height: 8rem"
>
Frame 6
</div>
<div
class="snap-start flex items-center justify-center bg-secondary p-24 shape-default"
style="min-width: 12rem; min-height: 8rem"
>
Frame 7
</div>
<div
class="snap-start flex items-center justify-center bg-secondary p-24 shape-default"
style="min-width: 12rem; min-height: 8rem"
>
Frame 8
</div>
</section>
  • Arrow Left / Right: scroll through frames when the reel has focus.
  • Tab: moves focus to focusable children within frames, or to the reel container itself if no focusable children exist.
  • The useReel hook automatically sets tabIndex={0} on the reel container when it has no focusable children, ensuring keyboard users can scroll with arrow keys.

Key consumer responsibilities from the WCAG audit:

  • Labelling: provide aria-label or aria-labelledby on the scrollable container so screen readers can announce its purpose.
  • Navigation buttons: previousButtonProps and nextButtonProps include aria-hidden="true" since the reel is already keyboard-navigable.
  • Focus management: with focusable children (links, buttons), keyboard users navigate by focusing each item. Without focusable children, useReel makes the container itself focusable.
  • Reduced motion: the .reel class respects prefers-reduced-motion by disabling smooth scrolling.

See the useReel hooks documentation for full API reference, options, and return values.