Membangun Modal/Popup Kustom yang Interaktif dan Aksesibel dengan HTML, CSS, dan Vanilla JS
Selamat datang di tutorial pembuatan modal kustom! Modal (atau popup) adalah jendela dialog yang muncul di atas konten halaman utama, biasanya untuk meminta input pengguna, menampilkan informasi penting, atau konfirmasi suatu tindakan. Membuat modal sendiri dari nol adalah cara terbaik untuk memahami cara kerjanya dan memastikan sesuai dengan kebutuhan desain Anda.
Mengapa Membuat Modal Kustom?
- Kontrol Penuh: Anda menentukan setiap aspek tampilan dan perilakunya.
- Ringan: Tidak ada dependensi atau kode berlebih dari library pihak ketiga.
- Pembelajaran Aksesibilitas: Kesempatan bagus untuk mempraktikkan standar aksesibilitas web (a11y) untuk komponen interaktif.
Apa yang Akan Kita Buat?
Kita akan membuat modal serbaguna dengan fitur berikut:
- Struktur HTML yang jelas untuk modal dan pemicunya.
- Tombol Pemicu (Trigger) untuk membuka modal.
- Overlay Gelap di belakang modal untuk memfokuskan perhatian.
- Konten Modal yang bisa disesuaikan (judul, isi, tombol aksi).
- Tombol Tutup (Close Button) di dalam modal.
- Fungsionalitas Menutup Modal dengan:
- Mengklik tombol tutup.
- Mengklik area overlay.
- Menekan tombol
Escape
pada keyboard.
- Transisi CSS yang halus untuk efek muncul dan hilang.
- Logika JavaScript murni untuk mengelola state dan interaksi.
- Pertimbangan Aksesibilitas Dasar: Fokus keyboard dan atribut ARIA.
Prasyarat
- Text Editor: VS Code, Sublime Text, dll.
- Web Browser: Chrome, Firefox, dll.
- Pemahaman Dasar HTML dan CSS.
- Pengetahuan Dasar JavaScript: Variabel, fungsi, event listener, manipulasi DOM (kelas, atribut).
- (Opsional) Ikon: Font Awesome untuk ikon tombol tutup.
Mari kita mulai!
Langkah 1: Persiapan Proyek dan Struktur Folder
- Buat folder utama proyek, misalnya
proyek-modal-kustom
. - Di dalamnya, buat:
index.html
style.css
script.js
Struktur dasar:
proyek-modal-kustom/
├── index.html
├── style.css
└── script.js
Langkah 2: Struktur Dasar HTML (index.html
)
Buka index.html
. Kita akan membuat tombol pemicu dan struktur untuk modal itu sendiri.
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Modal Kustom - Vanilla JS</title>
<link rel="stylesheet" href="style.css">
<!-- Font Awesome untuk ikon (opsional) -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
</head>
<body>
<header>
<h1>Contoh Halaman dengan Modal</h1>
</header>
<main>
<p>Ini adalah konten utama halaman. Klik tombol di bawah untuk membuka modal.</p>
<button class="open-modal-btn" data-modal-target="#myModal1">Buka Modal Informasi</button>
<button class="open-modal-btn" data-modal-target="#myModal2">Buka Modal Konfirmasi</button>
<!-- Konten lain di halaman -->
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</main>
<!-- Struktur Modal 1: Informasi -->
<div class="modal" id="myModal1" role="dialog" aria-modal="true" aria-labelledby="modal1Title" hidden>
<div class="modal-overlay" data-modal-close></div>
<div class="modal-content">
<button class="modal-close-btn" data-modal-close aria-label="Tutup modal">
<i class="fas fa-times"></i> <!-- Atau gunakan karakter '×' -->
</button>
<div class="modal-header">
<h2 id="modal1Title">Informasi Penting</h2>
</div>
<div class="modal-body">
<p>Ini adalah isi dari modal informasi. Anda bisa meletakkan teks, gambar, atau formulir di sini.</p>
<p>Pastikan informasi yang ditampilkan jelas dan mudah dipahami.</p>
</div>
<div class="modal-footer">
<button class="btn btn-primary" data-modal-close>Mengerti</button>
</div>
</div>
</div>
<!-- Struktur Modal 2: Konfirmasi (contoh lain) -->
<div class="modal" id="myModal2" role="dialog" aria-modal="true" aria-labelledby="modal2Title" hidden>
<div class="modal-overlay" data-modal-close></div>
<div class="modal-content modal-sm"> <!-- Tambah kelas untuk ukuran berbeda -->
<button class="modal-close-btn" data-modal-close aria-label="Tutup modal">×</button>
<div class="modal-header">
<h2 id="modal2Title">Konfirmasi Tindakan</h2>
</div>
<div class="modal-body">
<p>Apakah Anda yakin ingin melanjutkan tindakan ini? Tindakan ini tidak dapat dibatalkan.</p>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" data-modal-close>Batal</button>
<button class="btn btn-danger">Ya, Lanjutkan</button>
</div>
</div>
</div>
<footer>
<p>© 2025 Halaman Modal Kustom</p>
</footer>
<script src="script.js"></script>
</body>
</html>
Penjelasan HTML:
- Tombol Pemicu (
.open-modal-btn
):data-modal-target="#myModal1"
: Atribut data kustom untuk menghubungkan tombol ini dengan modal spesifik yang memiliki IDmyModal1
. Ini memungkinkan kita memiliki banyak modal dan banyak pemicu.
- Struktur Modal (
.modal
):id="myModal1"
: ID unik untuk setiap modal.role="dialog"
danaria-modal="true"
: Atribut ARIA penting untuk aksesibilitas, memberitahu teknologi pendukung bahwa ini adalah dialog modal.aria-labelledby="modal1Title"
: Menghubungkan modal dengan judulnya untuk aksesibilitas.hidden
: Atribut HTML5 untuk menyembunyikan modal secara default (akan dikontrol oleh JS dan CSS).
- Overlay (
.modal-overlay
): Elemen yang menutupi sisa halaman.data-modal-close
adalah atribut data yang akan kita gunakan di JS untuk menandai elemen yang bisa menutup modal saat diklik. - Konten Modal (
.modal-content
): Wrapper untuk isi sebenarnya dari modal..modal-close-btn
: Tombol untuk menutup modal. Juga memilikidata-modal-close
.aria-label
penting jika tombol hanya berisi ikon..modal-header
,.modal-body
,.modal-footer
: Struktur umum untuk konten modal.
- Kita membuat dua contoh modal (
#myModal1
dan#myModal2
) untuk menunjukkan fleksibilitas.
Langkah 3: Styling Dasar CSS (style.css
)
Buka style.css
. Kita akan mengatur tampilan awal modal (tersembunyi), overlay, dan konten modal.
/* style.css */
body {
margin: 0;
font-family: Arial, Helvetica, sans-serif;
line-height: 1.6;
color: #333;
}
header, main, footer {
padding: 20px;
max-width: 800px;
margin: 0 auto;
}
.open-modal-btn, .btn {
padding: 10px 20px;
font-size: 1rem;
cursor: pointer;
border: none;
border-radius: 5px;
margin-right: 10px;
}
.open-modal-btn {
background-color: #007bff;
color: white;
}
.open-modal-btn:hover {
background-color: #0056b3;
}
.btn-primary { background-color: #007bff; color: white; }
.btn-primary:hover { background-color: #0056b3; }
.btn-secondary { background-color: #6c757d; color: white; }
.btn-secondary:hover { background-color: #5a6268; }
.btn-danger { background-color: #dc3545; color: white; }
.btn-danger:hover { background-color: #c82333; }
/* Styling Modal */
.modal {
position: fixed; /* Tetap di layar saat scroll */
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex; /* Untuk menengahkan modal-content */
align-items: center;
justify-content: center;
/* Awalnya tersembunyi, akan diubah oleh JS dan CSS transisi */
visibility: hidden;
opacity: 0;
transition: opacity 0.3s ease, visibility 0s linear 0.3s; /* Transisi untuk fade */
z-index: 1000; /* Di atas segalanya */
}
.modal.is-open {
visibility: visible;
opacity: 1;
transition-delay: 0s; /* Hapus delay saat membuka */
}
.modal-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.6); /* Warna overlay gelap transparan */
cursor: pointer; /* Menunjukkan bisa diklik untuk menutup */
}
.modal-content {
position: relative; /* Agar tombol close bisa diposisikan absolut terhadap ini */
background-color: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
width: 90%;
max-width: 600px; /* Lebar maksimum modal */
z-index: 1001; /* Di atas overlay */
transform: translateY(-50px) scale(0.95); /* Efek awal sebelum muncul */
opacity: 0; /* Awalnya transparan */
transition: transform 0.3s ease-out, opacity 0.3s ease-out;
}
.modal.is-open .modal-content {
transform: translateY(0) scale(1); /* Efek muncul */
opacity: 1;
}
/* Ukuran modal berbeda (opsional) */
.modal-content.modal-sm {
max-width: 400px;
}
.modal-content.modal-lg {
max-width: 800px;
}
.modal-close-btn {
position: absolute;
top: 15px;
right: 15px;
background: none;
border: none;
font-size: 1.5rem; /* Ukuran ikon close */
color: #888;
cursor: pointer;
padding: 0;
line-height: 1; /* Agar ikon tidak punya spasi ekstra */
}
.modal-close-btn:hover {
color: #333;
}
.modal-header {
border-bottom: 1px solid #eee;
padding-bottom: 15px;
margin-bottom: 20px;
}
.modal-header h2 {
margin: 0;
font-size: 1.5rem;
}
.modal-body {
margin-bottom: 20px;
}
.modal-body p:last-child {
margin-bottom: 0;
}
.modal-footer {
border-top: 1px solid #eee;
padding-top: 15px;
text-align: right; /* Tombol footer di kanan */
}
.modal-footer .btn {
margin-left: 10px;
}
.modal-footer .btn:first-child {
margin-left: 0;
}
Penjelasan CSS Awal:
.modal
:position: fixed
agar menutupi seluruh viewport dan tetap saat di-scroll.display: flex
,align-items: center
,justify-content: center
untuk menengahkan.modal-content
.- Awalnya
visibility: hidden
danopacity: 0
. Transisi diatur agarvisibility
berubah setelahopacity
(untuk mencegah interaksi dengan elemen tak terlihat).
.modal.is-open
: Kelas yang akan ditambahkan oleh JavaScript untuk menampilkan modal. Properti transisi akan bekerja di sini..modal-overlay
: Menutupi seluruh layar di belakang konten modal..modal-content
:position: relative
untuk positioning tombol close.transform
danopacity
diatur untuk efek animasi muncul (misalnya, sedikit dari atas dan membesar).
.modal.is-open .modal-content
: Mengembalikantransform
danopacity
ke nilai normal saat modal terbuka.
Langkah 4: JavaScript - Logika Membuka dan Menutup Modal
Buka script.js
. Kita akan menulis logika untuk mengontrol visibilitas modal.
// script.js
document.addEventListener('DOMContentLoaded', () => {
const openModalButtons = document.querySelectorAll('[data-modal-target]');
const closeModalButtons = document.querySelectorAll('[data-modal-close]'); // Termasuk overlay dan tombol close
let previouslyFocusedElement = null; // Untuk menyimpan elemen yang fokus sebelum modal dibuka
// Fungsi untuk membuka modal
function openModal(modal) {
if (modal == null) return;
// Simpan elemen yang sedang fokus
previouslyFocusedElement = document.activeElement;
modal.removeAttribute('hidden'); // Hapus atribut hidden dulu
// Beri sedikit waktu agar display berubah sebelum memulai transisi CSS
requestAnimationFrame(() => {
modal.classList.add('is-open');
// Fokuskan elemen pertama yang bisa difokus di dalam modal
const focusableElements = modal.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
if (focusableElements.length > 0) {
focusableElements[0].focus();
} else {
modal.focus(); // Jika tidak ada, fokuskan modal itu sendiri (perlu tabindex="-1" di modal)
}
});
document.body.style.overflow = 'hidden'; // Mencegah scroll di body saat modal terbuka
}
// Fungsi untuk menutup modal
function closeModal(modal) {
if (modal == null) return;
modal.classList.remove('is-open');
// Kembalikan fokus ke elemen sebelumnya
if (previouslyFocusedElement) {
previouslyFocusedElement.focus();
previouslyFocusedElement = null;
}
// Tunggu transisi selesai sebelum menambahkan atribut 'hidden' lagi
// Durasi transisi opacity modal adalah 0.3s (300ms)
modal.addEventListener('transitionend', function handler() {
modal.setAttribute('hidden', true);
modal.removeEventListener('transitionend', handler); // Hapus listener setelah selesai
}, { once: true }); // Opsi {once: true} juga bisa, tapi beberapa browser lama mungkin tidak support
document.body.style.overflow = ''; // Kembalikan scroll body
}
// Event listener untuk tombol-tombol pembuka modal
openModalButtons.forEach(button => {
button.addEventListener('click', () => {
const modalId = button.dataset.modalTarget;
const modal = document.querySelector(modalId);
if (modal) {
openModal(modal);
}
});
});
// Event listener untuk elemen penutup modal (overlay, tombol close)
closeModalButtons.forEach(element => {
element.addEventListener('click', () => {
// Cari elemen modal terdekat yang menampung tombol/overlay ini
const modal = element.closest('.modal');
if (modal) {
closeModal(modal);
}
});
});
// Event listener untuk tombol Escape (Esc)
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape' || event.key === 'Esc') {
// Cari modal yang sedang terbuka
const openModals = document.querySelectorAll('.modal.is-open');
openModals.forEach(modal => {
closeModal(modal);
});
}
});
// (Opsional) Menutup modal jika diklik di luar modal-content (hanya jika klik di overlay)
// Ini sudah di-handle oleh data-modal-close pada .modal-overlay
// Jika ingin lebih spesifik:
// document.querySelectorAll('.modal').forEach(modal => {
// modal.addEventListener('click', (event) => {
// if (event.target === modal) { // Hanya jika klik langsung pada .modal (overlay)
// closeModal(modal);
// }
// });
// });
});
Penjelasan JavaScript:
- Seleksi Elemen: Mengambil semua tombol pemicu (
data-modal-target
) dan semua elemen yang bisa menutup modal (data-modal-close
). previouslyFocusedElement
: Variabel untuk menyimpan elemen yang memiliki fokus sebelum modal dibuka. Ini penting untuk mengembalikan fokus saat modal ditutup (baik untuk aksesibilitas).openModal(modal)
:- Menghapus atribut
hidden
. - Menggunakan
requestAnimationFrame
untuk memastikan browser telah memproses perubahan display sebelum menambahkan kelasis-open
(untuk transisi CSS yang benar). - Menambahkan kelas
is-open
untuk memicu transisi CSS. - Fokus Manajemen: Mencari elemen pertama yang bisa difokuskan di dalam modal dan memberinya fokus. Jika tidak ada, modal itu sendiri bisa difokuskan (memerlukan
tabindex="-1"
pada elemen.modal
di HTML). document.body.style.overflow = 'hidden'
: Mencegah konten halaman di belakang modal di-scroll.
- Menghapus atribut
closeModal(modal)
:- Menghapus kelas
is-open
. - Mengembalikan Fokus: Memberikan fokus kembali ke
previouslyFocusedElement
. - Menunggu transisi CSS selesai (berdasarkan durasi transisi opacity modal) sebelum menambahkan kembali atribut
hidden
. Ini penting agar animasi fade-out terlihat. document.body.style.overflow = ''
: Mengembalikan kemampuan scroll pada body.
- Menghapus kelas
- Event Listeners:
- Untuk setiap
openModalButton
: Mengambil target modal daridata-modal-target
dan memanggilopenModal
. - Untuk setiap
closeModalButton
(termasuk overlay): Menggunakanelement.closest('.modal')
untuk menemukan modal induk dan memanggilcloseModal
. - Untuk tombol
Escape
: Mencari semua modal yang sedang terbuka dan menutupnya.
- Untuk setiap
Langkah 5: Meningkatkan Aksesibilitas (Focus Trapping)
Salah satu aspek penting aksesibilitas modal adalah “focus trapping”. Artinya, saat modal terbuka, pengguna tidak bisa menekan tombol Tab
untuk berinteraksi dengan elemen di belakang modal. Fokus harus tetap terperangkap di dalam modal.
Ini sedikit lebih kompleks dan bisa ditambahkan sebagai peningkatan. Berikut adalah implementasi dasar focus trapping:
Tambahkan fungsi ini dan panggil di dalam openModal
dan closeModal
:
// script.js (lanjutan, di dalam DOMContentLoaded)
// ... (semua kode sebelumnya) ...
// --- Focus Trapping ---
let currentOpenModal = null; // Menyimpan referensi modal yang sedang terbuka
function trapFocus(modal) {
const focusableElementsString = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
const focusableElements = Array.from(modal.querySelectorAll(focusableElementsString));
if (focusableElements.length === 0) return;
const firstFocusableElement = focusableElements[0];
const lastFocusableElement = focusableElements[focusableElements.length - 1];
// Fokuskan elemen pertama saat modal dibuka (sudah ada di openModal)
// firstFocusableElement.focus();
const handleTabKeyPress = (event) => {
if (event.key === 'Tab') {
if (event.shiftKey) { // Shift + Tab
if (document.activeElement === firstFocusableElement) {
event.preventDefault();
lastFocusableElement.focus();
}
} else { // Tab
if (document.activeElement === lastFocusableElement) {
event.preventDefault();
firstFocusableElement.focus();
}
}
}
};
modal.addEventListener('keydown', handleTabKeyPress);
// Simpan referensi fungsi agar bisa dihapus saat modal ditutup
modal._handleTabKeyPress = handleTabKeyPress;
}
function releaseFocus(modal) {
if (modal && modal._handleTabKeyPress) {
modal.removeEventListener('keydown', modal._handleTabKeyPress);
delete modal._handleTabKeyPress;
}
}
// Modifikasi fungsi openModal dan closeModal
// Di dalam fungsi openModal(modal):
// Setelah: modal.classList.add('is-open');
// Tambahkan:
// currentOpenModal = modal;
// trapFocus(modal);
// // Fokuskan elemen pertama (sudah ada)
// Di dalam fungsi closeModal(modal):
// Sebelum: modal.classList.remove('is-open');
// Tambahkan:
// releaseFocus(modal);
// currentOpenModal = null;
Pembaruan openModal
dan closeModal
dengan Focus Trapping:
// script.js
document.addEventListener('DOMContentLoaded', () => {
const openModalButtons = document.querySelectorAll('[data-modal-target]');
const closeModalButtons = document.querySelectorAll('[data-modal-close]');
let previouslyFocusedElement = null;
let currentOpenModal = null; // Untuk focus trapping
// --- Focus Trapping Functions ---
function trapFocus(modal) {
const focusableElementsString = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
const focusableElements = Array.from(modal.querySelectorAll(focusableElementsString));
if (focusableElements.length === 0) return;
const firstFocusableElement = focusableElements[0];
const lastFocusableElement = focusableElements[focusableElements.length - 1];
const handleTabKeyPress = (event) => {
if (!modal.classList.contains('is-open')) return; // Hanya jika modal terbuka
if (event.key === 'Tab') {
// Jika tidak ada elemen yang bisa difokus atau hanya satu, jangan lakukan apa-apa
if (focusableElements.length <= 1) {
event.preventDefault();
return;
}
if (event.shiftKey) { // Shift + Tab
if (document.activeElement === firstFocusableElement) {
event.preventDefault();
lastFocusableElement.focus();
}
} else { // Tab
if (document.activeElement === lastFocusableElement) {
event.preventDefault();
firstFocusableElement.focus();
}
}
}
};
// Gunakan document untuk event keydown agar bisa menangkap tab dari luar modal juga
// dan mencegahnya jika modal terbuka
document.addEventListener('keydown', handleTabKeyPress);
modal._handleTabKeyPress = handleTabKeyPress; // Simpan referensi untuk remove
}
function releaseFocus(modal) {
if (modal && modal._handleTabKeyPress) {
document.removeEventListener('keydown', modal._handleTabKeyPress);
delete modal._handleTabKeyPress;
}
}
// Fungsi untuk membuka modal
function openModal(modal) {
if (modal == null || modal.classList.contains('is-open')) return;
previouslyFocusedElement = document.activeElement;
currentOpenModal = modal; // Set modal yang aktif
modal.removeAttribute('hidden');
requestAnimationFrame(() => {
modal.classList.add('is-open');
trapFocus(modal); // Mulai focus trapping
const focusableElements = modal.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
if (focusableElements.length > 0) {
focusableElements[0].focus();
} else {
// Beri modal tabindex agar bisa difokuskan jika tidak ada elemen lain
modal.setAttribute('tabindex', '-1');
modal.focus();
}
});
document.body.style.overflow = 'hidden';
}
// Fungsi untuk menutup modal
function closeModal(modal) {
if (modal == null || !modal.classList.contains('is-open')) return;
releaseFocus(modal); // Hentikan focus trapping
currentOpenModal = null; // Reset modal yang aktif
modal.classList.remove('is-open');
if (previouslyFocusedElement) {
previouslyFocusedElement.focus();
previouslyFocusedElement = null;
}
modal.addEventListener('transitionend', function handler() {
modal.setAttribute('hidden', true);
if (!modal.classList.contains('is-open') && modal.hasAttribute('tabindex')) {
modal.removeAttribute('tabindex'); // Hapus tabindex jika ditambahkan
}
modal.removeEventListener('transitionend', handler);
}, { once: true });
document.body.style.overflow = '';
}
// Event listener untuk tombol-tombol pembuka modal
openModalButtons.forEach(button => {
button.addEventListener('click', () => {
const modalId = button.dataset.modalTarget;
const modal = document.querySelector(modalId);
if (modal) {
openModal(modal);
}
});
});
// Event listener untuk elemen penutup modal (overlay, tombol close)
closeModalButtons.forEach(element => {
element.addEventListener('click', () => {
// Cari elemen modal terdekat yang menampung tombol/overlay ini
const modal = element.closest('.modal');
if (modal) {
closeModal(modal);
}
});
});
// Event listener untuk tombol Escape (Esc)
document.addEventListener('keydown', (event) => {
if ((event.key === 'Escape' || event.key === 'Esc') && currentOpenModal) {
closeModal(currentOpenModal); // Tutup modal yang sedang aktif
}
});
});
Perubahan Penting untuk Focus Trapping:
trapFocus
sekarang menambahkan event listenerkeydown
kedocument
agar bisa menangkap eventTab
bahkan jika fokus mencoba keluar dari modal.- Logika di dalam
handleTabKeyPress
memeriksa apakah modal sedang terbuka sebelum melakukan trapping. - Pastikan
releaseFocus
benar-benar menghapus listener daridocument
. - Jika tidak ada elemen yang bisa difokus di modal, kita tambahkan
tabindex="-1"
ke modal itu sendiri agar bisa difokuskan, dan menghapusnya saat modal ditutup.
Langkah 6: Menguji Modal Anda
Simpan semua file dan buka index.html
di browser.
- Klik tombol “Buka Modal”. Modal seharusnya muncul dengan efek transisi.
- Overlay gelap seharusnya menutupi halaman.
- Coba tutup modal dengan:
- Tombol “X” atau tombol close di footer.
- Mengklik area overlay.
- Menekan tombol
Escape
.
- Perhatikan animasi saat membuka dan menutup.
- Uji Aksesibilitas:
- Saat modal terbuka, coba tekan
Tab
. Fokus seharusnya berpindah antar elemen yang bisa difokus di dalam modal saja (tombol close, input, tombol footer), dan tidak ke elemen di belakang modal. - Tekan
Shift + Tab
untuk bergerak mundur. - Saat modal ditutup, fokus seharusnya kembali ke tombol yang tadi Anda klik untuk membuka modal.
- Saat modal terbuka, coba tekan
Kesimpulan dan Langkah Selanjutnya
Selamat! Anda telah berhasil membuat komponen modal kustom yang fungsional, responsif, dan mempertimbangkan aspek aksesibilitas dasar. Ini adalah keterampilan penting untuk developer front-end.
Apa yang telah dipelajari:
- Struktur HTML semantik untuk modal dan atribut ARIA.
- Styling CSS untuk overlay, konten modal, dan transisi animasi.
- Logika JavaScript untuk mengelola state buka/tutup.
- Menangani berbagai cara menutup modal (tombol, overlay, Esc).
- Manajemen fokus untuk pengalaman pengguna dan aksesibilitas yang lebih baik.
- Implementasi dasar focus trapping.
Langkah Selanjutnya yang Bisa Anda Jelajahi:
- Modal Bertingkat (Nested Modals): Bagaimana jika satu modal membuka modal lain? Perlu penanganan state dan fokus yang lebih cermat.
- Konten Dinamis: Memuat konten modal menggunakan AJAX/Fetch dari server.
- Animasi Lebih Kompleks: Eksplorasi efek animasi yang berbeda dengan CSS.
- Integrasi dengan Framework: Menerapkan konsep serupa dalam framework seperti React, Vue, atau Angular, yang biasanya memiliki solusi bawaan atau pola untuk komponen modal.
- Validasi Form di Dalam Modal: Jika modal berisi formulir.
- Pengujian Aksesibilitas Lebih Lanjut: Gunakan screen reader untuk menguji pengalaman pengguna dengan modal Anda.
Teruslah berlatih dan membangun komponen UI kustom. Ini akan sangat meningkatkan pemahaman Anda tentang cara kerja web. Semoga berhasil!