dotUI
dotUI
beta
  1. Components
  2. Layout
  3. Scroll area

Scroll area

Augments native scroll functionality for custom, cross-browser styling.

import * as React from "react";
import { ScrollArea } from "@/lib/components/core/default/scroll-area";

function Demo() {
  return (
    <div className="rounded-md border p-6">
      <ScrollArea className="h-72 w-full max-w-sm" type="always">
        <div className="space-y-4 p-4 pr-8">
          <h4 className="text-md font-bold">Principles of the typographic craft</h4>
          <p>
            Three fundamental aspects of typography are legibility, readability, and aesthetics.
            Although in a non-technical sense “legible” and “readable” are often used synonymously,
            typographically they are separate but related concepts.
          </p>
          <p>
            Legibility describes how easily individual characters can be distinguished from one
            another. It is described by Walter Tracy as “the quality of being decipherable and
            recognisable”. For instance, if a “b” and an “h”, or a “3” and an “8”, are difficult to
            distinguish at small sizes, this is a problem of legibility.
          </p>
          <p>
            Typographers are concerned with legibility insofar as it is their job to select the
            correct font to use. Brush Script is an example of a font containing many characters
            that might be difficult to distinguish. The selection of cases influences the legibility
            of typography because using only uppercase letters (all-caps) reduces legibility.
          </p>
        </div>
      </ScrollArea>
    </div>
  );
}

Installation

Copy and paste the following code into your project.

"use client";

import * as React from "react";
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
import { type VariantProps, tv } from "tailwind-variants";

const scrollAreaStyles = tv({
  slots: {
    root: "flex flex-col h-full w-full overflow-hidden",
    viewport: "h-full w-full flex flex-col [&>*]:!block [&>*]:w-fit [&>*]:grow",
    scrollbar:
      "flex touch-none select-none flex-col data-[orientation=horizontal]:flex-row data-[orientation=vertical]:flex-col bg-gray-800 rounded-full m-1",
    thumb:
      "relative before:absolute before:content-[''] before:top-1/2 before:left-1/2 before:-translate-x-1/2 before:-translate-y-1/2] w-full h-full before:min-w-4 before:min-h-4 bg-gray-500 hover:bg-gray-400 transition-colors rounded-[inherit]",
  },
  variants: {
    size: {
      sm: {
        scrollbar: "data-[orientation=horizontal]:h-1 data-[orientation=vertical]:w-1",
      },
      md: {
        scrollbar: "data-[orientation=horizontal]:h-2 data-[orientation=vertical]:w-2",
      },
      lg: {
        scrollbar: "data-[orientation=horizontal]:h-3 data-[orientation=vertical]:w-3",
      },
    },
  },
  defaultVariants: {
    size: "md",
  },
});

interface ScrollAreaProps
  extends ScrollAreaRootProps,
    Omit<ScrollAreaViewPortProps, "dir">,
    VariantProps<typeof scrollAreaStyles> {
  scrollbars?: "vertical" | "horizontal" | "both";
}
const ScrollArea = ({
  children,
  scrollbars = "vertical",
  size,
  asChild,
  type,
  scrollHideDelay,
  dir,
  ...viewportProps
}: ScrollAreaProps) => {
  return (
    <ScrollAreaRoot asChild={asChild} scrollHideDelay={scrollHideDelay} dir={dir} type={type}>
      <ScrollAreaViewPort {...viewportProps}>{children}</ScrollAreaViewPort>
      {scrollbars !== "vertical" && <ScrollAreaScrollbar orientation="horizontal" size={size} />}
      {scrollbars !== "horizontal" && <ScrollAreaScrollbar orientation="vertical" size={size} />}
      {scrollbars === "both" && <ScrollAreaCorner />}
    </ScrollAreaRoot>
  );
};

type ScrollAreaRootProps = ScrollAreaPrimitive.ScrollAreaProps;
const ScrollAreaRoot = ({ className, ...props }: ScrollAreaRootProps) => {
  const { root } = scrollAreaStyles();
  return <ScrollAreaPrimitive.Root className={root({ className })} {...props} />;
};

type ScrollAreaViewPortProps = ScrollAreaPrimitive.ScrollAreaViewportProps;
const ScrollAreaViewPort = ({ className, ...props }: ScrollAreaViewPortProps) => {
  const { viewport } = scrollAreaStyles();
  return <ScrollAreaPrimitive.Viewport className={viewport({ className })} {...props} />;
};

interface ScrollAreaScrollbarProps
  extends ScrollAreaPrimitive.ScrollAreaScrollbarProps,
    VariantProps<typeof scrollAreaStyles> {}
const ScrollAreaScrollbar = ({ className, size, ...props }: ScrollAreaScrollbarProps) => {
  const { scrollbar, thumb } = scrollAreaStyles({ size });
  return (
    <ScrollAreaPrimitive.Scrollbar className={scrollbar({ className })} {...props}>
      <ScrollAreaPrimitive.Thumb className={thumb()} />
    </ScrollAreaPrimitive.Scrollbar>
  );
};

const ScrollAreaCorner = ScrollAreaPrimitive.Corner;

