Seluruh chapter pada Course HTML ini mengajarkan potongan-potongan pengetahuan: satu chapter membahas elemen article, chapter lain membahas metadata atau stylesheet. Proyek ini menjadi tempat kita merangkai potongan tersebut menjadi satu karya utuh yang layak tayang. Format artikel dipilih karena merupakan jenis halaman web yang paling umum dijumpai, mulai dari blog pribadi hingga portal berita, sehingga keputusan struktur yang kita latih di sini langsung berguna pada pekerjaan nyata.
Hasil akhir proyek adalah satu halaman artikel bertema kebun rumah bernama Aneka Baca dengan fitur berikut:
- Struktur artikel semantik:
header,section,figuredenganfigcaption, danfooterberisi penulis serta tanggal terbit. - Navigasi antarartikel beserta metadata head yang lengkap:
description,canonical, dan Open Graph. - Stylesheet eksternal dengan token warna dan tipografi yang nyaman dibaca.
- Daftar isi berbasis
detailsdan tombol bookmark yang tersimpan melaluilocalStorage.
Proyek ini menuntut penguasaan beberapa section sebelumnya. Kerjakan bila kita telah melewati: Semantic HTML, Links dan Navigasi, Metadata dan SEO, HTML dan CSS, HTML dan JavaScript, serta Validation dan Code Quality. Konsep yang belum familiar sebaiknya dirujuk kembali pada chapter asalnya saat diperlukan.
Proyek dibagi menjadi empat bagian, berurutan dari kerangka sampai interaksi:
| Bagian | Fokus | Hasil tahap ini |
|---|---|---|
| Bagian 1 | Struktur artikel semantik | File artikel.html berisi satu artikel utuh |
| Bagian 2 | Navigasi dan metadata head | Link antarartikel serta description, canonical, Open Graph |
| Bagian 3 | Styling dasar | File styles/style.css untuk tipografi artikel |
| Bagian 4 | Peningkatan interaksi | Daftar isi details dan tombol bookmark localStorage |
Tiga folder pendukung menemani keempat bagian tersebut. Folder assets/ menyimpan gambar ilustrasi hero.svg yang dipakai pada figure artikel. Folder styles/ menyimpan style.css yang mulai ditulis pada Bagian 3 dan dipakai halaman artikel sampai akhir proyek. Folder tests/ menyimpan cek-struktur.html, sebuah halaman checklist mandiri dengan checkbox dan progres yang tersimpan otomatis; centang butir-butirnya setiap kali satu bagian tuntas. Struktur folder proyek tampak seperti berikut:
website-artikel/
├── bagian-1/
│ └── artikel.html
├── bagian-2/
│ └── artikel-navigasi.html
├── bagian-4/
│ ├── interaksi-artikel.html
│ └── interaksi-artikel.js
├── assets/
│ └── hero.svg
├── styles/
│ └── style.css
└── tests/
└── cek-struktur.htmlFile contoh pada setiap bagian berada satu tingkat di bawah akar proyek, sehingga rujukan ke aset bersama memakai awalan ../ — misalnya ../styles/style.css dan ../assets/hero.svg. Dengan struktur ini, setiap file contoh dapat diunduh dari halaman bagiannya lalu langsung bekerja tanpa penyesuaian path.
Tahap 1 dari proyek Website Artikel.
Buka → # Bagian 2: Navigasi dan MetadataTahap 2 dari proyek Website Artikel.
Buka → # Bagian 3: Styling DasarTahap 3 dari proyek Website Artikel.
Buka → # Bagian 4: Peningkatan InteraksiTahap 4 dari proyek Website Artikel.
Buka →Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
styles/style.css
Unduh berkas/* styles/style.css — Aneka Baca
Stylesheet bersama proyek Website Artikel.
Ditulis pada Bagian 3, dipakai seluruh halaman sampai akhir proyek. */
/* Token desain: ubah nilai di sini, seluruh halaman ikut menyesuaikan. */
:root {
--warna-teks: #26312b;
--warna-teks-lembut: #5c6b62;
--warna-latar: #faf7f1;
--warna-aksen: #1a6b54;
--warna-garis: #ddd5c7;
--lebar-baca: 65ch;
--font-artikel: Georgia, "Times New Roman", serif;
--font-antarmuka: "Segoe UI", Arial, sans-serif;
}
body {
margin: 0;
background-color: var(--warna-latar);
color: var(--warna-teks);
font-family: var(--font-artikel);
line-height: 1.7;
}
nav {
max-width: var(--lebar-baca);
margin-inline: auto;
padding: 1rem 1rem 0;
}
nav ul {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-wrap: wrap;
gap: 0.25rem 1.25rem;
font-family: var(--font-antarmuka);
font-size: 0.95rem;
}
nav a {
color: var(--warna-aksen);
}
article {
max-width: var(--lebar-baca);
margin-inline: auto;
padding: 1.5rem 1rem 3rem;
}
h1,
h2 {
font-family: var(--font-antarmuka);
line-height: 1.25;
}
h1 {
font-size: 2rem;
}
.lead {
font-size: 1.125rem;
color: var(--warna-teks-lembut);
}
figure {
margin: 2rem 0;
}
figure img {
max-width: 100%;
height: auto;
border-radius: 8px;
}
figcaption {
margin-top: 0.5rem;
font-size: 0.9rem;
color: var(--warna-teks-lembut);
}
article > footer {
margin-top: 2.5rem;
padding-top: 1rem;
border-top: 1px solid var(--warna-garis);
font-size: 0.95rem;
color: var(--warna-teks-lembut);
}
details {
border: 1px solid var(--warna-garis);
border-radius: 8px;
padding: 0.75rem 1rem;
margin: 0 0 2rem;
font-family: var(--font-antarmuka);
}
details ul {
margin: 0.5rem 0 0.25rem;
padding-left: 1.25rem;
}
#tombol-bookmark {
font-family: var(--font-antarmuka);
font-size: 1rem;
background-color: var(--warna-aksen);
color: #ffffff;
border: 0;
border-radius: 999px;
padding: 0.5rem 1.25rem;
cursor: pointer;
}
#tombol-bookmark[aria-pressed="true"] {
background-color: var(--warna-garis);
color: var(--warna-teks);
}
tests/cek-struktur.html
Unduh berkas<!doctype html>
<html lang="id">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Cek Struktur — Proyek Website Artikel</title>
<link rel="stylesheet" href="../styles/style.css">
<script src="cek-struktur.js" defer></script>
<style>
.cek {
list-style: none;
padding-left: 0;
}
.cek li {
margin-bottom: 0.5rem;
}
.cek input {
margin-right: 0.5rem;
}
#progres {
font-weight: 700;
}
</style>
</head>
<body>
<main>
<header>
<h1>Cek Struktur Proyek Website Artikel</h1>
<p>Halaman ini membantu memeriksa hasil setiap Bagian proyek Aneka Baca secara mandiri. Centang butir yang terpenuhi; progres tersimpan otomatis di browser dan tidak perlu dicentang ulang.</p>
<p id="progres">0 dari 14 butir terpenuhi</p>
</header>
<fieldset>
<legend>Struktur semantik (Bagian 1)</legend>
<ul class="cek">
<li><input type="checkbox" id="cek-s1" data-cek="s1"><label for="cek-s1">Dokumen memakai doctype, <code>html lang="id"</code>, <code>meta charset</code>, dan <code>title</code> gabungan judul artikel plus nama situs</label></li>
<li><input type="checkbox" id="cek-s2" data-cek="s2"><label for="cek-s2">Seluruh konten dibungkus satu elemen <code>article</code></label></li>
<li><input type="checkbox" id="cek-s3" data-cek="s3"><label for="cek-s3">Artikel memakai <code>header</code> berisi satu <code>h1</code> dan paragraf lead berclass</label></li>
<li><input type="checkbox" id="cek-s4" data-cek="s4"><label for="cek-s4">Gambar berada di dalam <code>figure</code> dengan <code>figcaption</code> berbahasa Indonesia</label></li>
<li><input type="checkbox" id="cek-s5" data-cek="s5"><label for="cek-s5">Setiap bagian isi memakai <code>section</code> dengan <code>h2</code> sendiri</label></li>
<li><input type="checkbox" id="cek-s6" data-cek="s6"><label for="cek-s6">Footer artikel berisi penulis dan <code>time</code> dengan atribut <code>datetime</code> format YYYY-MM-DD</label></li>
<li><input type="checkbox" id="cek-s7" data-cek="s7"><label for="cek-s7">Nav berisi daftar tautan dengan <code>aria-label</code> pembeda</label></li>
</ul>
</fieldset>
<fieldset>
<legend>Metadata head (Bagian 2)</legend>
<ul class="cek">
<li><input type="checkbox" id="cek-m1" data-cek="m1"><label for="cek-m1"><code>meta name="description"</code> berisi ringkasan satu-dua kalimat</label></li>
<li><input type="checkbox" id="cek-m2" data-cek="m2"><label for="cek-m2"><code>link rel="canonical"</code> menunjuk alamat resmi halaman</label></li>
<li><input type="checkbox" id="cek-m3" data-cek="m3"><label for="cek-m3"><code>og:title</code> dan <code>og:description</code> terisi dengan nilai berbeda dari kosong</label></li>
</ul>
</fieldset>
<fieldset>
<legend>File pendukung (Bagian 3)</legend>
<ul class="cek">
<li><input type="checkbox" id="cek-f1" data-cek="f1"><label for="cek-f1">File <code>assets/hero.svg</code> tersedia dan tampil pada halaman artikel</label></li>
<li><input type="checkbox" id="cek-f2" data-cek="f2"><label for="cek-f2">File <code>styles/style.css</code> terhubung lewat <code>link</code> dengan path <code>../styles/style.css</code></label></li>
<li><input type="checkbox" id="cek-f3" data-cek="f3"><label for="cek-f3">Warna dan jenis huruf diatur lewat token <code>:root</code>, bukan nilai lepas di tiap aturan</label></li>
</ul>
</fieldset>
<fieldset>
<legend>Interaksi (Bagian 4)</legend>
<ul class="cek">
<li><input type="checkbox" id="cek-i1" data-cek="i1"><label for="cek-i1">Daftar isi <code>details</code> tampil, dapat dibuka, dan tautannya melompat ke section terkait</label></li>
<li><input type="checkbox" id="cek-i2" data-cek="i2"><label for="cek-i2">Tombol bookmark mengubah label dan <code>aria-pressed</code> saat diklik</label></li>
<li><input type="checkbox" id="cek-i3" data-cek="i3"><label for="cek-i3">Status bookmark bertahan setelah halaman dimuat ulang</label></li>
</ul>
</fieldset>
<p><img src="../assets/hero.svg" alt="" width="112" height="63"></p>
<p>Halaman pemeriksaan ini juga memakai stylesheet proyek dan gambar aset melalui path relatif <code>../</code>, sekaligus menjadi contoh rujukan lintas folder.</p>
</main>
</body>
</html>
tests/cek-struktur.js
Unduh berkas// cek-struktur.js — checklist mandiri proyek dengan progres tersimpan.
// Digunakan bersama cek-struktur.html pada folder tests/ proyek.
const KUNCI_SIMPAN = "cek:website-artikel";
function bacaTersimpan() {
try {
return JSON.parse(localStorage.getItem(KUNCI_SIMPAN)) || {};
} catch (galat) {
return {};
}
}
function tulisTersimpan(nilai) {
try {
localStorage.setItem(KUNCI_SIMPAN, JSON.stringify(nilai));
} catch (galat) {
// Penyimpanan tidak tersedia; checklist tetap berfungsi tanpa persist.
}
}
function perbaruiProgres() {
const kotak = Array.from(document.querySelectorAll("[data-cek]"));
const terpenuhi = kotak.filter(function (item) {
return item.checked;
}).length;
const progres = document.querySelector("#progres");
if (progres) {
progres.textContent = terpenuhi + " dari " + kotak.length + " butir terpenuhi";
}
}
function pasang() {
const tersimpan = bacaTersimpan();
const kotak = Array.from(document.querySelectorAll("[data-cek]"));
kotak.forEach(function (item) {
item.checked = tersimpan[item.dataset.cek] === true;
item.addEventListener("change", function () {
const nilai = bacaTersimpan();
nilai[item.dataset.cek] = item.checked;
tulisTersimpan(nilai);
perbaruiProgres();
});
});
perbaruiProgres();
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", pasang);
} else {
pasang();
}