模態框與對話框 是做什麼的?
規則
- 使用 Radix Dialog 或 shadcn
<Dialog> — 預設具備無障礙性、焦點鎖定、滾動鎖定
- 破壞性操作使用確認對話框:「刪除此專案嗎?」搭配「取消」(次要按鈕)與「刪除」(破壞性按鈕)
- 行動裝置使用抽屜面板 / 抽屜:使用 shadcn 的
<Sheet> — 從底部或側邊滑出,觸控目標比置中模態框更好
- 焦點鎖定:Radix 自動處理 — 開啟時聚焦第一個可聚焦元素,關閉時返回原聚焦元素
- 滾動鎖定:模態框開啟時防止頁面滾動 — Radix 透過
<Dialog.Overlay> 處理
- 關閉觸發:Escape 鍵、點擊外部遮罩、明確的關閉按鈕 — 全部由 Radix 提供
- 受控狀態:
open + onOpenChange props — 若由外部事件觸發模態框,請勿依賴內部狀態
- 對話框內含表單:Enter 提交、成功時關閉、內嵌顯示錯誤訊息 — 驗證失敗時不要關閉
- 行動裝置:小螢幕上使用
<Sheet>(底部抽屜)取代置中 <Dialog> 來呈現操作
模式
import {
AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent,
AlertDialogDescription, AlertDialogFooter, AlertDialogHeader,
AlertDialogTitle, AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
function DeleteButton({ onDelete }: { onDelete: () => Promise<void> }) {
const [loading, setLoading] = useState(false);
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="destructive">刪除專案</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>刪除此專案嗎?</AlertDialogTitle>
<AlertDialogDescription>
此操作將永久刪除該專案及其所有資料。此動作無法復原。
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>取消</AlertDialogCancel>
<AlertDialogAction
onClick={async () => { setLoading(true); await onDelete(); setLoading(false); }}
disabled={loading}
>
{loading ? "刪除中..." : "刪除"}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
}
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from "@/components/ui/sheet";
function MobileMenu() {
return (
<Sheet>
<SheetTrigger asChild><Button variant="ghost" size="icon"><MenuIcon /></Button></SheetTrigger>
<SheetContent side="bottom">
<SheetHeader><SheetTitle>操作</SheetTitle></SheetHeader>
<nav className="flex flex-col gap-2 py-4">
<Button variant="ghost">編輯</Button>
<Button variant="ghost">分享</Button>
<Button variant="destructive">刪除</Button>
</nav>
</SheetContent>
</Sheet>
);
}
應避免
- 使用
position: fixed 自製模態框並手動管理焦點 — 請使用 Radix/shadcn Dialog
- 缺少 Escape 鍵關閉與點擊外部關閉 — Radix 已處理兩者,請勿覆蓋它們
- 在行動裝置上使用置中模態框呈現操作選單 — 請改用底部 Sheet/抽屜以獲得更好的拇指觸及範圍
- 在表單驗證失敗時關閉對話框 — 保持開啟,顯示內嵌錯誤訊息,讓使用者修正後重新提交
- 巢狀對話框缺乏正確的堆疊上下文 — 如有需要,請使用 Radix 的巢狀 Dialog 支援並搭配正確的
z-index