HextaUI

Shimmer

進行中の状態を示すために、テキスト上を横切る光の帯。テキストの色から色付けされ、視差効果の軽減が有効な場合は静止します。

Thinking about your question…

export function ShimmerDemo() {
  return (
    <p className="shimmer text-base text-muted-foreground">
      Thinking about your question…
    </p>
  )
}

shimmer は、shadcn の tailwind.css に含まれる Tailwind ユーティリティです。HextaUI のテーマがすでにインポートしているため、テーマや任意のコンポーネントを追加済みであれば使えます。

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

テーマを使わない場合は、shadcn をインストールし、Tailwind の後にその CSS をインポートしてください。

pnpm add shadcn
app/globals.css
@import "tailwindcss";
@import "shadcn/tailwind.css";
<p className="shimmer text-muted-foreground">Generating response…</p>

アシスタントが考え中、ファイルを処理中、ステップを実行中など、進行中の作業を説明する短いテキストに使います。動くハイライトが、言葉の横にスピナーの場所を取らずに「作業中」であることを伝えます。

テキストは background-clip: text を通じてグラデーションで描画され、より明るい帯がその上をスライドします。帯はテキスト自身の色から導かれるので、設定なしで、淡い色、前景色、色付きのテキストのいずれにも合い、明るい帯の方が読みやすいダークモードでは明るくなります。

  • 視差効果の軽減が有効な場合、グラデーションは完全に取り除かれ、テキストは通常の色で描画されます。
  • 右から左のレイアウトでは掃引が逆になり、それでも読み方向に進みます。
  • テキストは本物のテキストのままです。選択でき、検索でき、スクリーンリーダーでも通常どおり読み上げられます。

オプション

速度、帯の幅、色、角度は、それぞれクラス1つで指定します。

export function ShimmerOptions() {
  return (
    <div className="flex flex-col items-start gap-4 text-sm">
      <p className="shimmer text-muted-foreground shimmer-duration-1000">
        Faster: shimmer-duration-1000
      </p>
      <p className="shimmer text-muted-foreground shimmer-spread-24">
        Wider band: shimmer-spread-24
      </p>
      <p className="shimmer text-foreground shimmer-color-info">
        Tinted: shimmer-color-info
      </p>
      <p className="shimmer text-muted-foreground shimmer-angle-45">
        Steeper: shimmer-angle-45
      </p>
    </div>
  )
}

ステータスの更新

タスクの実行中は shimmer させ、完了したら shimmer-once で一度だけ掃引します。key を変えると、新しいステップごとに掃引が再開します。

"use client"

import * as React from "react"
import { cn } from "cn"

import { Button } from "@/components/ui/button"

const steps = ["Reading files…", "Planning changes…", "Writing code…"]

export function ShimmerStatus() {
  const [step, setStep] = React.useState<number | null>(null)

  React.useEffect(() => {
    if (step === null || step >= steps.length) {
      return
    }
    const timer = setTimeout(() => setStep(step + 1), 1600)
    return () => clearTimeout(timer)
  }, [step])

  const done = step !== null && step >= steps.length

  return (
    <div className="flex flex-col items-center gap-4">
      <p
        key={step ?? "idle"}
        role="status"
        className={cn(
          "h-5 text-sm text-muted-foreground",
          step !== null && "shimmer",
          done && "shimmer-duration-1200 shimmer-once"
        )}
      >
        {step === null ? "Ready" : done ? "Done." : steps[step]}
      </p>
      <Button variant="outline" size="sm" onClick={() => setStep(0)}>
        Run task
      </Button>
    </div>
  )
}
<p className="shimmer md:shimmer-none">Generating response…</p>
<p className="shimmer shimmer-reverse">Undoing…</p>
  • 1〜2行に留めてください。光る段落は読むと疲れます。
  • テキストが変わるときは、新しいステップが読み上げられるよう、role="status" の領域に入れてください。shimmer 自体は装飾です。
  • テキストではなくプレースホルダーのブロックには、テーマの animate-shimmer で面を光らせる Skeleton を使います。
クラス説明
shimmer2秒ごとに、テキスト上をハイライトが横切ります。
shimmer-once繰り返さずに一度だけ掃引します。
shimmer-reverse反対方向に掃引します。
shimmer-noneオフにします。たとえばブレークポイントで使います。
shimmer-color-<color>ハイライトの色。テーマの色でも任意の色でも指定でき、不透明度の修飾子を受け付けます。
shimmer-duration-<ms>1回の掃引の長さ(ミリ秒)。
shimmer-spread-<number>スペーシングスケールまたは任意の長さでの帯の幅。デフォルトは 3ch + 40px です。
shimmer-angle-<deg>帯の傾き。デフォルトは20°です。