Aura Design System

Scroller

A horizontal strip with back and forward controls.

Preview

Loading...
import { Scroller } from "@/components/ui/Scroller";

export function ScrollerDemo() {
  return (
  <Scroller>
    {pages.map((page) => (
      <div
        key={page}
        className="flex h-8 w-12 shrink-0 items-center justify-center rounded-sm bg-accent-3"
      >
        {page}
      </div>
    ))}
  </Scroller>
)

Installation

Make sure that namespace is set in your component.json file. Namespace docs: Learn more about namespaces

pnpm dlx shadcn@latest add @aura/scroller

Manual

Install the following dependencies:

pnpm install @radix-ui/react-icons

Copy and paste the class names utility into your utils/class-names.ts file.

import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

Copy and paste the Scroller component into your components/ui/Scroller.tsx file.

"use client";

import * as React from "react";
import { ChevronLeftIcon, ChevronRightIcon } from "@radix-ui/react-icons";
import { cn } from "@/utils/class-names";

function Scroller({
  className,
  label = "Scroller",
  children,
}: {
  className?: string;
  label?: string;
  children: React.ReactNode;
}) {
  const ref = React.useRef<HTMLDivElement>(null);
  const move = (direction: number) => {
    const root = ref.current;
    if (!root) return;
    const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    root.scrollBy({ left: direction * root.clientWidth * 0.8, behavior: reduced ? "auto" : "smooth" });
  };
  return (
    <div data-slot="scroller" className={cn("flex items-center gap-0.5", className)}>
      <button
        type="button"
        aria-label="Scroll back"
        className="inline-flex size-3 items-center justify-center rounded-sm border border-gray-6 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-8"
        onClick={() => move(-1)}
      >
        <ChevronLeftIcon className="icon" />
      </button>
      <div
        ref={ref}
        role="region"
        aria-label={label}
        tabIndex={0}
        className="flex gap-0.5 overflow-x-auto"
        onKeyDown={(event) => {
          if (event.key === "ArrowRight") move(1);
          if (event.key === "ArrowLeft") move(-1);
        }}
      >
        {children}
      </div>
      <button
        type="button"
        aria-label="Scroll forward"
        className="inline-flex size-3 items-center justify-center rounded-sm border border-gray-6 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-8"
        onClick={() => move(1)}
      >
        <ChevronRightIcon className="icon" />
      </button>
    </div>
  );
}

export { Scroller };