Lapisan
Alert dialog
Pertanyaan modal sebelum tindakan yang berdampak besar, dengan pilihan aman difokuskan lebih dulu dan konsekuensi dinyatakan dalam satu kalimat.
Alert dialog mengajukan satu pertanyaan sebelum sesuatu yang sulit atau tidak mungkin dibatalkan: menghapus, mengirim,
pergi dengan perubahan yang belum disimpan. Komponen ini adalah Dialog dengan role="alertdialog", judul
singkat yang menyebut objeknya, satu kalimat tentang konsekuensi, dan dua tombol. Aksi yang aman (Batal, Tetap di sini)
mendapat fokus, sehingga menekan Enter secara refleks tidak pernah menghancurkan apa pun. Sebagian besar aplikasi tidak
membutuhkan markup per tombol: pasang data-ds-confirm pada pemicu dan dialog bersama menangani sisanya. Untuk tindakan
yang dapat dibatalkan, lewati pertanyaan dan tawarkan Urungkan di Toast.
Asal: TPS Views/Shared/_ConfirmDialog.cshtml, _LeaveDialog.cshtml
Contoh
Konfirmasi dari atribut
Pemicu tetap berfungsi tanpa JavaScript (form langsung dikirim); dengan JavaScript, pemicu bertanya lebih dulu. Setelah "Hapus", form dikirim dengan tombol yang sama, sehingga server melihat request yang sama.
Lihat kode HTML
<div class="ds-actions is-start">
<form method="post" action="/projects/1024/delete">
<button type="submit" class="btn btn-ghost is-danger"
data-ds-confirm="Hapus proyek PRJ-1024?"
data-ds-confirm-text="38 tugas dan 12 file di dalamnya ikut terhapus. Tindakan ini tidak dapat dibatalkan."
data-ds-confirm-label="Hapus proyek">
<svg class="ds-icon" aria-hidden="true"><use href="#ds-icon-trash"></use></svg>
Hapus proyek
</button>
</form>
<button type="button" class="btn btn-primary"
data-ds-confirm="Kirim INV-2026-0418 ke Daniel Okafor?"
data-ds-confirm-text="Klien menerima PDF melalui email. Invoice yang sudah terkirim hanya dapat dikoreksi dengan nota kredit."
data-ds-confirm-label="Kirim invoice"
data-ds-confirm-tone="primary">
<svg class="ds-icon" aria-hidden="true"><use href="#ds-icon-send"></use></svg>
Kirim invoice
</button>
</div>Markup tertulis dengan ikon
Saat dialog membutuhkan lebih dari satu judul dan satu kalimat (daftar hal yang terdampak, konfirmasi yang diketik),
tulis sebagai dialog dengan role="alertdialog". Ikon mengulang tone; judul tetap menyampaikan semuanya.
Lihat kode HTML
<button type="button" class="btn" data-ds-dialog-open="alert-revoke">Cabut akses</button>
<dialog id="alert-revoke" class="modal" role="alertdialog" aria-labelledby="alert-revoke-title" aria-describedby="alert-revoke-desc">
<div class="modal-box ds-dialog is-sm">
<div class="ds-dialog__header">
<div class="ds-dialog__heading">
<span class="ds-dialog__icon is-danger"><svg class="ds-icon" aria-hidden="true"><use href="#ds-icon-alert-triangle"></use></svg></span>
<h2 id="alert-revoke-title" class="ds-dialog__title">Cabut akses Mei Tanaka?</h2>
<p id="alert-revoke-desc" class="ds-dialog__description">Ia keluar dari semua perangkat dalam satu menit dan kehilangan akses ke 4 proyek.</p>
</div>
</div>
<div class="ds-dialog__footer">
<button type="button" class="btn" data-ds-dialog-close="cancel">Batal</button>
<button type="button" class="btn btn-error" data-ds-dialog-close="revoke">Cabut akses</button>
</div>
</div>
</dialog>Konfirmasi dengan aksi async
DS.confirm({ action }) menjaga dialog tetap terbuka selama request berjalan: tombol menampilkan spinner setelah 300ms,
Escape dan Batal diblokir, dan kegagalan tetap berada di dalam dialog sebagai error yang diumumkan, bukan menutupnya.
Lihat kode HTML
<button type="button" class="btn" id="archive-demo">Arsipkan workspace</button>
<script type="module">
document.getElementById("archive-demo").addEventListener("click", async (event) => {
let attempts = 0;
const ok = await DS.confirm({
title: "Arsipkan workspace kantor Jakarta?",
text: "Anggota tetap memiliki akses baca. Workspace dapat dipulihkan dalam 30 hari.",
label: "Arsipkan",
tone: "primary",
action: () => new Promise((resolve, reject) => setTimeout(() => (++attempts === 1 ? reject(new Error("Server tidak menjawab. Coba lagi.")) : resolve()), 1200)),
}, event.currentTarget);
if (ok) DS.toast({ tone: "success", title: "Workspace diarsipkan" });
});
</script>Percobaan pertama pada contoh ini sengaja gagal untuk menampilkan state error; percobaan kedua berhasil.
State
Hapus 3 invoice?
Draf INV-2026-0420 sampai 0422 dihapus.
Arsipkan workspace?
Anggota tetap memiliki akses baca.
Server tidak menjawab. Coba lagi.
Arsipkan workspace?
Anggota tetap memiliki akses baca.
Lihat kode HTML
<div style="display: grid; gap: 1.5rem; grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); align-items: start">
<div class="ds-dialog" style="width: auto; max-width: none" role="group" aria-label="Diam">
<h2 class="ds-dialog-title">Hapus 3 invoice?</h2>
<p class="ds-dialog-desc">Draf INV-2026-0420 sampai 0422 dihapus.</p>
<div class="modal-action"><button type="button" class="btn">Batal</button><button type="button" class="btn btn-error">Hapus</button></div>
</div>
<div class="ds-dialog" style="width: auto; max-width: none" role="group" aria-label="Error">
<h2 class="ds-dialog-title">Arsipkan workspace?</h2>
<p class="ds-dialog-desc">Anggota tetap memiliki akses baca.</p>
<p class="ds-dialog__error" role="presentation">Server tidak menjawab. Coba lagi.</p>
<div class="modal-action"><button type="button" class="btn">Batal</button><button type="button" class="btn btn-primary">Arsipkan</button></div>
</div>
<div class="ds-dialog" style="width: auto; max-width: none" aria-busy="true" role="group" aria-label="Sibuk">
<h2 class="ds-dialog-title">Arsipkan workspace?</h2>
<p class="ds-dialog-desc">Anggota tetap memiliki akses baca.</p>
<div class="modal-action"><button type="button" class="btn">Batal</button><button type="button" class="btn btn-primary" aria-busy="true" aria-disabled="true"><span class="ds-spinner" aria-hidden="true"></span>Arsipkan</button></div>
</div>
</div>| State | Pemicu | Tampilan |
|---|---|---|
| Terbuka, destruktif | data-ds-confirm, tone danger (default) |
Aksi merah (btn-error), Batal difokuskan |
| Terbuka, utama | data-ds-confirm-tone="primary" |
Aksi teal, Batal tetap difokuskan kecuali data-ds-confirm-focus="accept" |
| Sibuk | action berjalan |
aria-busy="true"; spinner pada aksi setelah 300ms; label dan lebar tidak berubah; Escape, Batal, dan scrim tidak melakukan apa pun |
| Error | action ditolak |
.ds-dialog__error dengan role="alert" di atas aksi dalam --ds-error-text; fokus kembali ke aksi |
| Diterima | tombol aksi | Dialog menutup lewat jalur masuknya (140ms); promise resolve true; pemicu form mengirim form |
| Dibatalkan | Batal, Escape | Promise resolve false; fokus kembali ke pemicu |
| Reduced motion | media query | Hanya opacity |
Anatomi
dialog#ds-confirm.modal[role=alertdialog]: dialog bersama, dari partial_DsConfirmDialogatau dibuat oleh script.[data-ds-confirm-title],[data-ds-confirm-text]: judul dan konsekuensi.[data-ds-confirm-cancel],[data-ds-confirm-accept]: dua aksi..ds-dialog__error[data-ds-confirm-error]: baris error, ditambahkan oleh script bila belum ada..ds-dialog__iconopsional denganis-danger,is-warning, atauis-primarypada markup tertulis.#ds-leave: dialog perubahan yang belum disimpan (_DsLeaveDialog), anatomi sama dengan Tetap di sini dan Tinggalkan.
Penggunaan
- Bertanya hanya sebelum tindakan yang tidak dapat dibatalkan atau yang menjangkau orang lain. Bertanya sebelum setiap penyimpanan melatih orang untuk mengklik tanpa membaca.
- Sebut objek di judul sebagai pertanyaan: "Hapus proyek PRJ-1024?", bukan "Apakah Anda yakin?".
- Nyatakan konsekuensi dalam satu kalimat dengan angka bila ada: "38 tugas dan 12 file di dalamnya ikut terhapus."
- Gunakan kata kerja yang sama pada aksi dan pemicu: pemicu "Hapus proyek", aksi "Hapus proyek". Jangan pernah "Ya" dan "Tidak".
- Pertahankan Batal sebagai fokus awal untuk tindakan destruktif. Gunakan
data-ds-confirm-focus="accept"hanya untuk konfirmasi yang aman dan sering. - Jangan tambahkan tombol tutup: dua aksi dan Escape adalah satu-satunya jalan keluar.
- Jangan susun alert dialog di dalam alert dialog lain.
Aksesibilitas
role="alertdialog"membuat screen reader mengumumkan judul dan deskripsi (aria-describedby) segera setelah dialog terbuka.- Fokus menuju Batal (atau Tetap di sini); fokus kembali ke pemicu setelah ditutup.
- Baris error memakai
role="alert", sehingga dibacakan saat muncul. - Warna tidak pernah menjadi satu-satunya sinyal: label aksi menyatakan efeknya.
| Tombol | Aksi |
|---|---|
| Tab / Shift+Tab | Berpindah antara Batal dan aksi |
| Enter / Space | Mengaktifkan tombol yang difokuskan |
| Escape | Batal (tidak saat aksi berjalan) |
Razor
@* Sekali per layout, sebelum </body>. *@
<partial name="_DsConfirmDialog" />
<partial name="_DsLeaveDialog" />
<form method="post" asp-page-handler="Delete" asp-route-id="@Model.Project.Id">
<button type="submit" class="btn btn-ghost is-danger"
data-ds-confirm="@($"Hapus proyek {Model.Project.Code}?")"
data-ds-confirm-text="Tugas dan file di dalamnya ikut terhapus. Tindakan ini tidak dapat dibatalkan."
data-ds-confirm-label="Hapus proyek">Hapus proyek</button>
</form>
@* Alert dialog tertulis dengan tag helper. *@
<ds-dialog id="revoke" alert size="sm" icon="alert-triangle" icon-tone="danger"
title="Cabut akses Mei Tanaka?" description="Ia keluar dari semua perangkat dalam satu menit.">
<ds-dialog-footer>
<button type="button" class="btn" data-ds-dialog-close="cancel">Batal</button>
<button type="submit" form="revoke-form" class="btn btn-error">Cabut akses</button>
</ds-dialog-footer>
</ds-dialog>JavaScript
| Atribut | Efek |
|---|---|
data-ds-confirm="Title" |
Bertanya sebelum klik diteruskan (form dikirim dengan submitter yang sama, tautan dinavigasi) |
data-ds-confirm-text |
Kalimat konsekuensi |
data-ds-confirm-label |
Label aksi (default "Delete" / "Hapus") |
data-ds-confirm-tone |
danger (default) atau primary |
data-ds-confirm-focus="accept" |
Fokus ke aksi, bukan Batal |
const ok = await DS.confirm({
title: "Keluarkan Sofia Marquez dari PRJ-1024?",
text: "Tugasnya tetap ditugaskan kepadanya sampai Anda menugaskan ulang.",
label: "Keluarkan",
tone: "danger", // atau "primary"
focus: "cancel", // atau "accept"
action: () => api.removeMember(1024, "sofia"), // opsional; reject untuk menampilkan error dan tetap terbuka
});Event: ds:confirmed pada pemicu yang bukan bagian dari form maupun tautan; ds:dialog:open dan ds:dialog:close pada dialog.
Token
--ds-scrim, --ds-radius-2xl, --ds-shadow-overlay, --ds-error-text, --ds-warning-text, --color-error,
--color-warning, --color-primary, --ds-subtle, --ds-duration-base, --ds-duration-exit, --ds-ease-emphasized,
--ds-ease-exit, --ds-type-headline-*, --ds-type-footnote-size.
Native dan daisyUI
<dialog>native denganshowModal()dan daisyUI.modal/.modal-box; lihat Dialog untuk perubahan yang dibuat DS.window.confirm()hanya menjadi fallback untuk penjaga perubahan yang belum disimpan saat#ds-leavetidak ada; semua yang lain memakai dialog bergaya.- Tombol aksi adalah daisyUI
btn btn-errordanbtn btn-primary; Batal adalahbtnbiasa.