HextaUI

Scroll fade

Bordas de uma área de rolagem que esmaecem só quando há mais a ver, controladas pela posição de rolagem apenas com CSS.

  • v2.24.0Released
  • v2.23.0Released
  • v2.22.0Released
  • v2.21.0Released
  • v2.20.0Released
  • v2.19.0Released
  • v2.18.0Released
  • v2.17.0Released
  • v2.16.0Released
  • v2.15.0Released
  • v2.14.0Released
  • v2.13.0Released
  • v2.12.0Released
  • v2.11.0Released
  • v2.10.0Released
  • v2.9.0Released
  • v2.8.0Released
  • v2.7.0Released
  • v2.6.0Released
  • v2.5.0Released
  • v2.4.0Released
  • v2.3.0Released
  • v2.2.0Released
  • v2.1.0Released
const releases = Array.from({ length: 24 }, (_, index) => `v2.${24 - index}.0`)

export function ScrollFadeDemo() {
  return (
    <div className="w-full max-w-xs rounded-xl bg-muted">
      <ul
        tabIndex={0}
        aria-label="Releases"
        className="h-64 scroll-fade overflow-y-auto rounded-xl p-2 text-sm outline-none focus-visible:ring-3 focus-visible:ring-focus-ring focus-visible:outline-hidden"
      >
        {releases.map((release) => (
          <li
            key={release}
            className="flex h-9 items-center justify-between rounded-md px-3"
          >
            <span className="font-mono">{release}</span>
            <span className="text-muted-foreground">Released</span>
          </li>
        ))}
      </ul>
    </div>
  )
}

Scroll fade é um conjunto de utilitários do Tailwind vindos do tailwind.css do shadcn. O tema do HextaUI já o importa, então se você adicionou o tema ou qualquer componente, já o tem.

pnpm dlx shadcn@latest add https://hextaui.com/r/theme.json

Sem o tema, instale shadcn e importe seu CSS depois do Tailwind.

pnpm add shadcn
app/globals.css
@import "tailwindcss";
@import "shadcn/tailwind.css";
<div className="scroll-fade h-64 overflow-y-auto">…</div>
<div className="flex scroll-fade-x overflow-x-auto">…</div>

Adicione a qualquer elemento que rola. Uma borda só esmaece enquanto há mais conteúdo além dela, então uma linha cortada sinaliza "continue rolando" e uma lista em repouso mantém as bordas nítidas.

O esmaecimento é uma mask-image, então o conteúdo se dissolve no que estiver atrás dele. Não há gradiente de sobreposição para combinar com o fundo, e funciona em imagens, superfícies com tom e vidro. Uma animação CSS controlada por rolagem aumenta o esmaecimento de cada borda nos primeiros e últimos 96px de rolagem. Nenhum JavaScript é executado e nunca há nova renderização.

  • Cada esmaecimento tem 12% do contêiner, limitado a 40px.
  • Navegadores sem animações controladas por rolagem mostram os dois esmaecimentos o tempo todo. É um pouco menos preciso, mas ainda indica que dá para rolar.
  • scroll-fade-x, scroll-fade-s e scroll-fade-e seguem a direção de escrita, então o esmaecimento de início fica à direita em layouts da direita para a esquerda.

Horizontal

Uma linha de filtros que rola para o lado. Combine com no-scrollbar, também do CSS do shadcn, quando só o esmaecimento já mostra que há mais.

const topics = [
  "All",
  "Design",
  "Engineering",
  "Product",
  "Research",
  "Marketing",
  "Sales",
  "Support",
  "Operations",
  "Finance",
]

export function ScrollFadeHorizontal() {
  return (
    <div
      tabIndex={0}
      role="region"
      aria-label="Topics"
      className="no-scrollbar flex w-full max-w-sm scroll-fade-x gap-2 overflow-x-auto rounded-md outline-none focus-visible:ring-3 focus-visible:ring-focus-ring focus-visible:outline-hidden"
    >
      {topics.map((topic) => (
        <span
          key={topic}
          className="shrink-0 rounded-full bg-muted px-3 py-1.5 text-sm"
        >
          {topic}
        </span>
      ))}
    </div>
  )
}

Uma borda

Um chat começa no final, então só as mensagens antigas acima precisam de uma dica. scroll-fade-t esmaece apenas o topo, e scroll-fade-t-16 o deixa mais alto.

"use client"

const messages = Array.from({ length: 16 }, (_, index) => ({
  id: index,
  text: index % 3 === 0 ? "Sounds good, ship it." : "Pushed the fix to main.",
}))

export function ScrollFadeEdge() {
  return (
    <div className="w-full max-w-xs rounded-xl bg-muted">
      <ul
        ref={(node) => {
          if (node) {
            node.scrollTop = node.scrollHeight
          }
        }}
        tabIndex={0}
        aria-label="Messages"
        className="flex h-64 scroll-fade-t flex-col gap-2 overflow-y-auto rounded-xl p-3 text-sm outline-none scroll-fade-t-16 focus-visible:ring-3 focus-visible:ring-focus-ring focus-visible:outline-hidden"
      >
        {messages.map((message) => (
          <li
            key={message.id}
            className="w-fit max-w-[80%] shrink-0 rounded-2xl bg-background px-3 py-2 even:self-end even:bg-primary even:text-primary-foreground"
          >
            {message.text}
          </li>
        ))}
      </ul>
    </div>
  )
}
<div className="scroll-fade scroll-fade-16 overflow-y-auto">…</div>
<div className="scroll-fade scroll-fade-[20%] overflow-y-auto">…</div>
<div className="scroll-fade [--scroll-fade-reveal:12rem] overflow-y-auto">…</div>

Os tamanhos aceitam a escala de espaçamento ou qualquer comprimento ou porcentagem. --scroll-fade-reveal define quanto você rola antes de uma borda atingir o esmaecimento total.

  • A máscara também esmaece o fundo e a borda do próprio elemento. Coloque a superfície em um wrapper e o esmaecimento no filho que rola, como fazem os exemplos.
  • Uma máscara esconde tudo perto da borda, incluindo anéis de foco. Dê algum padding ao elemento que rola para que itens com foco não sejam esmaecidos.
  • Use Scroll area em vez disso quando também quiser barras de rolagem personalizadas. Ele tem seus próprios esmaecimentos, medidos com JavaScript, que funcionam em todos os navegadores.
ClasseDescrição
scroll-fade, scroll-fade-yEsmaece as bordas superior e inferior.
scroll-fade-xEsmaece as bordas de início e de fim, seguindo a direção.
scroll-fade-t, scroll-fade-bEsmaece apenas a borda superior ou inferior.
scroll-fade-s, scroll-fade-eEsmaece apenas a borda de início ou de fim.
scroll-fade-l, scroll-fade-rEsmaece apenas a borda esquerda ou direita, ignorando a direção.
scroll-fade-<size>Tamanho de todos os esmaecimentos.
scroll-fade-{t,b,s,e}-<size>Tamanho do esmaecimento de uma borda.
scroll-fade-noneDesliga o esmaecimento, por exemplo em um breakpoint.
--scroll-fade-revealDistância de rolagem em que uma borda esmaece. O padrão é 96px.