Chapter pertama section ini menunjukkan bahwa buka-tutup konten bisa dibangun dengan HTML murni. Bila kebutuhannya tumbuh — animasi halus, banyak panel yang saling terkait, atau struktur markup tertentu — kita memerlukan pola yang lebih umum. Pola ini dikenal sebagai disclosure pattern dan didokumentasikan oleh WAI-ARIA Authoring Practices Guide (APG). Chapter ini membedah anatomi polanya, lalu menempatkan versi native dan versi custom berdampingan agar perbedaannya terlihat jelas.
Anatomi Pola Disclosure #
Disclosure adalah pola UI untuk membuka dan menutup sebuah region konten: satu kontrol pemicu, satu status, dan satu area yang muncul serta hilang. Menurut WAI-ARIA APG, pemicunya adalah button yang membawa aria-expanded, dan regionnya adalah elemen dengan id yang dirujuk lewat aria-controls. Karena pemicunya tombol asli, keyboard menangani Enter dan Space secara otomatis tanpa kode tambahan. Ketiga bagian itu dirangkum sebagai berikut:
- Kontrol:
buttondengan label yang mendeskripsikan isi regionnya. - Status:
aria-expanded="true"atau"false"pada tombol, diumumkan pembaca layar sebagai dibentangkan atau diciutkan. - Region: elemen konten dengan
id, dirujukaria-controls, dan disembunyikan lewat atributhidden.
Versi Native: details dan summary #
Pasangan details dan summary adalah implementasi disclosure pattern yang seluruh aturannya ditangani browser: status, klik, keyboard, dan pengumuman pembaca layar hadir tanpa satu atribut ARIA pun. Versi native menjadi pilihan pertama selama konten boleh menjadi anak langsung dari details. Batasannya ada di fleksibilitas: animasi pembukaan terbatas, styling strukturnya kaku, dan panel tidak bisa diletakkan terpisah dari pemicunya.
Versi Custom: Tombol, aria-expanded, dan Region #
Versi custom menukar keringanan itu dengan kendali. Kita memilihnya ketika konten perlu beranimasi lewat transisi CSS, ketika beberapa panel harus tersinkron seperti accordion, atau ketika struktur markup menuntut pemicu dan panel berjauhan. Harganya jelas: atribut hidden, aria-expanded, dan aria-controls harus dipasang serta diperbarui sendiri oleh JavaScript. Lupa memperbarui salah satunya membuat tombol mengaku keadaan yang salah.
| Aspek | details dan summary | Tombol + ARIA + JavaScript |
|---|---|---|
| Klik dan keyboard | Otomatis | Otomatis (tombol asli) |
| Status diumumkan | Otomatis | Perlu aria-expanded |
| Menyembunyikan region | Otomatis | Perlu atribut hidden |
| Animasi pembukaan | Terbatas | Bebas lewat transisi CSS |
| Penempatan panel | Harus di dalam details | Bebas di mana pun |
Contoh: Dua Versi Berdampingan #
Kita tulis kedua versi untuk konten yang sama agar perbedaannya terlihat langsung. Halaman bantuan berikut menyajikan versi native di kartu pertama dan versi custom di kartu kedua.
<!doctype html>
<html lang="id">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Disclosure: Native dan Custom</title>
<style>
body {
font-family: sans-serif;
max-width: 560px;
margin: 24px auto;
line-height: 1.6;
}
.kartu {
border: 1px solid #d1d5db;
border-radius: 8px;
padding: 12px 16px;
margin-bottom: 16px;
}
summary {
cursor: pointer;
font-weight: bold;
}
button {
padding: 6px 12px;
border-radius: 6px;
border: 1px solid #6b7280;
background: #f3f4f6;
cursor: pointer;
}
</style>
</head>
<body>
<h1>Cara Pakai Kupon</h1>
<div class="kartu">
<details>
<summary>Versi native: details dan summary</summary>
<p>Masukkan kode kupon di halaman pembayaran, lalu tekan Terapkan.</p>
</details>
</div>
<div class="kartu">
<button id="tombol-kupon" type="button" aria-expanded="false" aria-controls="panel-kupon">
Versi custom: tombol dan panel
</button>
<p id="panel-kupon" hidden>
Masukkan kode kupon di halaman pembayaran, lalu tekan Terapkan.
</p>
</div>
<script>
const tombol = document.querySelector("#tombol-kupon");
const panel = document.querySelector("#panel-kupon");
function alihkanPanel() {
if (panel.hidden) {
panel.hidden = false;
tombol.setAttribute("aria-expanded", "true");
} else {
panel.hidden = true;
tombol.setAttribute("aria-expanded", "false");
}
}
tombol.addEventListener("click", alihkanPanel);
</script>
</body>
</html>Output yang Dihasilkan:
Saat halaman baru dibuka:
- Kartu pertama menampilkan label "Versi native: details dan summary" dalam keadaan tertutup
- Kartu kedua menampilkan tombol "Versi custom: tombol dan panel" dengan panel tersembunyi
Salah satu label atau tombol ditekan:
- Kartu yang bersangkutan membuka paragraf "Masukkan kode kupon..."
- Tombol versi custom memperbarui aria-expanded menjadi true
Ditekan lagi:
- Keduanya menutup kembali dan aria-expanded kembali ke falseMari kita bedah isi halaman itu:
- Kartu pertama (
details): seluruh mekanisme berjalan tanpa atribut ARIA dan tanpa JavaScript. <button id="tombol-kupon">: pemicu versi custom; membawatype="button",aria-expanded="false"sebagai status awal, danaria-controls="panel-kupon"yang menunjuk id regionnya.<p id="panel-kupon" hidden>: region yang dirujuk; atributhiddenmengeluarkannya dari render sekaligus dari accessibility tree.- Function
alihkanPanel: cabangifmembacapanel.hidden, lalu menulis keadaan panel dan status tombol di tempat yang sama agar keduanya sinkron. addEventListener("click", alihkanPanel): menghubungkan tombol dengan fungsi; penekanan Enter maupun Space pada tombol memicu eventclickyang sama.
Perhatikan bahwa kedua versi menghasilkan perilaku yang sama; yang berbeda adalah siapa yang menangani detailnya. Mulailah dari versi native, lalu naik ke versi custom hanya ketika kebutuhan animasi atau sinkronisasi benar-benar muncul. Chapter berikutnya berpindah ke dua elemen yang menyampaikan status berupa angka, yaitu progress dan meter.
Referensi resmi untuk pendalaman:
- W3C WAI — ARIA Authoring Practices Guide: Disclosure Pattern
- MDN Web Docs — details
- MDN Web Docs — aria-expanded
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
disclosure-dua-versi.html
Unduh berkas<!doctype html>
<html lang="id">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Disclosure: Native dan Custom</title>
<style>
body {
font-family: sans-serif;
max-width: 560px;
margin: 24px auto;
line-height: 1.6;
}
.kartu {
border: 1px solid #d1d5db;
border-radius: 8px;
padding: 12px 16px;
margin-bottom: 16px;
}
summary {
cursor: pointer;
font-weight: bold;
}
button {
padding: 6px 12px;
border-radius: 6px;
border: 1px solid #6b7280;
background: #f3f4f6;
cursor: pointer;
}
</style>
</head>
<body>
<h1>Cara Pakai Kupon</h1>
<div class="kartu">
<details>
<summary>Versi native: details dan summary</summary>
<p>Masukkan kode kupon di halaman pembayaran, lalu tekan Terapkan.</p>
</details>
</div>
<div class="kartu">
<button id="tombol-kupon" type="button" aria-expanded="false" aria-controls="panel-kupon">
Versi custom: tombol dan panel
</button>
<p id="panel-kupon" hidden>
Masukkan kode kupon di halaman pembayaran, lalu tekan Terapkan.
</p>
</div>
<script>
const tombol = document.querySelector("#tombol-kupon");
const panel = document.querySelector("#panel-kupon");
function alihkanPanel() {
if (panel.hidden) {
panel.hidden = false;
tombol.setAttribute("aria-expanded", "true");
} else {
panel.hidden = true;
tombol.setAttribute("aria-expanded", "false");
}
}
tombol.addEventListener("click", alihkanPanel);
</script>
</body>
</html>