export type {
  ScrollAreaProps,
  ScrollAreaRootProps,
  ScrollAreaViewPortProps,
  ScrollAreaScrollbarProps,
};
export { ScrollArea, ScrollAreaRoot, ScrollAreaViewPort, ScrollAreaScrollbar, ScrollAreaCorner };

Update the import paths to match your project setup.

Usage

Use ScrollArea to augment native scroll functionality for custom, cross-browser styling.

Options

Sizes

import * as React from "react";
import { ScrollArea } from "@/lib/components/core/default/scroll-area";

const sizes = ["sm", "md", "lg"] as const;

function Demo() {
  return (
    <div className="grid grid-cols-3 gap-10">
      {sizes.map((size) => (
        <div key={size} className="rounded-md border p-3">
          <ScrollArea size={size} className="h-72" type="always">
            <div className="space-y-4 p-4 pr-8">
              <h4 className="text-md font-bold">Principles of the typographic craft</h4>
              <p>
                Three fundamental aspects of typography are legibility, readability, and aesthetics.
                Although in a non-technical sense “legible” and “readable” are often used
                synonymously, typographically they are separate but related concepts.
              </p>
              <p>
                Legibility describes how easily individual characters can be distinguished from one
                another. It is described by Walter Tracy as “the quality of being decipherable and
                recognisable”. For instance, if a “b” and an “h”, or a “3” and an “8”, are difficult
                to distinguish at small sizes, this is a problem of legibility.
              </p>
              <p>
                Typographers are concerned with legibility insofar as it is their job to select the
                correct font to use. Brush Script is an example of a font containing many characters
                that might be difficult to distinguish. The selection of cases influences the
                legibility of typography because using only uppercase letters (all-caps) reduces
                legibility.
              </p>
            </div>
          </ScrollArea>
        </div>
      ))}
    </div>
  );
}

Scrollbars

"use client";

import * as React from "react";
import { Radio, RadioGroup } from "@/lib/components/core/default/radio-group";
import { ScrollArea, type ScrollAreaProps } from "@/lib/components/core/default/scroll-area";

function Demo() {
  const [scrollbars, setScrollbars] = React.useState("vertical");
  return (
    <div className="flex items-center gap-10">
      <div className="rounded-md border p-6">
        <ScrollArea
          scrollbars={scrollbars as ScrollAreaProps["scrollbars"]}
          className="h-44 w-full max-w-sm"
          type="always"
        >
          <div className="flex w-[500px] items-start gap-4">
            <div className="space-y-4 p-4 pr-8">
              <h4 className="text-md font-bold">Principles of the typographic craft</h4>
              <p>
                Three fundamental aspects of typography are legibility, readability, and aesthetics.
                Although in a non-technical sense “legible” and “readable” are often used
                synonymously, typographically they are separate but related concepts.
              </p>
              <p>
                Legibility describes how easily individual characters can be distinguished from one
                another. It is described by Walter Tracy as “the quality of being decipherable and
                recognisable”. For instance, if a “b” and an “h”, or a “3” and an “8”, are difficult
                to distinguish at small sizes, this is a problem of legibility.
              </p>
              <p>
                Typographers are concerned with legibility insofar as it is their job to select the
                correct font to use. Brush Script is an example of a font containing many characters
                that might be difficult to distinguish. The selection of cases influences the
                legibility of typography because using only uppercase letters (all-caps) reduces
                legibility.
              </p>
            </div>
            <div className="space-y-4 p-4 pr-8">
              <h4 className="text-md font-bold">Principles of the typographic craft</h4>
              <p>
                Three fundamental aspects of typography are legibility, readability, and aesthetics.
                Although in a non-technical sense “legible” and “readable” are often used
                synonymously, typographically they are separate but related concepts.
              </p>
              <p>
                Legibility describes how easily individual characters can be distinguished from one
                another. It is described by Walter Tracy as “the quality of being decipherable and
                recognisable”. For instance, if a “b” and an “h”, or a “3” and an “8”, are difficult
                to distinguish at small sizes, this is a problem of legibility.
              </p>
              <p>
                Typographers are concerned with legibility insofar as it is their job to select the
                correct font to use. Brush Script is an example of a font containing many characters
                that might be difficult to distinguish. The selection of cases influences the
                legibility of typography because using only uppercase letters (all-caps) reduces
                legibility.
              </p>
            </div>
          </div>
        </ScrollArea>
      </div>
      <RadioGroup label="Scrollbars" value={scrollbars} onChange={setScrollbars}>
        <Radio value="vertical">Vertical</Radio>
        <Radio value="horizontal">Horizontal</Radio>
        <Radio value="both">Both</Radio>
      </RadioGroup>
    </div>
  );
}

API Reference

ScrollArea accepts all HTML div element props and the following:

PropTypeDefaultDescription
type
'auto' | 'always' | 'scroll' | 'hover'
'hover'
Describes the nature of scrollbar visibility.
scrollHideDelay
number
600
If type is set to either 'scroll' or 'hover', this prop determines the length of time, in milliseconds, before the scrollbars are hidden after the user stops interacting with scrollbars.
dir
'ltr' | 'rtl'
600
The reading direction of the scroll area
scrollbars
'vertical' | 'horizontal' | 'both'
'vertical'
Controls the scrollable axes.

Built by mehdibha. The source code is available on GitHub.