# scroll-fade

Utilities for adding a fade effect to the edges of a scroll container.

## Installation

```bash
npx shadcn@latest add https://reactdocs.canceydejean.dev/r/scroll-fade.json
```

[Registry JSON](https://reactdocs.canceydejean.dev/r/scroll-fade.json)

## Preview

```tsx
import { scrollFadeClass, scrollFadeXClass } from "@/lib/scroll-fade";

const verticalItems = Array.from({ length: 12 }, (_, i) => `Item ${i + 1}`);
const tags = [
  "Design",
  "Engineering",
  "Marketing",
  "Product",
  "Research",
  "Sales",
  "Support",
  "Operations",
];

export function Preview() {
  return (
    <div className="flex w-full max-w-md flex-col gap-6">
      <div className="overflow-hidden rounded-2xl border">
        <div className={`${scrollFadeClass} max-h-40 px-4 py-2`}>
          <ul className="divide-y">
            {verticalItems.map((item) => (
              <li key={item} className="py-2 text-sm">
                {item}
              </li>
            ))}
          </ul>
        </div>
      </div>
      <div className="overflow-hidden rounded-2xl border">
        <div className={`${scrollFadeXClass} flex gap-2 px-3 py-3`}>
          {tags.map((tag) => (
            <span
              key={tag}
              className="shrink-0 rounded-full border bg-muted px-3 py-1 text-xs font-medium"
            >
              {tag}
            </span>
          ))}
        </div>
      </div>
    </div>
  );
}
```


## Source

### lib/scroll-fade.ts

```ts
/**
 * Tailwind class helpers for scroll-edge fades.
 * Requires `@import "shadcn/tailwind.css"` in your global CSS
 * (ships with the `shadcn` package).
 *
 * Prefer writing utility class names as string literals in markup so Tailwind
 * can see them at build time.
 *
 * @see https://ui.shadcn.com/docs/utils/scroll-fade
 */

/** Vertical scroller: fade top + bottom edges, scroll-aware. */
export const scrollFadeClass = "scroll-fade overflow-y-auto";

/** Alias for vertical scroll-fade. */
export const scrollFadeYClass = "scroll-fade-y overflow-y-auto";

/** Horizontal scroller: fade start + end edges (RTL-aware). */
export const scrollFadeXClass = "scroll-fade-x overflow-x-auto";

/** Physical / logical edge fades (scroll-aware). */
export const scrollFadeEdgeClasses = {
  top: "scroll-fade-t overflow-y-auto",
  bottom: "scroll-fade-b overflow-y-auto",
  left: "scroll-fade-l overflow-x-auto",
  right: "scroll-fade-r overflow-x-auto",
  /** Logical start (mirrors in RTL) */
  start: "scroll-fade-s overflow-x-auto",
  /** Logical end (mirrors in RTL) */
  end: "scroll-fade-e overflow-x-auto",
} as const;

/** Common fixed fade depths on the spacing scale. */
export const scrollFadeSizeClasses = {
  4: "scroll-fade-4",
  8: "scroll-fade-8",
  12: "scroll-fade-12",
  16: "scroll-fade-16",
  24: "scroll-fade-24",
} as const;

/** Disable the fade (works in any class order, useful for responsive cases). */
export const scrollFadeNoneClass = "scroll-fade-none";
```



## Usage

Scroll-aware edge fades for overflow containers. Classes ship in
`shadcn/tailwind.css` — import that in your global CSS (or install the design
system [styles](/utilities/styles) item).

```css
@import "tailwindcss";
@import "shadcn/tailwind.css";
```

```tsx
import { scrollFadeClass } from "@/lib/scroll-fade";

<div className={`${scrollFadeClass} max-h-48`}>
  {/* long content */}
</div>
```

Or use the utilities directly:

```tsx
<div className="scroll-fade max-h-48 overflow-y-auto">{/* ... */}</div>
<div className="scroll-fade-x overflow-x-auto">{/* ... */}</div>
<div className="scroll-fade-b overflow-y-auto">{/* ... */}</div>
```

| Class | Effect |
| --- | --- |
| `scroll-fade` / `scroll-fade-y` | Top + bottom edges, vertical scroll |
| `scroll-fade-x` | Start + end edges, horizontal scroll (RTL-aware) |
| `scroll-fade-t` / `b` / `l` / `r` | Single physical edge |
| `scroll-fade-s` / `e` | Logical inline edges (mirror in RTL) |
| `scroll-fade-<n>` | Fixed fade depth on the spacing scale |
| `scroll-fade-none` | Disable the fade |

Put the background and border on a **wrapper**; put `scroll-fade` on the **inner** scroller so the mask dissolves content, not the chrome.

