HextaUI

Alert

ステータスやフィードバックを伝えるインラインメッセージです。ニュートラルなサーフェス、色付きのアイコン、滑らかに折りたたまれる閉じる操作を備えています。

Heads up
You can add components to your app using the CLI.
import { IconCircleX, IconTerminal2 } from "@tabler/icons-react"

import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"

export function AlertDemo() {
  return (
    <div className="flex w-full max-w-lg flex-col gap-3">
      <Alert>
        <IconTerminal2 />
        <AlertTitle>Heads up</AlertTitle>
        <AlertDescription>
          You can add components to your app using the CLI.
        </AlertDescription>
      </Alert>
      <Alert variant="destructive">
        <IconCircleX />
        <AlertTitle>Payment failed</AlertTitle>
        <AlertDescription>
          Your card was declined. Update your billing details.
        </AlertDescription>
      </Alert>
    </div>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/alert.json

コンポーネント、HextaUIのテーマトークン、依存するHextaUIコンポーネントを追加します。

import {
  Alert,
  AlertAction,
  AlertClose,
  AlertDescription,
  AlertTitle,
} from "@/components/ui/alert"
<Alert>
  <IconTerminal2 />
  <AlertTitle>Heads up</AlertTitle>
  <AlertDescription>
    You can add components to your app using the CLI.
  </AlertDescription>
</Alert>
Alert
├── AlertTitle
├── AlertDescription
├── AlertAction
└── AlertClose

バリアント

variant はアイコンにのみ色を付けるため、面は中立のままで、主張しすぎずに状態がひと目で分かります。

import {
  IconAlertTriangle,
  IconCircleCheck,
  IconCircleX,
  IconInfoCircle,
  IconTerminal2,
} from "@tabler/icons-react"

import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"

const alerts = [
  {
    variant: "default",
    icon: IconTerminal2,
    title: "Heads up",
    description: "You can add components to your app using the CLI.",
  },
  {
    variant: "destructive",
    icon: IconCircleX,
    title: "Payment failed",
    description: "Your card was declined. Update your billing details.",
  },
  {
    variant: "success",
    icon: IconCircleCheck,
    title: "Changes saved",
    description: "Your profile is now visible to your team.",
  },
  {
    variant: "info",
    icon: IconInfoCircle,
    title: "New version available",
    description: "Reload the page to get the latest features.",
  },
  {
    variant: "warning",
    icon: IconAlertTriangle,
    title: "Trial ends in 3 days",
    description: "Add a payment method to keep your workspace active.",
  },
] as const

export function AlertVariants() {
  return (
    <div className="flex w-full max-w-lg flex-col gap-3">
      {alerts.map(({ variant, icon: Icon, title, description }) => (
        <Alert key={variant} variant={variant}>
          <Icon />
          <AlertTitle>{title}</AlertTitle>
          <AlertDescription>{description}</AlertDescription>
        </Alert>
      ))}
    </div>
  )
}

Soft

appearance="soft" は枠線をなくし、面全体を variant の色で淡く染めます。

import {
  IconAlertTriangle,
  IconCircleCheck,
  IconCircleX,
  IconInfoCircle,
  IconTerminal2,
} from "@tabler/icons-react"

import {
  Alert,
  AlertAction,
  AlertClose,
  AlertDescription,
  AlertTitle,
} from "@/components/ui/alert"
import { Button } from "@/components/ui/button"

const alerts = [
  {
    variant: "default",
    icon: IconTerminal2,
    title: "Heads up",
    description: "You can add components to your app using the CLI.",
  },
  {
    variant: "destructive",
    icon: IconCircleX,
    title: "Payment failed",
    description: "Your card was declined. Update your billing details.",
  },
  {
    variant: "success",
    icon: IconCircleCheck,
    title: "Changes saved",
    description: "Your profile is now visible to your team.",
  },
  {
    variant: "info",
    icon: IconInfoCircle,
    title: "New version available",
    description: "Reload the page to get the latest features.",
  },
  {
    variant: "warning",
    icon: IconAlertTriangle,
    title: "Trial ends in 3 days",
    description: "Add a payment method to keep your workspace active.",
  },
] as const

export function AlertSoft() {
  return (
    <div className="flex w-full max-w-lg flex-col gap-3">
      {alerts.map(({ variant, icon: Icon, title, description }) => (
        <Alert key={variant} variant={variant} appearance="soft">
          <Icon />
          <AlertTitle>{title}</AlertTitle>
          <AlertDescription>{description}</AlertDescription>
        </Alert>
      ))}
      <Alert variant="success" appearance="soft">
        <IconCircleCheck />
        <AlertTitle>Message archived</AlertTitle>
        <AlertAction>
          <Button variant="ghost" size="xs">
            Undo
          </Button>
        </AlertAction>
        <AlertClose />
      </Alert>
    </div>
  )
}

部分的なコンテンツ

タイトルのみ、説明のみ、アイコンなしのいずれも、追加のクラスなしで揃います。グリッドが各パーツをそれぞれの列に配置します。

import { IconCircleCheck, IconInfoCircle } from "@tabler/icons-react"

import {
  Alert,
  AlertClose,
  AlertDescription,
  AlertTitle,
} from "@/components/ui/alert"

export function AlertPartialContent() {
  return (
    <div className="flex w-full max-w-lg flex-col gap-3">
      <Alert variant="success">
        <IconCircleCheck />
        <AlertTitle>Invite sent</AlertTitle>
        <AlertClose />
      </Alert>
      <Alert>
        <IconInfoCircle />
        <AlertDescription>
          Sessions expire after 30 days of inactivity.
        </AlertDescription>
      </Alert>
      <Alert>
        <AlertTitle>No icon</AlertTitle>
        <AlertDescription>The content starts at the padding.</AlertDescription>
      </Alert>
    </div>
  )
}

アクション

ボタンを <AlertAction /> に入れます。タイトルが折り返しても、ボタンはタイトルの 1 行目に揃います。

import { IconInfoCircle, IconTerminal2 } from "@tabler/icons-react"

import {
  Alert,
  AlertAction,
  AlertClose,
  AlertDescription,
  AlertTitle,
} from "@/components/ui/alert"
import { Button } from "@/components/ui/button"

export function AlertActions() {
  return (
    <div className="flex w-full max-w-lg flex-col gap-3">
      <Alert variant="info">
        <IconInfoCircle />
        <AlertTitle>
          A new version of the dashboard is available with faster charts, saved
          filters and a redesigned sidebar
        </AlertTitle>
        <AlertDescription>
          Reloading keeps your current filters.
        </AlertDescription>
        <AlertAction>
          <Button size="xs">Reload</Button>
        </AlertAction>
      </Alert>
      <Alert>
        <IconTerminal2 />
        <AlertTitle>Message archived</AlertTitle>
        <AlertAction>
          <Button variant="secondary" size="xs">
            Undo
          </Button>
        </AlertAction>
        <AlertClose />
      </Alert>
    </div>
  )
}

閉じられる alert

<AlertClose /> を追加すると、ユーザーが閉じられるようになります。alert はなめらかに折りたたまれ、下のコンテンツが上にスライドします。キーボードで閉じると、フォーカスは次のコントロール(ここでは「Reset」)に移ります。

"use client"

import * as React from "react"
import { IconInfoCircle } from "@tabler/icons-react"

import {
  Alert,
  AlertClose,
  AlertDescription,
  AlertTitle,
} from "@/components/ui/alert"
import { Button } from "@/components/ui/button"

export function AlertDismissible() {
  const [key, setKey] = React.useState(0)

  return (
    <div className="flex w-full max-w-lg flex-col gap-3">
      <Alert key={key} variant="info">
        <IconInfoCircle />
        <AlertTitle>We’ve updated our terms</AlertTitle>
        <AlertDescription>
          Read the <a href="#">new terms</a> before your next billing date.
        </AlertDescription>
        <AlertClose />
      </Alert>
      <div className="flex gap-2">
        <Button variant="outline" size="sm" onClick={() => setKey(key + 1)}>
          Reset
        </Button>
        <Button variant="outline" size="sm">
          Next focusable
        </Button>
      </div>
    </div>
  )
}

制御

open と onOpenChange を渡すと、独自の state で制御できます。表示と非表示の両方がアニメーションします。

"use client"

import * as React from "react"
import { IconCircleCheck } from "@tabler/icons-react"

import {
  Alert,
  AlertClose,
  AlertDescription,
  AlertTitle,
} from "@/components/ui/alert"
import { Button } from "@/components/ui/button"

export function AlertControlled() {
  const [open, setOpen] = React.useState(true)

  return (
    <div className="flex w-full max-w-lg flex-col gap-3">
      <Button
        variant="outline"
        size="sm"
        className="self-start"
        onClick={() => setOpen(!open)}
      >
        {open ? "Hide" : "Show"} alert
      </Button>
      <Alert variant="success" open={open} onOpenChange={setOpen}>
        <IconCircleCheck />
        <AlertTitle>Deployment complete</AlertTitle>
        <AlertDescription>
          Your changes are live on production.
        </AlertDescription>
        <AlertClose />
      </Alert>
      <p className="text-sm text-muted-foreground">Content below slides up.</p>
    </div>
  )
}

閉じる操作のキャンセル

閉じるボタンの onClick で event.preventDefault() を呼ぶと、alert を開いたままにできます。たとえば、先に確認を求めたい場合に使えます。

"use client"

import * as React from "react"
import { IconAlertTriangle } from "@tabler/icons-react"

import {
  Alert,
  AlertClose,
  AlertDescription,
  AlertTitle,
} from "@/components/ui/alert"

export function AlertCancelDismiss() {
  const [confirmed, setConfirmed] = React.useState(false)

  return (
    <Alert variant="warning" className="max-w-lg">
      <IconAlertTriangle />
      <AlertTitle>Unsaved changes</AlertTitle>
      <AlertDescription>
        {confirmed
          ? "Click dismiss again to close."
          : "The first click is cancelled with event.preventDefault()."}
      </AlertDescription>
      <AlertClose
        onClick={(event) => {
          if (!confirmed) {
            event.preventDefault()
            setConfirmed(true)
          }
        }}
      />
    </Alert>
  )
}

読み込み後に挿入

ページの読み込み後にマウントされた alert はフェードとスライドで表示され、スクリーンリーダーに通知されます。サーバーの HTML にすでに含まれている alert は、モーションなしで表示されます。

"use client"

import * as React from "react"
import { IconCircleX } from "@tabler/icons-react"

import {
  Alert,
  AlertClose,
  AlertDescription,
  AlertTitle,
} from "@/components/ui/alert"
import { Button } from "@/components/ui/button"

export function AlertInserted() {
  const [errors, setErrors] = React.useState<number[]>([])

  return (
    <div className="flex w-full max-w-lg flex-col gap-3">
      <Button
        variant="outline"
        size="sm"
        className="self-start"
        onClick={() => setErrors([...errors, Date.now()])}
      >
        Submit form
      </Button>
      {errors.map((id) => (
        <Alert
          key={id}
          variant="destructive"
          onOpenChange={() =>
            setErrors((current) => current.filter((error) => error !== id))
          }
        >
          <IconCircleX />
          <AlertTitle>Email is already in use</AlertTitle>
          <AlertDescription>Try signing in instead.</AlertDescription>
          <AlertClose />
        </Alert>
      ))}
    </div>
  )
}

リッチなコンテンツ

説明内のインラインアイコンやリンクは、それぞれのスタイルを保ちます。タイトルに見出しレベルを付けるには render を使います。

import {
  IconExternalLink,
  IconInfoCircle,
  IconTerminal2,
} from "@tabler/icons-react"

import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
import { Button } from "@/components/ui/button"

export function AlertRichContent() {
  return (
    <Alert className="max-w-lg">
      <IconInfoCircle />
      <AlertTitle render={<h3 />}>Rendered as an h3</AlertTitle>
      <AlertDescription>
        <p>
          Press <IconTerminal2 className="inline size-4 align-text-bottom" /> to
          open the terminal, or read the <a href="#">docs</a>.
        </p>
        <Button
          variant="outline"
          size="sm"
          nativeButton={false}
          render={<a href="#" />}
        >
          Open guide <IconExternalLink />
        </Button>
      </AlertDescription>
    </Alert>
  )
}

右から左

アイコン、コンテンツ、閉じるボタンは、読む方向に合わせて反転します。

import { IconAlertTriangle } from "@tabler/icons-react"

import {
  Alert,
  AlertClose,
  AlertDescription,
  AlertTitle,
} from "@/components/ui/alert"

export function AlertRtl() {
  return (
    <div dir="rtl" className="w-full max-w-lg">
      <Alert variant="warning">
        <IconAlertTriangle />
        <AlertTitle>تنتهي الفترة التجريبية خلال 3 أيام</AlertTitle>
        <AlertDescription>
          أضف طريقة دفع لإبقاء مساحة العمل نشطة.
        </AlertDescription>
        <AlertClose />
      </Alert>
    </div>
  )
}
キーアクション
Tabフォーカスを alert 内のリンク、アクション、閉じるボタンに移動します。
EnterSpaceフォーカス中の閉じるボタンを実行します。alert が折りたたまれると、フォーカスは次のフォーカス可能な要素に移り、後続がない場合は前の要素に移ります。
  • destructive と warning の alert には role="alert" が付き、すぐに通知されます。それ以外の variant には role="status" が付き、間を待って通知されます。
  • 閉じるボタンには「Dismiss」というラベルが付いています。翻訳するには aria-label を渡します。
  • フォーカスのある alert が閉じても、フォーカスが失われることはありません。
  • モーションの低減が有効な場合、alert は即座に表示・非表示になります。

各パーツは、レンダリングする要素の属性をすべて受け付けます。variant は alertVariants としてエクスポートされており、他の要素に同じスタイルを適用できます。

プロパティ型デフォルト
variantアイコンに色を付けます。destructive と warning は assertive で通知されます。
"default" | "destructive" | "success" | "info" | "warning""default"
appearance
"outline" | "soft""outline"
open
boolean–
defaultOpen
booleantrue
onOpenChange閉じるボタンが押されたときに呼ばれます。
(open: boolean) => void–
render
ReactElement | (props, state) => ReactElement<div>
属性説明
data-slot="alert"CSS で alert を指定します。
data-variant現在のバリアント。
data-appearance現在の外観。
data-ending-style閉じた後に alert が折りたたまれている間、付与されます。
プロパティ型デフォルト
render
ReactElement | (props, state) => ReactElement<div>
属性説明
data-slot="alert-title"CSS でタイトルを指定します。
プロパティ型デフォルト
render
ReactElement | (props, state) => ReactElement<div>
属性説明
data-slot="alert-description"CSS で説明を指定します。内部のリンクには自動で下線が付きます。
プロパティ型デフォルト
render
ReactElement | (props, state) => ReactElement<div>
属性説明
data-slot="alert-action"CSS でアクション領域を指定します。

最も近い alert を閉じる ghost の <Button />。Button のすべての props を受け付け、<Alert /> の内側で使う必要があります。

プロパティ型デフォルト
aria-label
string"Dismiss"
children
ReactNode<IconX />
onClickevent.preventDefault() を呼ぶと、alert を開いたままにします。
(event: MouseEvent) => void–
属性説明
data-slot="alert-close"CSS で閉じるボタンを指定します。

使用しているブロック

Alert の上に構築されるブロック。