i-Willink
ドキュメントメニュー

Overlay (Dialog / Popover)

willink DS の overlay は DialogPopover の 2 系統で、どちらも Radix Primitives の上に構築されています。フォーカス管理・Esc での dismiss・ARIA ロールは Radix が担い、DS は trigger に紐づくスタイルとモーション(motion のロールトークン)を上乗せします。すべて @willink-labs/react から import します。

Dialog vs Popover の使い分け

どちらも trigger から開く浮遊パネルですが、モーダル性が決定的に異なります。

コンポーネント性質用途
Dialogモーダル。フォーカストラップで背面を遮断します。確認・破壊的操作の警告・フォーム送信。
Popover非モーダル。trigger にアンカーされた浮遊パネルで、背面は操作可能なままです。設定・ピッカー・コンボボックス。

読み取り専用の hover / focus 補足には Tooltip を使い、操作可能な要素(ボタン・リンク・入力)は入れないでください。Tooltip はインタラクティブなコンテンツの容れ物ではありません。

Dialog

モーダルダイアログです。DialogTrigger asChild を付けて任意の要素(通常は Button)を trigger にし、DialogHeader / DialogTitle / DialogDescription / DialogFooter で構造化します。

tsx
import {
  Dialog,
  DialogTrigger,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogDescription,
  DialogFooter,
  DialogClose,
  Button,
} from '@willink-labs/react';

<Dialog>
  <DialogTrigger asChild>
    <Button>開く</Button>
  </DialogTrigger>
  <DialogContent size="md">
    <DialogHeader>
      <DialogTitle>確認</DialogTitle>
      <DialogDescription>この操作は取り消せません。</DialogDescription>
    </DialogHeader>
    <DialogFooter>
      <DialogClose asChild>
        <Button variant="outline">キャンセル</Button>
      </DialogClose>
      <Button>実行</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>

size

DialogContent は cva の size prop で最大幅を選びます。 既定は md です。

size最大幅用途
smmax-w-sm確認ダイアログ。
md(既定)max-w-md標準的なフォーム。
lgmax-w-lg項目数の多いフォーム。
xlmax-w-2xlリッチなコンテンツ。
fullmax-w-[95vw]ほぼ全画面のワークスペース。

その他の props

DialogContent は次の prop も受け取ります(型は DialogContentProps)。

  • closeButton?: ReactNode | false — 既定の を差し替える、または false で非表示にします。既定のボタンには aria-label="Close" が付与済みです。
  • portal?: boolean DialogPortal で body 直下に描画するか(既定 true)。
  • overlay?: boolean — 背面の DialogOverlay を描画するか(既定 true)。
tsx
{/* 既定の ✕ をカスタム要素に差し替える */}
<DialogContent closeButton={<XIcon aria-hidden />}>…</DialogContent>

{/* ✕ を非表示にする(自前の閉じる導線を用意する場合のみ) */}
<DialogContent closeButton={false}>…</DialogContent>

アクセシビリティ(Dialog)

Radix が フォーカストラップ Esc での dismiss を自動で提供し、role="dialog" を管理します。DialogTitle がアクセシブルネームになるため、必ず含めてください。開閉アニメーションは data-[state=open]:animate-dialog-in / data-[state=closed]:animate-dialog-out で、motion-reduce:animate-none により reduced-motion 環境では停止します。

Popover

非モーダルの浮遊パネルです。PopoverTrigger にアンカーされ、背面は操作可能なまま残ります。cva の variant は持たず、配置のみを prop で調整します。

tsx
import {
  Popover,
  PopoverTrigger,
  PopoverContent,
  Button,
} from '@willink-labs/react';

<Popover>
  <PopoverTrigger asChild>
    <Button>設定</Button>
  </PopoverTrigger>
  {/* aria-label は必須(下記 a11y を参照) */}
  <PopoverContent aria-label="設定" align="end" sideOffset={8}>
    …
  </PopoverContent>
</Popover>

PopoverContent の主な props:

  • align?: "start" | "center" | "end" — trigger に対する整列(既定 center)。
  • sideOffset?: number — trigger との距離(既定 4)。

aria-label は必須

重要: PopoverContent role="dialog" として描画されるため、アクセシブルネームが必須です。aria-label を渡すか、内部の見出しを指す aria-labelledby を渡してください。これを省くとスクリーンリーダーは「unnamed dialog」と読み上げ、axe の aria-dialog-name ルールが失敗します。

tsx
<PopoverContent aria-labelledby="filters-heading">
  <h3 id="filters-heading">絞り込み</h3>
  …
</PopoverContent>

Radix が フォーカス管理と、Esc / 外側クリックでの dismiss を提供します。フォーカス可能な要素を含めない単純な情報表示なら、 モーダルではなく Tooltip の採用を検討してください。

Storybook で挙動を確認できます — Dialog / Popover のストーリー。次は アクセシビリティ で overlay を含む a11y 契約全体を確認する。