Materi 1 · Modul 0
Kenalan sama web: HTML, CSS, dan browser
Sebelum menulis satu tag pun, pahami dulu tiga lapisan yang membangun setiap halaman web.
Tiga bahasa, tiga tugas berbeda
| Bahasa | Tugasnya |
|---|---|
| HTML | Struktur dan isi — apa yang ada di halaman (judul, paragraf, gambar, tombol). |
| CSS | Tampilan — bagaimana rupanya (warna, ukuran, jarak, tata letak). |
| JavaScript | Perilaku — apa yang terjadi saat berinteraksi (sudah dibahas di handbook terpisah). |
Analogi yang sering dipakai: HTML seperti kerangka dan isi sebuah rumah (dinding, pintu, kamar), CSS seperti cat, furnitur, dan dekorasinya, JavaScript seperti listrik dan mekanisme yang membuat pintu bisa dibuka otomatis. Ketiganya berdiri sendiri, tapi bekerja sama membentuk satu halaman.
Bagaimana browser membaca halaman
Catatan — Browser membaca file HTML dari atas ke bawah, membangun struktur halaman (disebut DOM — sudah dibahas mendalam di handbook JavaScript Materi 11), lalu menerapkan aturan CSS ke struktur itu untuk menentukan tampilan akhirnya. HTML menjawab "elemen apa saja yang ada dan bagaimana urutannya", CSS menjawab "bagaimana semua itu seharusnya terlihat".
Alat yang dibutuhkan
- Editor kode — VS Code gratis dan paling umum dipakai.
- Browser — Chrome, Firefox, atau apa pun, plus DevTools-nya (tekan
F12) untuk memeriksa elemen. - Tidak butuh server, tidak butuh instalasi rumit — file
.htmlbisa langsung dibuka di browser.
File HTML pertama
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Halaman Pertama</title>
</head>
<body>
<h1>Halo, Dunia!</h1>
<p>Ini halaman web pertamaku.</p>
</body>
</html>Simpan file ini dengan ekstensi .html, lalu buka langsung di browser (klik dua kali, atau drag ke jendela browser). Tidak perlu php -S atau server apa pun untuk HTML/CSS murni — browser bisa membaca file lokal langsung.
Membedah anatomi di atas
<!DOCTYPE html>— memberi tahu browser "ini dokumen HTML modern", wajib di baris pertama.<html lang="id">— elemen pembungkus seluruh halaman;lang="id"menandai bahasa Indonesia (membantu pembaca layar dan mesin pencari).<head>— informasi tentang halaman, tidak tampil langsung di layar (judul tab, meta data, link CSS).<body>— semua yang tampil di layar.
Catatan — Hampir semua elemen HTML punya pasangan tag pembuka dan penutup: <h1>...</h1>. Tag penutup punya garis miring /. Beberapa elemen "self-closing" tanpa isi (seperti <meta>) tidak butuh tag penutup — dibahas lebih lanjut di Materi 2.
Yang akan kamu pelajari
- Modul 0 — elemen dasar HTML: teks, list, link, gambar, tabel, form.
- Modul 1 — HTML semantik, aksesibilitas, dan SEO dasar.
- Modul 2 — fondasi CSS: selector, box model, typography.
- Modul 3 — layout modern: Flexbox, Grid, responsive design.
- Modul 4 — CSS lanjutan: animasi, custom property, dan praktik produksi.
Setelah handbook ini, fondasi yang kamu bangun langsung nyambung ke handbook JavaScript (yang memanipulasi elemen-elemen ini) dan seluruh seri backend (PHP, Laravel) yang menghasilkan HTML ini secara dinamis.
Materi 2 · Modul 0
Struktur dokumen dan elemen dasar
Aturan dasar menulis HTML yang valid — tag, atribut, dan cara menyarangkan elemen dengan benar.
Anatomi sebuah elemen
<p class="intro">Ini sebuah paragraf.</p>
<!-- ↑ ↑ ↑ ↑
tag buka atribut isi tag tutup -->Atribut memberi informasi tambahan pada elemen — selalu di dalam tag pembuka, berbentuk nama="nilai". Satu elemen boleh punya banyak atribut, dipisah spasi.
Elemen self-closing (tanpa isi)
<img src="foto.jpg" alt="Deskripsi foto">
<br> <!-- ganti baris -->
<hr> <!-- garis horizontal pemisah -->
<input type="text">Elemen seperti <img>, <br>, <hr>, dan <input> tidak punya "isi" di antara tag — mereka berdiri sendiri, tanpa tag penutup.
Menyarangkan elemen (nesting) dengan benar
<div>
<p>Teks dengan <strong>penekanan</strong> di dalamnya.</p>
</div>
<!-- elemen yang dibuka TERAKHIR harus ditutup PERTAMA --><p>Teks dengan <strong>penekanan</p></strong>
<!-- <strong> dibuka di dalam <p>, tapi ditutup DI LUAR <p> — urutan tertukar -->Hati-hati — Browser modern cukup "memaafkan" HTML yang berantakan (tetap mencoba menampilkannya semampu mungkin), tapi tag yang bersarang tidak rapi bisa menghasilkan tampilan yang tidak terduga, terutama saat digabung dengan CSS kompleks nanti. Biasakan menulis tag penutup dalam urutan terbalik dari tag pembuka — ini akan sangat membantu saat debugging tampilan yang aneh.
Komentar HTML
<!-- Ini komentar, tidak tampil di halaman -->
<p>Teks ini tetap tampil.</p>
<!--
Komentar bisa beberapa baris
seperti ini
-->Elemen heading dan paragraf
<h1>Judul Utama (hanya SATU per halaman)</h1>
<h2>Sub Judul</h2>
<h3>Sub-sub Judul</h3>
<!-- ... sampai h6, urutan tingkatan penting untuk aksesibilitas (Materi 7) -->
<p>Ini sebuah paragraf teks biasa.</p>Catatan — Heading (h1–h6) bukan sekadar "teks besar" — mereka menandai struktur dan hierarki isi halaman. Mesin pencari dan pembaca layar untuk pengguna tunanetra memakai urutan heading ini untuk memahami kerangka halaman. Jangan melompati tingkatan (dari h1 langsung ke h4) hanya demi ukuran teks — untuk mengubah ukuran, itu tugas CSS (Modul 2), bukan memilih tag heading yang salah.
Latihan — Buat halaman HTML dengan satu <h1>, dua <h2>, dan beberapa <p> di bawah tiap <h2>. Sisipkan komentar HTML yang menjelaskan tiap bagian. Buka di browser dan pastikan strukturnya rapi.
Materi 3 · Modul 0
Teks, list, dan link
Elemen yang akan paling sering kamu pakai — menyusun dan menghubungkan konten.
Elemen teks inline
<p>
Kata ini <strong>sangat penting</strong>,
dan ini <em>ditekankan</em>.
Ini <code>kode inline</code>,
dan ini <mark>disorot</mark>.
<br>
Baris baru di sini.
</p>| Tag | Makna |
|---|---|
<strong> | Kepentingan tinggi (biasanya tampil tebal, tapi maknanya semantik, bukan cuma visual). |
<em> | Penekanan (biasanya miring). |
<code> | Potongan kode atau istilah teknis. |
<mark> | Teks yang disorot/highlight. |
<span> | Pembungkus generik tanpa makna khusus — dipakai untuk menarget CSS (Modul 2). |
Catatan — <strong> dan <b> (begitu juga <em> dan <i>) bisa terlihat identik secara visual, tapi beda makna: <strong>/<em> membawa arti "ini penting/ditekankan", sedangkan <b>/<i> cuma "buat tebal/miring" tanpa makna. Pilih berdasarkan maksud, bukan tampilan — tampilan selalu bisa diubah lewat CSS nanti.
List: unordered, ordered, dan description
<!-- unordered list — bullet point, urutan tidak penting -->
<ul>
<li>Kaos</li>
<li>Hoodie</li>
<li>Topi</li>
</ul>
<!-- ordered list — bernomor, urutan PENTING -->
<ol>
<li>Isi keranjang</li>
<li>Checkout</li>
<li>Bayar</li>
</ol>
<!-- description list — pasangan istilah dan penjelasan -->
<dl>
<dt>HTML</dt>
<dd>Bahasa struktur halaman web.</dd>
<dt>CSS</dt>
<dd>Bahasa untuk mengatur tampilan.</dd>
</dl>List bisa disarangkan (list di dalam <li> dari list lain) untuk membuat sub-list — berguna untuk menu navigasi bertingkat atau daftar isi berjenjang.
Link
<!-- link ke halaman lain (eksternal) -->
<a href="https://onlenkan.com">Kunjungi Onlenkan</a>
<!-- link ke halaman lain dalam situs yang sama (relatif) -->
<a href="tentang.html">Tentang Kami</a>
<a href="/produk/kaos">Lihat Kaos</a>
<!-- link ke bagian di HALAMAN YANG SAMA -->
<a href="#kontak">Lompat ke Kontak</a>
<h2 id="kontak">Kontak</h2>
<!-- buka di tab baru -->
<a href="https://google.com" target="_blank" rel="noopener">Google</a>
<!-- link email dan telepon -->
<a href="mailto:halo@onlenkan.com">Kirim Email</a>
<a href="tel:+6281234567890">Telepon Kami</a>Hati-hati — Saat memakai target="_blank" (buka tab baru), selalu tambahkan rel="noopener". Tanpa itu, halaman yang baru dibuka punya akses terbatas ke halaman asalmu lewat JavaScript (celah keamanan kecil tapi nyata, dikenal sebagai tabnabbing) — kebiasaan baik untuk dibawa sejak awal.
Latihan — Buat halaman "Profil" dengan: daftar unordered berisi 3 hobi, daftar ordered berisi 3 langkah suatu proses, dan tiga link berbeda (satu ke website lain dengan target="_blank" yang benar, satu email, satu lompat ke bagian di halaman yang sama).
Materi 4 · Modul 0
Gambar, tabel, dan media
Menampilkan konten visual dan data terstruktur.
Gambar
<img src="produk/kaos.jpg" alt="Kaos polos warna hitam">
<!-- ukuran eksplisit — mencegah "lompatan" layout saat gambar sedang dimuat -->
<img src="produk/kaos.jpg" alt="Kaos polos" width="400" height="400">
<!-- lazy loading — gambar baru dimuat saat akan terlihat, bagus untuk halaman panjang -->
<img src="produk/kaos.jpg" alt="Kaos polos" loading="lazy">Hati-hati — Atribut alt wajib diisi dengan deskripsi yang berarti, bukan dikosongkan atau asal-asalan. Ini dibaca keras-keras oleh pembaca layar untuk pengguna tunanetra, ditampilkan kalau gambar gagal dimuat, dan dibaca mesin pencari untuk memahami isi gambar. Dibahas lebih dalam di Materi 7 soal aksesibilitas.
Figure dan caption
<figure>
<img src="produk/kaos.jpg" alt="Kaos polos hitam">
<figcaption>Kaos Polos Edisi Terbatas</figcaption>
</figure><figure> mengelompokkan gambar dengan keterangannya secara semantik — lebih bermakna daripada sekadar <img> dan <p> terpisah.
Tabel: untuk data tabular, bukan layout
<table>
<thead>
<tr>
<th>Produk</th>
<th>Harga</th>
<th>Stok</th>
</tr>
</thead>
<tbody>
<tr>
<td>Kaos Polos</td>
<td>Rp 85.000</td>
<td>24</td>
</tr>
<tr>
<td>Hoodie</td>
<td>Rp 210.000</td>
<td>8</td>
</tr>
</tbody>
</table>Catatan — <thead> membungkus baris header, <tbody> membungkus baris data. <th> (table header) berbeda dari <td> (table data) — pembaca layar mengumumkan <th> secara berbeda, membantu pengguna memahami struktur tabel. Pada masa awal web, tabel sering disalahgunakan untuk mengatur layout seluruh halaman — ini praktik usang; untuk layout, pakai CSS (Modul 3).
Video dan audio
<video src="promo.mp4" controls width="600"></video>
<audio src="lagu.mp3" controls></audio>
<!-- beberapa sumber, browser pilih format yang didukung -->
<video controls width="600">
<source src="promo.webm" type="video/webm">
<source src="promo.mp4" type="video/mp4">
Browser kamu tidak mendukung video.
</video>controls menampilkan tombol play/pause/volume bawaan browser — tanpa itu, video/audio tidak punya kontrol apa pun yang terlihat.
Latihan — Buat halaman "Katalog Produk" dengan tabel berisi minimal 4 baris (nama, harga, stok). Tambahkan satu gambar produk dengan <figure> dan caption yang sesuai, lengkap dengan alt yang deskriptif.
Materi 5 · Modul 0
Form HTML
Cara mengumpulkan input dari pengguna — fondasi untuk login, pencarian, dan checkout yang nanti diproses backend.
Struktur form dasar
<form action="/kirim" method="POST">
<label for="nama">Nama</label>
<input type="text" id="nama" name="nama">
<button type="submit">Kirim</button>
</form>Catatan — action menentukan URL tujuan saat form dikirim, method menentukan HTTP method-nya (GET untuk mencari/membaca, POST untuk mengubah data — aturan yang sama persis dibahas di handbook PHP Materi 12). <label for="nama"> terhubung ke <input id="nama"> lewat atribut yang sama — mengklik label otomatis fokus ke input-nya, dan penting untuk aksesibilitas.
Jenis-jenis input
<input type="text"> <!-- teks satu baris -->
<input type="email"> <!-- validasi format email bawaan browser -->
<input type="password"> <!-- karakter disembunyikan -->
<input type="number"> <!-- hanya angka, ada tombol naik/turun -->
<input type="date"> <!-- date picker bawaan browser -->
<input type="checkbox"> <!-- centang, bisa pilih banyak -->
<input type="radio" name="ukuran"> <!-- pilih SATU dari beberapa, name HARUS sama -->
<input type="file"> <!-- upload file -->
<input type="search"> <!-- khusus pencarian -->
<textarea></textarea> <!-- teks banyak baris -->
<select name="kategori">
<option value="pakaian">Pakaian</option>
<option value="aksesoris">Aksesoris</option>
</select>Catatan — Memilih type yang tepat memberi perilaku bawaan browser secara gratis: type="email" otomatis memvalidasi format dasar, type="number" otomatis menolak huruf, dan di perangkat mobile, keyboard yang muncul otomatis menyesuaikan (keypad angka untuk number, keyboard dengan tombol @ untuk email).
Radio button: name yang sama = pilihan eksklusif
<p>Pilih ukuran:</p>
<label><input type="radio" name="ukuran" value="S"> S</label>
<label><input type="radio" name="ukuran" value="M"> M</label>
<label><input type="radio" name="ukuran" value="L"> L</label>Hati-hati — Semua radio button dalam satu kelompok pilihan harus punya name yang sama persis — itulah yang memberi tahu browser bahwa mereka saling eksklusif (memilih satu otomatis membatalkan yang lain). Lupa menyamakan name adalah kesalahan paling umum dengan radio button — hasilnya, semua opsi bisa dicentang bersamaan, bukan cuma satu.
Validasi bawaan browser
<input type="text" required> <!-- wajib diisi -->
<input type="text" minlength="3" maxlength="50">
<input type="number" min="1" max="100">
<input type="text" placeholder="Contoh: 08123456789">Ini sama persis dengan validasi HTML5 yang dibahas di handbook JavaScript Materi 14 — berguna sebagai lapisan pertama yang cepat, tapi perlu diingat (seperti disinggung di sana) validasi ini tidak menggantikan validasi di server, yang dibahas lengkap di handbook PHP Materi 12 dan Laravel Materi 13.
Latihan — Buat form registrasi dengan: input nama (required), email (type email, required), password (type password, minlength 8), radio button untuk jenis kelamin, dropdown untuk kota, dan checkbox "Saya setuju dengan syarat & ketentuan" (required). Pastikan setiap input punya label yang terhubung dengan benar.
Materi 6 · Modul 1
Elemen semantik
Menamai bagian halaman sesuai perannya — bukan sekadar kotak <div> generik di mana-mana.
Masalah dengan div semua
<div class="header">...</div>
<div class="nav">...</div>
<div class="main-content">...</div>
<div class="footer">...</div>
<!-- browser, mesin pencari, dan pembaca layar TIDAK TAHU div mana yang apa -->
<!-- nama class hanya untuk MANUSIA yang baca kode, bukan untuk mesin --><header>...</header>
<nav>...</nav>
<main>...</main>
<footer>...</footer>
<!-- sekarang STRUKTURNYA jelas, bahkan tanpa melihat nama class apa pun -->Catatan — "Semantik" berarti nama elemen membawa makna, bukan cuma bentuk visual. <div> tidak salah untuk dipakai — tapi dipakai untuk pengelompokan yang murni visual, bukan untuk bagian-bagian struktural utama halaman. Elemen semantik membantu mesin pencari memahami halamanmu (penting untuk SEO, Materi 8) dan membantu pembaca layar menavigasi halaman dengan cepat (Materi 7).
Elemen semantik struktural utama
<body>
<header>
<h1>Toko Onlenkan</h1>
<nav>
<ul>
<li><a href="/">Beranda</a></li>
<li><a href="/produk">Produk</a></li>
</ul>
</nav>
</header>
<main>
<article>
<h2>Kaos Polos Edisi Terbatas</h2>
<p>Deskripsi produk di sini...</p>
</article>
<aside>
<h3>Produk Terkait</h3>
<p>...</p>
</aside>
</main>
<footer>
<p>© 2026 Onlenkan</p>
</footer>
</body>| Elemen | Untuk |
|---|---|
<header> | Bagian pembuka halaman atau section — biasanya judul, logo, nav. |
<nav> | Kumpulan link navigasi utama. |
<main> | Konten utama halaman — hanya satu per halaman. |
<article> | Konten yang berdiri sendiri dan masuk akal dipisah (artikel blog, satu produk, satu komentar). |
<section> | Pengelompokan tematik dalam halaman, biasanya dengan heading-nya sendiri. |
<aside> | Konten terkait tapi terpisah dari alur utama — sidebar, info tambahan. |
<footer> | Bagian penutup halaman atau section — copyright, link tambahan. |
article vs section: kapan pakai yang mana
Catatan — Aturan praktis: pakai <article> kalau isinya masuk akal berdiri sendiri, terlepas dari konteks halaman (bisa dibagikan, disindikasikan, atau dibaca sendirian dan tetap masuk akal) — seperti satu produk, satu postingan blog. Pakai <section> untuk mengelompokkan bagian-bagian terkait dalam satu halaman yang tidak masuk akal berdiri sendiri, seperti "Bagian Testimoni" atau "Bagian FAQ".
Latihan — Ambil halaman "Katalog Produk" dari latihan Materi 4, susun ulang dengan <header> (berisi judul dan <nav>), <main> yang membungkus tabel produk sebagai <section>, dan <footer> di bagian bawah.
Materi 7 · Modul 1
Atribut penting dan aksesibilitas dasar
Membuat halaman yang bisa dipakai semua orang — termasuk yang memakai pembaca layar, keyboard saja, atau koneksi lambat.
Kenapa aksesibilitas penting
Tidak semua pengguna berinteraksi dengan halamanmu lewat mouse dan melihat layar secara langsung. Pengguna tunanetra memakai screen reader (pembaca layar) yang "membacakan" halaman — dan itu hanya bisa bekerja dengan baik kalau HTML-nya ditulis dengan benar. Pengguna lain mungkin hanya memakai keyboard (karena disabilitas motorik, atau sekadar preferensi). Aksesibilitas bukan fitur tambahan — ini bagian dari menulis HTML yang benar.
Alt text yang baik
<!-- ❌ tidak berguna -->
<img src="kaos.jpg" alt="gambar">
<img src="kaos.jpg" alt="kaos.jpg">
<!-- ✅ deskriptif, seolah menjelaskan ke orang yang tidak bisa melihat gambarnya -->
<img src="kaos.jpg" alt="Kaos polos hitam dengan logo kecil di dada kiri">
<!-- gambar PURELY dekoratif (tidak membawa informasi) — alt KOSONG, bukan dihilangkan -->
<img src="garis-dekorasi.svg" alt="">Catatan — alt="" (kosong, bukan dihilangkan) secara eksplisit memberi tahu pembaca layar "lewati gambar ini, tidak penting" — berbeda dari lupa menulis alt sama sekali, yang justru membuat pembaca layar membacakan nama file-nya yang membingungkan.
Label yang benar untuk form
<!-- ✅ cara 1: for/id — paling umum -->
<label for="email">Email</label>
<input type="email" id="email" name="email">
<!-- ✅ cara 2: membungkus input langsung di dalam label -->
<label>
Email
<input type="email" name="email">
</label>
<!-- ❌ hanya placeholder, TANPA label — masalah serius -->
<input type="email" placeholder="Email">Hati-hati — placeholder saja bukan pengganti <label>. Placeholder hilang begitu pengguna mulai mengetik, pembaca layar sering tidak membacanya dengan baik, dan kontrasnya biasanya terlalu rendah untuk dibaca nyaman. Setiap input yang berarti harus punya label sungguhan — ini salah satu kesalahan aksesibilitas paling umum di web.
Keyboard navigation
<!-- ✅ elemen interaktif NATIVE — otomatis bisa diakses keyboard (Tab, Enter, Space) -->
<button>Klik Saya</button>
<a href="/produk">Lihat Produk</a>
<!-- ❌ div yang "dipura-pura" jadi tombol lewat JavaScript — TIDAK otomatis bisa Tab/Enter -->
<div onclick="simpanData()">Klik Saya</div>Catatan — Aturan paling sederhana untuk aksesibilitas: pakai elemen HTML native sesuai fungsinya (<button> untuk tombol, <a> untuk link, <input> untuk input). Mereka otomatis bisa dinavigasi dengan keyboard (tombol Tab untuk pindah, Enter/Space untuk mengaktifkan) dan dikenali pembaca layar — tanpa kamu menulis kode tambahan apa pun. Membuat elemen interaktif dari <div> generik kehilangan semua ini secara gratis.
ARIA: hanya saat benar-benar perlu
<button aria-label="Tutup dialog">✕</button>
<!-- tombol cuma berisi simbol "✕" — aria-label memberi nama yang jelas untuk pembaca layar -->
<div role="alert">Produk berhasil ditambahkan ke keranjang</div>
<!-- pembaca layar akan OTOMATIS mengumumkan ini begitu muncul, tanpa pengguna harus mencarinya -->Catatan — Aturan emas ARIA: "No ARIA is better than bad ARIA" — pakai elemen semantik yang sudah benar secara native dulu (Materi 6 & di atas), dan tambahkan atribut aria-* hanya untuk kasus yang tidak bisa diekspresikan HTML biasa, seperti memberi label pada tombol ikon tanpa teks.
Latihan — Audit form registrasi dari latihan Materi 5: pastikan setiap input punya label yang benar-benar terhubung (bukan cuma placeholder). Ganti elemen apa pun yang berupa <div onclick> (kalau ada) jadi <button>. Tambahkan alt yang deskriptif pada semua gambar di halaman latihanmu sebelumnya.
Materi 8 · Modul 1
Meta tag dan SEO dasar
Informasi di <head> yang tidak tampil langsung, tapi menentukan bagaimana halamanmu muncul di mesin pencari dan saat dibagikan.
Meta tag dasar yang wajib ada
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Toko Onlenkan — Kaos dan Aksesoris Berkualitas</title>
<meta name="description" content="Belanja kaos dan aksesoris berkualitas dengan harga terjangkau di Toko Onlenkan.">
</head>| Tag | Fungsi |
|---|---|
charset="UTF-8" | Memastikan karakter khusus (termasuk emoji, huruf non-Latin) ditampilkan benar — sama pentingnya seperti utf8mb4 di handbook MySQL. |
viewport | Memberi tahu browser mobile untuk menampilkan halaman sesuai lebar layar perangkat, bukan menyusutkan tampilan desktop — wajib untuk responsive design (Modul 3). |
<title> | Teks di tab browser DAN judul yang tampil di hasil pencarian Google. |
description | Ringkasan yang (biasanya) tampil di bawah judul di hasil pencarian. |
Hati-hati — Tanpa <meta name="viewport" ...>, browser mobile menampilkan halamanmu seolah layar lebar desktop lalu menyusutkannya — hasilnya teks super kecil yang harus di-zoom manual. Ini salah satu penyebab paling umum halaman "terlihat rusak" di HP, padahal tampil normal di laptop.
Open Graph: tampilan saat dibagikan di media sosial
<meta property="og:title" content="Kaos Polos Edisi Terbatas">
<meta property="og:description" content="Kaos premium, bahan adem, stok terbatas.">
<meta property="og:image" content="https://onlenkan.com/img/kaos.jpg">
<meta property="og:url" content="https://onlenkan.com/produk/kaos-polos">Ini yang menentukan gambar, judul, dan deskripsi yang muncul saat seseorang membagikan link halamanmu di WhatsApp, Facebook, atau Twitter/X — tanpa ini, platform-platform itu akan menebak sendiri (seringnya hasilnya kurang menarik).
Struktur heading yang baik untuk SEO
Catatan — Mesin pencari membaca struktur heading (h1–h6, Materi 2) untuk memahami topik dan hierarki halaman — satu <h1> yang jelas menyatakan topik utama halaman adalah sinyal SEO dasar yang penting. Begitu juga elemen semantik (Materi 6): halaman yang tersusun rapi dengan <nav>, <main>, <article> lebih mudah "dipahami" mesin pencari dibanding tumpukan <div> tanpa makna.
robots.txt dan sitemap (sekilas)
Dua hal di luar HTML itu sendiri, tapi relevan untuk SEO: robots.txt (file di root domain) memberi tahu mesin pencari halaman mana yang boleh/tidak boleh di-crawl, dan sitemap.xml adalah daftar semua halaman di situsmu untuk membantu mesin pencari menemukannya. Keduanya di luar cakupan handbook ini, tapi baik diketahui namanya untuk project nyata nanti.
Latihan — Lengkapi <head> halaman "Katalog Produk" dari latihan sebelumnya dengan: charset, viewport, title yang deskriptif, meta description, dan tiga tag Open Graph dasar (title, description, image).
Materi 9 · Modul 1
Validasi HTML dan praktik baik
Menutup Modul 1 dengan kebiasaan yang akan membuat kodemu lebih andal dan mudah dirawat.
Memvalidasi HTML
Browser cukup "memaafkan" HTML yang salah (Materi 2), tapi itu bukan alasan untuk menulis HTML sembarangan — kesalahan kecil bisa menyebabkan bug tampilan yang aneh dan sulit dilacak. W3C menyediakan validator resmi di validator.w3.org — tempel kode HTML-mu di sana untuk mengecek kesalahan sintaks.
Kesalahan umum yang perlu dihindari
<!-- 1. lupa menutup tag -->
<p>Teks tanpa penutup
<!-- 2. nesting yang tidak tepat (Materi 2) -->
<p><div>div di dalam p — TIDAK VALID</div></p>
<!-- 3. id yang duplikat di satu halaman -->
<div id="konten">...</div>
<div id="konten">...</div> <!-- id HARUS unik per halaman -->
<!-- 4. atribut tanpa tanda kutip yang berisi spasi -->
<img alt=Kaos polos hitam> <!-- browser bingung di mana nilai alt berakhir -->Hati-hati — <div> tidak boleh berada di dalam <p> karena <p> hanya boleh berisi konten "inline" (teks, <span>, <a>, dll), bukan elemen blok seperti <div>. Kalau kamu menemukan diri menulis <div> di dalam <p>, itu pertanda strukturnya perlu disusun ulang.
class vs id: kapan pakai yang mana
| Atribut | Aturan |
|---|---|
id | Unik — hanya satu elemen per halaman yang boleh punya id tertentu. Untuk target link (#kontak) atau label form. |
class | Boleh dipakai berkali-kali di banyak elemen, dan satu elemen boleh punya banyak class sekaligus (class="kartu produk-aktif"). Untuk styling CSS (Modul 2) yang dipakai berulang. |
Format dan indentasi yang konsisten
<ul>
<li>Item satu</li>
<li>Item dua</li>
</ul>
<!-- indentasi konsisten (2 atau 4 spasi) membuat struktur bersarang mudah dibaca sekilas -->Kebanyakan editor kode (seperti VS Code) bisa merapikan indentasi otomatis — biasakan memakainya, karena HTML yang rapi jauh lebih mudah dilacak saat ada bug tampilan nanti.
Checklist sebelum lanjut ke CSS
- ✅ Setiap tag yang butuh penutup, tertutup dengan benar.
- ✅ Heading berurutan dan logis (tidak lompat dari
h1keh4). - ✅ Semua gambar punya
altyang sesuai. - ✅ Semua input form punya label yang benar-benar terhubung.
- ✅ Elemen semantik dipakai untuk struktur utama (
header,nav,main,footer). - ✅
idunik di seluruh halaman, tidak ada duplikat. - ✅
<head>lengkap dengan charset, viewport, title, dan description.
Latihan — Ambil salah satu halaman latihanmu sebelumnya, tempel ke validator.w3.org, dan perbaiki semua error yang muncul. Jalankan checklist di atas secara manual terhadap halaman itu.
Materi 10 · Modul 2
Kenalan CSS
Cara mengatur tampilan elemen HTML — dan tiga cara menyisipkannya ke halaman.
Anatomi aturan CSS
/* selector properti nilai */
p {
color: blue;
font-size: 16px;
}
/* "selector" MENENTUKAN elemen mana yang diatur,
setiap baris di dalam {} adalah properti: nilai; */Tiga cara menyisipkan CSS
<p style="color: blue; font-size: 16px;">Teks biru</p><head>
<style>
p { color: blue; }
</style>
</head>/* style.css */
p { color: blue; }<head>
<link rel="stylesheet" href="style.css">
</head>Catatan — File CSS terpisah adalah cara yang hampir selalu dipakai di project nyata: satu file CSS bisa dipakai di banyak halaman HTML sekaligus (konsisten, tidak perlu mengulang), dan browser bisa menyimpannya di cache (lebih cepat dimuat di kunjungan berikutnya). Hindari style inline kecuali untuk kasus yang benar-benar butuh nilai dinamis dari JavaScript.
Selector dasar
/* selector tag — mengatur SEMUA elemen dengan tag itu */
p { color: #333; }
/* selector class — titik (.) di depan, dipakai berulang */
.kartu { padding: 1rem; }
/* selector id — pagar (#) di depan, HANYA SATU elemen */
#header-utama { background: white; }
/* gabung beberapa selector untuk aturan yang sama */
h1, h2, h3 { font-family: sans-serif; }
/* elemen ANAK di dalam elemen tertentu */
.kartu p { margin: 0; } /* HANYA di dalam .kartu */
Live preview
Judul berwarna lewat class .judul
Teks biasa dengan kata yang disorot lewat class .highlight.
DevTools browser: alat terpenting belajar CSS
Catatan — Tekan F12 (atau klik kanan → Inspect) di browser mana pun, lalu klik elemen di halaman — DevTools menunjukkan persis aturan CSS apa yang sedang berlaku pada elemen itu, dan kamu bisa mengubahnya langsung untuk mencoba (perubahan ini sementara, hilang saat refresh). Ini cara tercepat bereksperimen dengan CSS tanpa harus bolak-balik edit file dan refresh manual.
Latihan — Buat file style.css terpisah dan hubungkan ke salah satu halaman HTML latihanmu sebelumnya. Beri warna berbeda untuk h1, beri satu paragraf sebuah class, dan atur warnanya lewat class itu. Buka DevTools dan coba ubah salah satu nilai warna langsung di sana untuk melihat efeknya.
Materi 11 · Modul 2
Warna, unit, dan typography
Bahan dasar untuk mengatur tampilan teks dan warna di seluruh halaman.
Cara menulis warna
.a { color: red; } /* nama warna bawaan — terbatas */
.b { color: #FF0000; } /* hex — paling umum dipakai */
.c { color: rgb(255, 0, 0); } /* red, green, blue — 0 sampai 255 */
.d { color: rgba(255, 0, 0, 0.5); } /* rgb + alpha (transparansi) 0-1 */
.e { color: hsl(0, 100%, 50%); } /* hue, saturation, lightness */Catatan — hex (#RRGGBB) paling umum dipakai karena ringkas dan mudah disalin dari alat desain seperti Figma. rgba() berguna khusus saat butuh transparansi (nilai alpha ke-4, dari 0 = transparan penuh sampai 1 = pekat penuh) — misalnya untuk overlay gelap transparan di atas gambar.
Unit ukuran: px, %, rem, em, vw/vh
| Unit | Artinya |
|---|---|
px | Pixel — ukuran tetap, tidak relatif terhadap apa pun. |
% | Persentase dari elemen induknya. |
rem | Relatif terhadap ukuran font akar (<html>) — biasanya 1rem = 16px secara default. |
em | Relatif terhadap ukuran font elemen itu sendiri (atau induk terdekat yang mengaturnya). |
vw / vh | 1% dari lebar/tinggi layar browser (viewport) — berguna untuk elemen full-screen. |
html { font-size: 16px; } /* 1rem = 16px untuk SELURUH halaman */
h1 { font-size: 2rem; } /* = 32px, TIDAK peduli konteksnya di mana */
.kotak { width: 50%; } /* separuh lebar elemen INDUKnya */
.hero { height: 100vh; } /* tinggi penuh layar browser */Hati-hati — Untuk font-size, lebih disukai rem daripada px: kalau pengguna mengubah pengaturan ukuran font bawaan browser mereka (untuk aksesibilitas — lihat Materi 7), elemen yang pakai rem ikut menyesuaikan, sedangkan yang pakai px tetap kaku di ukuran yang sama.
Properti teks dasar
p {
font-family: "Helvetica", sans-serif; /* daftar cadangan jika font pertama tak tersedia */
font-size: 1rem;
font-weight: 400; /* 400=normal, 700=bold */
line-height: 1.6; /* jarak antar baris — tanpa unit = kelipatan font-size */
text-align: left; /* left, center, right, justify */
text-decoration: none; /* hilangkan garis bawah (umum untuk ) */
letter-spacing: 0.02em;
}Live preview
Paragraf dengan line-height: 1.7 — baris cukup renggang, nyaman dibaca.
Paragraf dengan line-height: 1.1 — baris rapat, terasa sempit.
Catatan — line-height adalah salah satu properti yang paling berpengaruh pada kenyamanan membaca, tapi paling sering diabaikan pemula. Nilai sekitar 1.5–1.7 untuk paragraf teks biasanya nyaman dibaca — terlalu rapat (1.1) membuat baris saling berdempetan, terlalu renggang membuat teks terasa "putus".
Mengimpor Google Fonts
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">body { font-family: "Inter", sans-serif; }Latihan — Impor satu Google Font pilihanmu. Buat aturan untuk body (font-family, font-size, line-height) dan h1–h3 (ukuran berbeda, font-weight tebal). Terapkan pada salah satu halaman latihanmu dan bandingkan sebelum/sesudah.
Materi 12 · Modul 2
Box model
Konsep paling fundamental di CSS — setiap elemen adalah sebuah "kotak", dan memahami kotaknya adalah kunci mengatur layout apa pun.
Empat lapisan kotak
Live preview — arahkan kursor, buka DevTools untuk lihat box model
| Lapisan | Dari luar ke dalam |
|---|---|
| margin | Jarak di LUAR elemen — ruang antara elemen ini dan elemen tetangganya. |
| border | Garis tepi elemen. |
| padding | Jarak di DALAM elemen — antara tepi (border) dan isinya. |
| content | Isi sebenarnya — teks, gambar, elemen anak. |
Menulis margin, padding, dan border
.kartu {
margin: 16px; /* keempat sisi sama: 16px */
margin: 16px 8px; /* atas-bawah 16px, kiri-kanan 8px */
margin: 16px 8px 4px 8px; /* atas kanan bawah kiri (searah jarum jam) */
margin-top: 16px; /* atau atur satu sisi saja */
padding: 20px;
border: 1px solid #ccc; /* ketebalan, gaya garis, warna */
border-radius: 8px; /* sudut membulat */
}box-sizing: perbedaan yang membingungkan banyak pemula
.kotak {
width: 300px;
padding: 20px;
border: 2px solid black;
}
/* lebar SEBENARNYA = 300 + 20+20 (padding) + 2+2 (border) = 344px!
width di CSS cuma mengatur CONTENT-nya, padding & border DITAMBAHKAN di luar */* {
box-sizing: border-box; /* terapkan ke SEMUA elemen */
}
.kotak {
width: 300px;
padding: 20px;
border: 2px solid black;
}
/* lebar SEBENARNYA = TETAP 300px — padding & border dihitung DI DALAM 300px itu */Hati-hati — Ini jebakan CSS paling terkenal bagi pemula. Dengan box-sizing: content-box (nilai bawaan browser), menambahkan padding atau border membuat elemen jadi lebih besar dari width yang kamu tulis — sering membuat layout "bocor" tak terduga. Hampir semua project modern menambahkan * { box-sizing: border-box; } di awal CSS mereka sebagai standar, dan handbook ini akan selalu mengasumsikannya mulai sekarang.
Margin collapse (perilaku khusus margin vertikal)
Catatan — Perilaku unik: kalau dua elemen bertumpuk vertikal, margin-bottom elemen atas dan margin-top elemen bawah tidak dijumlahkan — yang dipakai adalah nilai yang lebih besar di antara keduanya (disebut margin collapse). Ini hanya terjadi pada margin vertikal antar elemen sejenis, tidak pada padding. Kalau jarak antar elemenmu terlihat lebih kecil dari dugaan, ini sering jadi penyebabnya.
Latihan — Buat tiga kotak <div> dengan class .kartu, masing-masing diberi width: 200px, padding: 16px, border: 2px solid, dan margin: 12px. Terapkan * { box-sizing: border-box; } di awal CSS-mu, lalu bandingkan lebar sebenarnya di DevTools dengan dan tanpa baris itu.
Materi 13 · Modul 2
Selector lanjutan dan spesifisitas
Menargetkan elemen dengan lebih presisi, dan memahami aturan siapa yang "menang" saat dua aturan CSS bentrok.
Combinator selector
.kartu p { } /* descendant: SEMUA di DALAM .kartu, berapa pun levelnya */
.kartu > p { } /* child: HANYA yang langsung anak .kartu */
h2 + p { } /* adjacent sibling: yang PERSIS setelah
*/
h2 ~ p { } /* general sibling: SEMUA setelah
(sejajar, sama induk) */
Attribute selector
input[type="email"] { } /* hanya input dengan type="email" */
a[target="_blank"] { } /* link yang buka tab baru */
a[href^="https"] { } /* href yang DIAWALI "https" */
img[src$=".png"] { } /* src yang DIAKHIRI ".png" */Spesifisitas: siapa yang menang saat bentrok
p { color: blue; }
.teks { color: red; }
/* elemen — warnanya MERAH, class lebih "spesifik" dari tag */
| Jenis selector | "Bobot" (dari rendah ke tinggi) |
|---|---|
Selector tag (p) | 1 poin |
Class (.teks), attribute ([type="text"]) | 10 poin |
ID (#header) | 100 poin |
style="..." inline | 1000 poin |
!important | Mengalahkan SEMUANYA (hindari, lihat peringatan di bawah) |
Catatan — Saat beberapa aturan CSS menargetkan elemen yang sama dengan properti yang sama, aturan dengan spesifisitas lebih tinggi yang menang — bukan yang ditulis paling akhir. Kalau spesifisitasnya sama persis, barulah urutan penulisan menentukan (yang ditulis belakangan menang). Ini sumber kebingungan paling umum saat CSS "tidak kena" padahal sudah ditulis.
Hindari !important
.teks { color: red !important; }
/* ini akan MENGALAHKAN aturan apa pun, termasuk yang lebih spesifik */
/* makin banyak !important dipakai, makin sulit CSS di-debug nantinya */Hati-hati — !important terasa seperti solusi cepat saat CSS "tidak mau nurut", tapi dia merusak sistem spesifisitas sepenuhnya — begitu dipakai di satu tempat, kamu sering perlu menambahkan !important lagi di tempat lain untuk mengalahkannya, dan CSS jadi semakin sulit diprediksi. Kalau sebuah aturan "tidak kena", biasanya itu tanda perlu memahami kenapa (selector yang lebih spesifik di tempat lain), bukan memaksakannya dengan !important.
Latihan — Buat tiga aturan CSS yang bentrok untuk warna teks sebuah elemen yang sama: satu lewat selector tag, satu lewat class, satu lewat id. Tanpa melihat hasilnya dulu, tebak warna mana yang akan menang berdasarkan tabel spesifisitas di atas — lalu buka di browser untuk memverifikasi tebakanmu.
Materi 14 · Modul 2
Display dan positioning
Bagaimana elemen "mengalir" relatif satu sama lain — dasar sebelum masuk ke Flexbox dan Grid di Modul 3.
display: block vs inline vs inline-block
| Nilai | Perilaku |
|---|---|
block | Mengambil seluruh lebar yang tersedia, selalu pindah ke baris baru. Default untuk div, p, h1. |
inline | Lebar sesuai isinya, tidak pindah baris, width/height diabaikan. Default untuk span, a, strong. |
inline-block | Seperti inline (tidak pindah baris), tapi width/height/margin vertikal tetap berlaku. |
none | Elemen sepenuhnya hilang — tidak memakan ruang sama sekali. |
Live preview
Hati-hati — width dan height tidak berpengaruh pada elemen inline — jebakan umum saat mencoba mengatur ukuran <span> atau <a> tanpa hasil. Solusinya: ubah jadi inline-block atau block dulu.
position: static, relative, absolute, fixed
.a { position: static; } /* default — mengikuti alur normal, top/left dll DIABAIKAN */
.b { position: relative; } /* tetap di alur normal, TAPI bisa digeser relatif posisi aslinya */
.b { top: 10px; left: 20px; } /* geser 10px ke bawah, 20px ke kanan DARI posisi normal */
.c { position: absolute; } /* LEPAS dari alur, posisi relatif ke induk terdekat yang position-nya BUKAN static */
.c { top: 0; right: 0; } /* nempel di pojok kanan atas induknya */
.d { position: fixed; } /* LEPAS dari alur, posisi relatif ke LAYAR, tetap di tempat saat di-scroll */Catatan — Pola paling umum: induk diberi position: relative (tanpa pergeseran apa pun — cuma untuk "menjadi acuan"), lalu anaknya diberi position: absolute untuk diposisikan persis di dalam induk itu — misalnya badge "Baru!" di pojok kartu produk, atau ikon di dalam input pencarian. fixed dipakai untuk elemen yang harus selalu terlihat saat scroll, seperti tombol "kembali ke atas" atau navbar yang menempel di atas layar.
Contoh: badge di pojok kartu
.kartu {
position: relative; /* jadi "acuan" untuk badge di dalamnya */
padding: 16px;
}
.badge {
position: absolute;
top: 8px;
right: 8px;
background: red;
color: white;
padding: 2px 8px;
border-radius: 4px;
}Live preview
Kaos Polos Edisi Terbatas
Latihan — Buat kartu produk dengan position: relative, lalu tambahkan badge "Diskon 20%" di pojok kanan atasnya dengan position: absolute. Buat juga navbar sederhana dengan position: fixed di atas, dan scroll halaman untuk memastikan navbar-nya tetap terlihat.
Materi 15 · Modul 2
Pseudo-class dan pseudo-element
Menargetkan "keadaan" atau "bagian" elemen tanpa perlu menambah class atau HTML tambahan.
Pseudo-class interaksi
a { color: blue; }
a:hover { color: darkblue; } /* saat kursor di atasnya */
a:active { color: red; } /* saat SEDANG diklik */
a:focus { outline: 2px solid blue; } /* saat fokus keyboard (Tab) — JANGAN dihilangkan! */
input:focus { border-color: #1D6FA5; }
input:disabled { opacity: 0.5; }
input:checked { accent-color: green; } /* checkbox/radio yang tercentang */Hati-hati — Jangan pernah menulis *:focus { outline: none; } untuk "menghilangkan garis jelek di sekitar elemen yang diklik" — itu menghapus satu-satunya petunjuk visual bagi pengguna keyboard tentang elemen mana yang sedang aktif, masalah aksesibilitas yang serius (Materi 7). Kalau ingin tampilan fokus yang berbeda, ganti stylenya (misalnya outline: 2px solid biru-custom), jangan dihilangkan sama sekali.
Pseudo-class struktural
li:first-child { } /* elemen PERTAMA di antara saudaranya */
li:last-child { } /* elemen TERAKHIR */
tr:nth-child(even) { background: #f5f5f5; } /* baris GENAP — "zebra stripes" tabel */
tr:nth-child(odd) { } /* baris GANJIL */
p:not(.intro) { } /* SEMUA KECUALI yang berclass .intro */
Live preview — zebra stripes
| Kaos Polos | Rp 85.000 |
| Hoodie | Rp 210.000 |
| Topi | Rp 45.000 |
| Jaket | Rp 320.000 |
Pseudo-element: menargetkan bagian dari elemen
p::first-letter { font-size: 2em; font-weight: bold; } /* huruf pertama */
p::first-line { font-weight: bold; } /* baris pertama */
/* ::before dan ::after — menyisipkan KONTEN lewat CSS, tanpa menambah HTML */
.wajib::after {
content: " *";
color: red;
}
/* elemen akan tampil: Nama * (asterisk merah) */Catatan — ::before dan ::after sangat sering dipakai untuk dekorasi kecil (tanda wajib di form, ikon panah, garis dekoratif) tanpa menambah elemen HTML tambahan yang sebenarnya tidak membawa makna apa pun — menjaga HTML tetap bersih sementara dekorasi murni visual ditangani CSS, sesuai pembagian tugas di Materi 1.
Transisi sederhana pada hover
.tombol {
background: #1D6FA5;
color: white;
padding: 10px 20px;
border-radius: 6px;
transition: background 0.2s; /* perubahan background jadi HALUS, bukan instan */
}
.tombol:hover { background: #154f78; }transition dibahas lebih mendalam di Modul 4, tapi menambahkannya pada properti yang berubah saat :hover adalah kebiasaan kecil yang membuat interaksi terasa jauh lebih halus — perbedaan antara tampilan "kaku" dan "matang".
Latihan — Buat tabel dengan efek zebra stripes memakai :nth-child(even). Buat tombol dengan efek hover yang halus (transition). Tambahkan tanda asterisk merah otomatis lewat ::after pada label form yang wajib diisi.
Materi 16 · Modul 3
Flexbox
Sistem layout satu dimensi — mengatur elemen dalam satu baris atau satu kolom dengan mudah. Salah satu alat CSS paling sering dipakai sehari-hari.
Mengaktifkan flexbox
.container {
display: flex; /* anak-anak langsungnya otomatis sejajar HORIZONTAL */
}Live preview
Begitu induk diberi display: flex, SEMUA anak langsungnya otomatis berjajar dalam satu baris — tanpa perlu float atau trik lama lainnya.
Mengatur arah: flex-direction
.container { flex-direction: row; } /* default — horizontal, kiri ke kanan */
.container { flex-direction: column; } /* vertikal, atas ke bawah */Mengatur jarak: gap
.container {
display: flex;
gap: 16px; /* jarak OTOMATIS antar anak, tanpa perlu margin manual tiap item */
}Catatan — gap jauh lebih bersih dibanding cara lama memberi margin-right pada tiap anak (yang butuh pengecualian khusus untuk anak terakhir, karena anak terakhir tidak boleh punya margin kanan ekstra). gap otomatis hanya memberi jarak di antara item, bukan di tepi luar container.
justify-content: mengatur sumbu utama
.container { justify-content: flex-start; } /* rapat di awal (default) */
.container { justify-content: center; } /* di tengah */
.container { justify-content: flex-end; } /* rapat di akhir */
.container { justify-content: space-between; } /* jarak sama rata, tanpa jarak di tepi luar */
.container { justify-content: space-around; } /* jarak sama rata, DENGAN setengah jarak di tepi luar */Live preview — space-between
align-items: mengatur sumbu silang (biasanya vertikal)
.container {
display: flex;
height: 200px;
align-items: center; /* anak di-TENGAH-kan SECARA VERTIKAL */
}
/* nilai lain: flex-start, flex-end, stretch (default, anak memanjang penuh tinggi container) */Catatan — Kombinasi justify-content: center + align-items: center adalah cara paling umum dipakai di CSS modern untuk menengahkan elemen secara sempurna (horizontal dan vertikal) — masalah yang dulu terkenal rumit di CSS lama, sekarang hanya dua baris.
flex-wrap: membiarkan item "turun baris"
.container {
display: flex;
flex-wrap: wrap; /* jika tidak muat satu baris, lanjut ke baris berikutnya */
gap: 12px;
}
.kartu { flex: 1 1 200px; } /* tumbuh, menyusut, lebar dasar 200px — lihat di bawah */Properti flex pada anak: grow, shrink, basis
.a { flex: 1; } /* ambil 1 "bagian" dari ruang sisa yang tersedia */
.b { flex: 2; } /* ambil 2 bagian — DUA KALI LEBIH LEBAR dari .a */
.c { flex: 0 0 150px; } /* TIDAK tumbuh, TIDAK menyusut, selalu tepat 150px (sidebar tetap) */Live preview — sidebar tetap + konten mengisi sisa ruang
100px
Latihan — Buat navbar dengan Flexbox: logo di kiri, menu navigasi di tengah/kanan, memakai justify-content: space-between. Buat juga baris tiga kartu produk sejajar dengan gap, di mana ketiganya punya lebar sama rata (flex: 1).
Materi 17 · Modul 3
CSS Grid
Sistem layout dua dimensi — mengatur baris DAN kolom sekaligus. Pelengkap Flexbox, bukan penggantinya.
Flexbox vs Grid: kapan pakai yang mana
Catatan — Aturan praktis sederhana: pakai Flexbox saat mengatur elemen dalam satu arah (satu baris ATAU satu kolom) — navbar, deretan tombol, kartu yang sejajar. Pakai Grid saat butuh mengatur baris DAN kolom sekaligus secara eksplisit — layout halaman penuh (header, sidebar, konten, footer), galeri foto, atau tata letak dashboard. Keduanya sering dipakai bersamaan dalam satu halaman, untuk bagian yang berbeda.
Mengaktifkan grid dan kolom
.container {
display: grid;
grid-template-columns: 200px 200px 200px; /* 3 kolom, masing-masing 200px */
gap: 16px;
}fr unit: pembagian ruang yang fleksibel
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr; /* 3 kolom SAMA LEBAR, mengisi ruang tersedia */
}
.layout {
grid-template-columns: 250px 1fr; /* sidebar TETAP 250px, sisanya untuk konten */
}Live preview
fr (fraction/pecahan) adalah unit khusus Grid — 1fr 1fr 1fr membagi ruang tersedia jadi 3 bagian sama rata, tanpa perlu menghitung persentase manual.
repeat() dan auto-fit: grid responsif tanpa media query
.container {
display: grid;
grid-template-columns: repeat(3, 1fr); /* sama persis dengan "1fr 1fr 1fr" */
gap: 16px;
}
/* kolom OTOMATIS menyesuaikan jumlahnya berdasarkan lebar layar! */
.galeri {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 16px;
}
/* setiap kolom MINIMAL 180px, mengisi SISA ruang (1fr), dan JUMLAH kolom
otomatis berkurang di layar sempit — tanpa satu baris media query pun! */Catatan — Pola repeat(auto-fit, minmax(180px, 1fr)) adalah salah satu trik Grid paling berguna: grid galeri atau kartu produk otomatis menyesuaikan jumlah kolom sesuai lebar layar tanpa kode tambahan apa pun. Media query (Materi 18) tetap penting untuk banyak kasus lain, tapi pola ini sering menghemat beberapa baris kode untuk kasus grid sederhana.
grid-column dan grid-row: elemen yang membentang
.container {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
}
.unggulan {
grid-column: span 2; /* elemen ini membentang LEBAR 2 kolom */
}Live preview
Area grid bernama (named grid areas)
.layout {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-areas:
"header header"
"sidebar konten"
"footer footer";
gap: 12px;
}
header { grid-area: header; }
aside { grid-area: sidebar; }
main { grid-area: konten; }
footer { grid-area: footer; }
/* "gambar" layout langsung terlihat dari susunan string grid-template-areas! */Ini salah satu fitur Grid paling elegan — struktur layout keseluruhan halaman jadi terbaca langsung dari kode CSS-nya, seperti gambar ASCII dari tata letak sebenarnya.
Latihan — Buat galeri produk dengan grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)), isi minimal 6 kartu produk. Lalu buat layout halaman penuh (header, sidebar, konten, footer) memakai grid-template-areas.
Materi 18 · Modul 3
Responsive design dan media query
Membuat satu halaman yang tampil baik di HP, tablet, dan desktop — bukan membuat halaman terpisah untuk tiap ukuran.
Apa itu media query
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr; /* default: 3 kolom (asumsi desktop) */
}
@media (max-width: 768px) {
/* aturan DI DALAM blok ini HANYA berlaku saat lebar layar ≤ 768px */
.container {
grid-template-columns: 1fr; /* jadi 1 kolom di layar sempit */
}
}Catatan — @media (max-width: 768px) berarti "terapkan aturan ini kalau lebar layar 768px atau kurang". Angka seperti 768px disebut breakpoint — titik di mana tampilan berubah. Nilai ini tidak baku, pilih berdasarkan kapan layout mulai "terlihat sempit", biasanya dicek langsung dengan menyempitkan jendela browser atau DevTools mode responsive.
Breakpoint umum (sebagai acuan, bukan aturan mutlak)
| Breakpoint | Kira-kira untuk |
|---|---|
max-width: 480px | HP (potret) |
max-width: 768px | Tablet / HP (lanskap) |
max-width: 1024px | Tablet besar / laptop kecil |
min-width: 1280px | Desktop besar |
Hati-hati — Jangan memilih breakpoint berdasarkan ukuran perangkat spesifik ("iPhone 14 lebarnya sekian px") — ada ratusan ukuran layar berbeda di dunia nyata. Lebih baik lebarkan/sempitkan jendela browser secara manual sampai layoutmu mulai terlihat rusak, lalu taruh breakpoint tepat di titik itu.
Beberapa breakpoint sekaligus
.galeri { grid-template-columns: repeat(4, 1fr); } /* default: desktop, 4 kolom */
@media (max-width: 1024px) {
.galeri { grid-template-columns: repeat(3, 1fr); } /* tablet besar: 3 kolom */
}
@media (max-width: 768px) {
.galeri { grid-template-columns: repeat(2, 1fr); } /* tablet: 2 kolom */
}
@media (max-width: 480px) {
.galeri { grid-template-columns: 1fr; } /* HP: 1 kolom */
}Menyembunyikan/menampilkan elemen berdasarkan layar
.menu-mobile { display: none; } /* sembunyi di desktop (default) */
.menu-desktop { display: flex; }
@media (max-width: 768px) {
.menu-mobile { display: block; } /* tampil di HP */
.menu-desktop { display: none; } /* sembunyi di HP */
}Pola umum: tombol hamburger menu yang hanya muncul di layar sempit, sementara menu horizontal penuh tampil di desktop — keduanya ada di HTML yang sama, CSS yang menentukan mana yang terlihat.
Menguji responsive di DevTools
Catatan — Di Chrome/Firefox DevTools (F12), ada ikon perangkat kecil (mode "responsive"/"device toolbar") yang mensimulasikan berbagai ukuran layar langsung di browser desktop — jauh lebih cepat daripada bolak-balik cek di HP fisik saat masih dalam proses coding.
Latihan — Ambil galeri produk dari latihan Materi 17, tambahkan media query agar jumlah kolom berkurang di layar sempit (4 → 2 → 1). Uji dengan DevTools mode responsive, menyempitkan layar secara bertahap sampai nyaman di semua ukuran.
Materi 19 · Modul 3
Mobile-first dan viewport
Filosofi menulis CSS yang membuat kode lebih ringkas dan tampilan mobile lebih prioritas — bukan sekadar renungan belakangan.
Desktop-first vs mobile-first
.container { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 1024px) { .container { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 768px) { .container { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 480px) { .container { grid-template-columns: 1fr; } }.container { grid-template-columns: 1fr; } /* DEFAULT = tampilan HP, paling sederhana */
@media (min-width: 480px) { .container { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 768px) { .container { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1024px) { .container { grid-template-columns: repeat(4, 1fr); } }Catatan — "Mobile-first" berarti CSS dasar (di luar media query mana pun) ditulis untuk layar sempit, lalu @media (min-width: ...) menambahkan/mengubah aturan untuk layar yang lebih lebar secara bertahap. Pendekatan ini umumnya disukai karena dua alasan: paling banyak traffic web datang dari perangkat mobile, dan menulis dari yang paling sederhana lalu menambah kompleksitas terasa lebih alami daripada sebaliknya.
Mengapa meta viewport (dari Materi 8) sangat krusial
<meta name="viewport" content="width=device-width, initial-scale=1.0">Hati-hati — Tanpa tag viewport ini, SEMUA media query mobile-first di atas tidak akan bekerja seperti yang diharapkan — browser mobile akan tetap menganggap halamanmu selebar layar desktop (biasanya sekitar 980px) lalu menyusutkannya, sehingga min-width: 480px akan selalu dianggap terpenuhi bahkan di HP. width=device-width memberi tahu browser "pakai lebar perangkat sebenarnya", initial-scale=1.0 mencegah zoom otomatis saat halaman dimuat.
Unit relatif untuk responsive yang lebih mulus
.hero {
padding: 5vw; /* padding ikut proporsional dengan lebar layar */
font-size: clamp(1.5rem, 4vw, 3rem); /* minimal, ideal, maksimal — teks menyesuaikan OTOMATIS */
}clamp(minimum, ideal, maksimum) adalah fungsi CSS modern yang sangat berguna: nilai "ideal" (biasanya pakai vw) akan dipakai selama masih di antara minimum dan maksimum — hasilnya, ukuran font yang mengalir proporsional mengikuti lebar layar, tapi tidak pernah terlalu kecil atau terlalu besar.
Gambar yang responsif
img {
max-width: 100%; /* TIDAK PERNAH melebihi lebar induknya */
height: auto; /* tinggi menyesuaikan OTOMATIS, rasio tetap terjaga */
}Hati-hati — Tanpa max-width: 100%, gambar dengan ukuran asli yang besar bisa "bocor" keluar dari containernya di layar sempit, menyebabkan scroll horizontal yang tidak diinginkan. Dua baris CSS ini sering dianggap aturan dasar wajib untuk hampir semua gambar di web modern.
Latihan — Tulis ulang galeri produk dari Materi 18 dengan pendekatan mobile-first: mulai dari 1 kolom sebagai default, lalu tambahkan min-width media query untuk memperbanyak kolom di layar lebih lebar. Tambahkan img { max-width: 100%; height: auto; } ke semua gambar di halamanmu.
Materi 20 · Modul 3
Pola layout praktis
Menggabungkan semua yang sudah dipelajari Modul 3 menjadi pola-pola layout nyata yang akan kamu pakai berulang-ulang.
Pola 1: Navbar responsif
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 2rem;
}
.navbar ul { display: flex; gap: 1.5rem; list-style: none; }
@media (max-width: 768px) {
.navbar ul { flex-direction: column; } /* menu jadi vertikal di HP */
}Live preview
- Beranda
- Produk
- Kontak
Pola 2: Kartu produk (card)
.kartu {
border: 1px solid #e0e0e0;
border-radius: 10px;
overflow: hidden; /* gambar ikut rounded corner kartunya */
transition: transform 0.2s, box-shadow 0.2s;
}
.kartu:hover {
transform: translateY(-4px); /* naik sedikit saat hover */
box-shadow: 0 8px 20px rgba(0,0,0,0.1);
}
.kartu img { width: 100%; display: block; }
.kartu .isi { padding: 16px; }Live preview
Kaos Polos
Rp 85.000
Pola 3: Hero section
.hero {
min-height: 70vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: 2rem;
background: linear-gradient(135deg, #1D6FA5, #5CB3E8);
color: white;
}
.hero h1 { font-size: clamp(2rem, 5vw, 3.5rem); }Pola 4: Footer dengan beberapa kolom
.footer {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
gap: 2rem;
padding: 3rem 2rem;
background: #121A20;
color: #DCE4E8;
}
/* pakai pola auto-fit dari Materi 17 — kolom otomatis responsif tanpa media query tambahan */Menyusun semuanya dalam satu halaman
Catatan — Halaman web nyata biasanya adalah kombinasi dari pola-pola ini bertumpuk vertikal: <header> dengan navbar Flexbox, <main> berisi hero section lalu grid kartu produk, dan <footer> multi-kolom. Tidak perlu satu sistem layout untuk seluruh halaman — Flexbox dan Grid dipakai berdampingan, masing-masing di bagian yang paling cocok untuknya.
Latihan besar — Gabungkan semua pola di atas jadi satu halaman landing page utuh: navbar responsif di atas, hero section dengan judul besar, grid kartu produk (minimal 6), dan footer multi-kolom. Pastikan seluruhnya tetap rapi saat diuji di lebar layar HP, tablet, dan desktop lewat DevTools.
Materi 21 · Modul 4
Transition dan animation
Membuat perubahan tampilan terasa halus, bukan instan dan kasar — detail kecil yang besar dampaknya.
transition: perubahan dari A ke B secara halus
.tombol {
background: #1D6FA5;
transition: background 0.3s ease;
/* properti durasi gaya percepatan */
}
.tombol:hover { background: #154f78; }
/* beberapa properti sekaligus */
.kartu {
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
/* atau singkat: SEMUA properti yang berubah, durasi sama */
.kartu { transition: all 0.2s ease; }Live preview — arahkan kursor
Fungsi easing (timing function)
| Nilai | Rasanya |
|---|---|
linear | Kecepatan konstan — terasa kaku, jarang dipakai sendirian. |
ease | Default — mulai lambat, cepat di tengah, melambat di akhir. Paling umum. |
ease-in | Mulai lambat, berakhir cepat. |
ease-out | Mulai cepat, berakhir lambat — enak untuk elemen yang "mendarat". |
@keyframes: animasi multi-tahap
@keyframes muncul {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.toast {
animation: muncul 0.3s ease-out;
/* nama-keyframe durasi easing */
}@keyframes denyut {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
.badge-live {
animation: denyut 1.5s ease-in-out infinite; /* infinite = ulang terus-menerus */
}Live preview
LIVE sekarangCatatan — transition dipakai untuk perubahan antara DUA keadaan (biasanya dipicu :hover, :focus, atau perubahan class lewat JavaScript). @keyframes + animation dipakai untuk urutan perubahan yang berjalan sendiri, bisa berulang, dan bisa punya banyak tahap di antaranya (0%, 50%, 100%, dst) — cocok untuk hal seperti badge "live" yang berdenyut terus, atau loading spinner.
Hormati preferensi pengguna: prefers-reduced-motion
@media (prefers-reduced-motion: reduce) {
* {
animation: none !important;
transition: none !important;
}
}Catatan — Sebagian pengguna mengaktifkan pengaturan sistem "kurangi gerakan" karena animasi berlebihan bisa memicu pusing atau tidak nyaman bagi mereka — ini bagian dari aksesibilitas (Materi 7). Media query prefers-reduced-motion membaca preferensi sistem itu dan memungkinkan mematikan animasi untuk pengguna yang memintanya.
Latihan — Tambahkan transition pada tombol dan kartu produk dari latihan Materi 20 (efek hover halus). Buat satu animasi @keyframes sederhana (misalnya badge yang berdenyut, atau elemen yang muncul perlahan dengan fade-in) dan terapkan ke satu elemen di halamanmu.
Materi 22 · Modul 4
Custom property (CSS variables)
Menyimpan nilai yang dipakai berulang kali dalam satu tempat — fondasi untuk tema, termasuk dark mode.
Mendeklarasikan dan memakai variabel
:root {
/* :root = seluruh dokumen — variabel ini berlaku GLOBAL */
--warna-primer: #1D6FA5;
--warna-aksen: #C2501F;
--jarak-standar: 16px;
--radius-kartu: 10px;
}
.tombol {
background: var(--warna-primer);
padding: var(--jarak-standar);
border-radius: var(--radius-kartu);
}
.badge { background: var(--warna-aksen); }Catatan — Dengan custom property, mengganti warna tema utama cukup mengubah SATU baris di :root — semua elemen yang memakai var(--warna-primer) otomatis ikut berubah. Dibanding mencari-ganti nilai hex di puluhan tempat berbeda, ini jauh lebih aman dan cepat, terutama di project yang berkembang besar.
Nilai fallback (cadangan)
.kartu {
padding: var(--jarak-kartu, 20px);
/* kalau --jarak-kartu belum didefinisikan di mana pun, pakai 20px */
}Dark mode dengan custom property
:root {
--bg: #FFFFFF;
--teks: #121A20;
}
[data-theme="dark"] {
--bg: #121A20;
--teks: #F1F4F6;
}
body {
background: var(--bg);
color: var(--teks);
transition: background 0.2s, color 0.2s;
}const tombolTema = document.querySelector('#toggle-tema');
tombolTema.addEventListener('click', () => {
const html = document.documentElement;
const temaSaatIni = html.getAttribute('data-theme');
html.setAttribute('data-theme', temaSaatIni === 'dark' ? 'light' : 'dark');
});Inilah persis teknik yang dipakai handbook ini sendiri untuk tombol light/dark mode di pojok kanan atas — satu atribut data-theme di <html> menentukan set custom property mana yang aktif, dan seluruh halaman ikut berubah karena semua warnanya memakai var(...).
Mengakses dan mengubah variabel lewat JavaScript
document.documentElement.style.setProperty('--warna-primer', '#FF0000');
// mengubah custom property lewat JS, berguna untuk fitur seperti
// "pilih warna tema" interaktif yang dikontrol penggunaLatihan — Pindahkan semua nilai warna yang dipakai berulang di CSS latihanmu sebelumnya (Materi 20) menjadi custom property di :root. Ganti nilainya satu kali dan lihat bagaimana seluruh halaman ikut berubah secara konsisten.
Materi 23 · Modul 4
Transform dan efek visual
Memutar, menggeser, menskalakan elemen — dan efek visual lain yang memberi "rasa" modern pada halaman.
transform: translate, scale, rotate
.a { transform: translateX(20px); } /* geser 20px ke kanan — TIDAK mengubah layout elemen lain */
.b { transform: translateY(-10px); } /* geser 10px ke atas */
.c { transform: scale(1.1); } /* perbesar 110% */
.d { transform: rotate(15deg); } /* putar 15 derajat */
.e { transform: translateY(-4px) scale(1.03); } /* GABUNG beberapa transform sekaligus */Catatan — Keunggulan transform dibanding mengubah top/left/width langsung: transform tidak memengaruhi layout elemen lain di sekitarnya (elemen tetangga tidak perlu "menyesuaikan diri"), dan browser bisa merender transform jauh lebih efisien — karena itu, transform jadi pilihan utama untuk animasi hover (seperti di Materi 20 dan 21) demi performa yang mulus.
box-shadow: bayangan untuk kedalaman
.kartu {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
/* x y blur warna+transparansi */
}
/* beberapa shadow ditumpuk untuk efek lebih halus */
.kartu-elevated {
box-shadow:
0 1px 2px rgba(0,0,0,0.06),
0 8px 24px rgba(0,0,0,0.08);
}Live preview
opacity dan gradient
.overlay { opacity: 0.6; } /* 0 = tak terlihat, 1 = pekat penuh — berlaku ke SELURUH elemen termasuk isinya */
.hero {
background: linear-gradient(135deg, #1D6FA5, #5CB3E8);
/* sudut warna awal warna akhir */
}
.radial {
background: radial-gradient(circle, #5CB3E8, #1D6FA5); /* dari tengah menyebar keluar */
}Hati-hati — opacity membuat SELURUH elemen (termasuk teks di dalamnya) jadi transparan, berbeda dari rgba() (Materi 11) yang hanya membuat satu properti warna tertentu transparan. Overlay gelap transparan di atas gambar biasanya lebih baik dibuat dengan background: rgba(0,0,0,0.5) pada lapisan terpisah, bukan opacity pada elemen yang berisi teks — supaya tekstnya sendiri tidak ikut pudar.
filter: efek seperti Instagram, langsung di CSS
img { filter: grayscale(100%); } /* hitam-putih */
img { filter: blur(4px); } /* buram */
img { filter: brightness(1.2) contrast(1.1); } /* gabung beberapa efek */
.kartu-nonaktif img { filter: grayscale(100%); }
.kartu-nonaktif img:hover { filter: grayscale(0%); } /* kembali berwarna saat hover */Latihan — Pada kartu produk latihan Materi 20, tambahkan box-shadow yang lembut sebagai default, perkuat saat :hover (gabungkan dengan transform: translateY() yang sudah ada). Buat satu hero section dengan linear-gradient sebagai background.
Materi 24 · Modul 4 · Penutup
Praktik produksi dan ke mana setelah ini
Menutup handbook dengan kebiasaan yang membedakan CSS "asal jalan" dari CSS yang dirawat dengan baik.
CSS reset: titik awal yang konsisten
* {
box-sizing: border-box; /* dari Materi 12 — jadikan standar di SETIAP project */
margin: 0;
padding: 0;
}
img, video { max-width: 100%; display: block; }
body {
font-family: system-ui, sans-serif;
line-height: 1.6;
}
a { color: inherit; text-decoration: none; }
ul, ol { list-style: none; }Catatan — Tiap browser punya nilai default yang sedikit berbeda untuk margin, padding, dan ukuran heading — CSS reset meratakan semua itu ke titik awal yang konsisten dan diketahui, sehingga tampilan tidak tiba-tiba berbeda di browser yang berbeda. Project besar sering memakai reset yang lebih lengkap (misalnya "Modern CSS Reset" yang populer di komunitas), tapi reset sederhana di atas sudah cukup untuk kebanyakan kasus.
Mengorganisasi file CSS
css/
├── reset.css # reset dasar
├── variables.css # semua custom property (:root)
├── layout.css # navbar, footer, grid halaman
├── components.css # kartu, tombol, badge — elemen yang dipakai berulang
└── pages.css # style khusus satu halaman tertentuUntuk project kecil, satu file style.css sudah cukup. Begitu project membesar, memisahkan berdasarkan tanggung jawab (seperti struktur di atas) membuat kode lebih mudah ditemukan dan dirawat — pola yang serupa dengan pemisahan folder di handbook backend manapun yang sudah kamu pelajari.
Penamaan class yang konsisten (sekilas BEM)
/* BEM: Block__Element--Modifier — konvensi penamaan yang populer */
.kartu { } /* Block — komponen utama */
.kartu__judul { } /* Element — bagian DI DALAM block */
.kartu__harga { }
.kartu--diskon { } /* Modifier — VARIAN dari block */Catatan — Tidak wajib dipakai, tapi mengetahui BEM (Block Element Modifier) berguna karena konvensi ini sangat umum ditemui di project nyata — namanya mungkin terlihat aneh di awal, tapi tujuannya sederhana: mencegah tabrakan nama class dan membuat hubungan antar elemen (mana induk, mana bagian, mana variannya) jelas hanya dari namanya.
Checklist sebelum deploy
- ✅
* { box-sizing: border-box; }sudah diterapkan. - ✅ Meta viewport ada di setiap halaman (Materi 8 & 19).
- ✅ Diuji minimal di tiga lebar layar: HP, tablet, desktop.
- ✅ Semua gambar punya
alt, semua input form punya label (Materi 7). - ✅ Kontras warna teks terhadap background cukup jelas dibaca.
- ✅
:focustidak dihilangkan — pengguna keyboard tetap bisa menavigasi. - ✅ Tidak ada
!importantyang menumpuk tak terkendali (Materi 13). - ✅ CSS divalidasi tidak ada typo properti/nilai yang jelas salah.
Ke mana setelah handbook ini
Catatan — Dengan fondasi HTML & CSS ini, kamu siap melanjutkan ke beberapa arah:
- JavaScript dari Nol (handbook terpisah) — membuat halaman yang kamu bangun di sini jadi interaktif: memanipulasi elemen lewat DOM, menangani event, validasi form secara dinamis.
- React dari Nol (handbook terpisah) — membangun antarmuka dengan pendekatan berbasis komponen, memakai Tailwind CSS yang menulis utility class langsung di HTML/JSX (prinsip CSS yang sama, sintaks berbeda).
- Tailwind CSS — framework utility-first yang sangat populer; semua konsep Modul 2–4 di handbook ini (box model, Flexbox, Grid, spesifisitas) tetap jadi fondasi penting untuk memahaminya, karena Tailwind pada dasarnya "mengemas" properti CSS yang sama ke dalam nama class pendek.
- Sass/SCSS — "pra-prosesor" CSS yang menambah fitur seperti nesting dan variabel lebih lanjut (meski custom property di Materi 22 sudah mencakup banyak kebutuhan variabel modern).
Terima kasih sudah mengikuti handbook ini sampai akhir. Fondasi yang kamu bangun di sini — box model, Flexbox, Grid, responsive design — adalah dasar yang dipakai ulang di hampir semua teknologi frontend modern, apa pun framework yang kamu pilih selanjutnya.
Latihan penutup — Ambil landing page lengkap dari latihan Materi 20, terapkan checklist di atas satu per satu. Tambahkan CSS reset di awal file, custom property untuk warna tema (Materi 22), dan pastikan semuanya lolos uji responsive di tiga ukuran layar berbeda.