Portofolio adalah kartu nama digital bagi pengembang web: satu halaman yang merangkum siapa kita, apa yang telah dibuat, dan bagaimana cara menghubungi. Kali ini proyeknya naik tingkat dari halaman profil tunggal menjadi website satu halaman dengan navigasi antar-section. Melalui tokoh fiktif Siti Rahma, kita melatih tautan anchor, article, serta interaksi navigasi yang halus.

Hasil Akhir yang Dibangun #

Hasil akhirnya adalah satu halaman portofolio untuk Siti Rahma, pengembang web fiktif, dengan seluruh konten dalam satu dokumen. Fitur yang kita bangun secara bertahap:

  • Kerangka dokumen valid dengan metadata lengkap.
  • Navigasi berupa daftar tautan anchor menuju empat section ber-id.
  • Section hero, daftar proyek berbentuk article, keahlian, dan kontak.
  • Stylesheet berpalet biru dengan navigasi flexbox dan pengguliran halus.
  • Status tautan aktif lewat aria-current serta tombol kembali ke atas.

Prasyarat #

Proyek ini merangkai konsep dari section sebelumnya; baca dahulu bila materinya masih asing:

  • Section Struktur Dokumen dan Semantic HTML: kerangka dokumen dan elemen bermakna.
  • Section Links dan Navigasi: tautan anchor menuju id elemen.
  • Section HTML dan CSS: selector, warna, dan tata letak dasar.
  • Section HTML dan JavaScript: event click, classList, dan pemilih DOM.

Empat Bagian Proyek #

Kerjakan keempat bagian berurutan; setiap bagian memakai hasil bagian sebelumnya:

BagianFokus
Bagian 1: Menyusun Struktur HalamanKerangka valid: head, header, main, footer
Bagian 2: Navigasi dan SectionNav anchor, hero, article proyek, keahlian, kontak
Bagian 3: Styling DasarStylesheet biru: flexbox dan smooth scroll
Bagian 4: Interaksi Navigasiaria-current dan tombol kembali ke atas

Memakai File Pendukung #

Tiga folder proyek menyimpan berkas pendukung. Susun salinan lokalnya mengikuti pohon berikut:

text
website-portofolio/
├── assets/
│   └── logo.svg
├── styles/
│   └── style.css
├── tests/
│   └── cek-struktur.html
├── part-1-struktur-halaman/
│   └── portofolio.html
└── part-2-navigasi-dan-section/
    └── portofolio-navigasi.html
  • assets/logo.svg: logo monogram “SR” untuk header setiap tahap.
  • styles/style.css: stylesheet final, ditulis pada Bagian 3 dan dipakai Bagian 4.
  • tests/cek-struktur.html: checklist sepuluh butir pemeriksaan; kemajuannya tersimpan di localStorage.

Unduh contoh tiap bagian lewat tautan pada header code block, lalu letakkan sesuai pohon di atas agar jalur relatif seperti ../styles/style.css tetap benar.

Referensi resmi untuk pendalaman:

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

styles/style.css

Unduh berkas
css
/* Stylesheet proyek Website Portofolio - ditulis pada Bagian 3, dipakai Bagian 3 dan 4 */
:root {
  --warna-utama: #2451b3;
  --warna-teks: #1f2937;
  --warna-latar: #f3f4f6;
  --warna-putih: #ffffff;
  --jarak: 16px;
}

* {
  box-sizing: border-box;
}

html {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  font-family: system-ui, sans-serif;
  line-height: 1.6;
  color: var(--warna-teks);
  background-color: var(--warna-latar);
}

header {
  background-color: var(--warna-utama);
  color: var(--warna-putih);
  padding: var(--jarak);
}

header img {
  width: 56px;
  height: auto;
  border-radius: 50%;
}

nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--jarak);
  margin: var(--jarak) 0 0;
  padding: 0;
  list-style: none;
}

nav a {
  color: var(--warna-putih);
  text-decoration: none;
  font-weight: 600;
}

nav a.aktif {
  text-decoration: underline;
  text-underline-offset: 4px;
}

main {
  max-width: 960px;
  margin: 0 auto;
  padding: var(--jarak);
}

section {
  margin-bottom: calc(var(--jarak) * 2);
  padding: var(--jarak);
  background-color: var(--warna-putih);
  border-radius: 8px;
}

h2 {
  margin-top: 0;
  color: var(--warna-utama);
}

article {
  margin-top: var(--jarak);
  padding-top: var(--jarak);
  border-top: 1px solid #d1d5db;
}

img {
  max-width: 100%;
  height: auto;
}

footer {
  background-color: var(--warna-utama);
  color: var(--warna-putih);
}

footer p {
  margin: 0;
  padding: var(--jarak);
  text-align: center;
}

.tombol-atas {
  position: fixed;
  right: 16px;
  bottom: 16px;
  padding: 10px 16px;
  border: none;
  border-radius: 6px;
  background-color: var(--warna-teks);
  color: var(--warna-putih);
  cursor: pointer;
}

tests/cek-struktur.html

Unduh berkas
html
<!doctype html>
<html lang="id">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Checklist Pemeriksaan Website Portofolio</title>
  <style>
    body {
      font-family: system-ui, sans-serif;
      max-width: 720px;
      margin: 0 auto;
      padding: 16px;
      line-height: 1.6;
      color: #1f2937;
    }
    h1 {
      font-size: 1.4rem;
    }
    ul {
      padding-left: 20px;
    }
    li {
      margin-bottom: 8px;
    }
    #progres {
      width: 100%;
      height: 16px;
    }
  </style>
</head>
<body>
  <header>
    <img src="../assets/logo.svg" alt="Logo monogram SR" width="48" height="48">
    <h1>Checklist Pemeriksaan Website Portofolio</h1>
  </header>
  <p>Gunakan halaman ini untuk memeriksa hasil setiap Bagian proyek. Centang butir yang telah dipenuhi; kemajuan tersimpan otomatis di browser.</p>
  <p id="ringkasan" aria-live="polite">0 dari 10 butir terpenuhi.</p>
  <progress id="progres" value="0" max="10"></progress>
  <ul>
    <li><label><input type="checkbox" data-cek="1"> Dokumen dimulai dengan <code>doctype</code> dan <code>html lang="id"</code></label></li>
    <li><label><input type="checkbox" data-cek="2"> Head memuat <code>meta charset</code>, <code>viewport</code>, <code>title</code>, dan <code>description</code></label></li>
    <li><label><input type="checkbox" data-cek="3"> Nav dengan <code>aria-label</code> memuat tautan ke seluruh section</label></li>
    <li><label><input type="checkbox" data-cek="4"> Setiap tautan nav menuju id section yang benar-benar ada</label></li>
    <li><label><input type="checkbox" data-cek="5"> Section beranda, proyek, keahlian, dan kontak ber-id unik</label></li>
    <li><label><input type="checkbox" data-cek="6"> Setiap proyek dibungkus <code>article</code> dengan heading sendiri</label></li>
    <li><label><input type="checkbox" data-cek="7"> Logo dan gambar lain memakai teks <code>alt</code> yang menjelaskan isi</label></li>
    <li><label><input type="checkbox" data-cek="8"> Halaman terhubung ke stylesheet <code>styles/style.css</code></label></li>
    <li><label><input type="checkbox" data-cek="9"> Tautan nav aktif membawa <code>aria-current</code> saat dipilih</label></li>
    <li><label><input type="checkbox" data-cek="10"> Tombol kembali ke atas menggulir halaman dengan halus</label></li>
  </ul>
  <p>Sintaks seluruh file proyek juga dapat diperiksa dengan Nu Html Checker di <a href="https://validator.w3.org/nu/">validator.w3.org/nu</a>.</p>
  <script>
    const daftar = document.querySelectorAll("input[type='checkbox']");
    const progres = document.querySelector("#progres");
    const ringkasan = document.querySelector("#ringkasan");
    const KUNCI = "cek-website-portofolio";

    function simpan() {
      const status = [];
      daftar.forEach(function (butir) {
        status.push(butir.checked);
      });
      try {
        localStorage.setItem(KUNCI, JSON.stringify(status));
      } catch (galat) {
        // penyimpanan tidak tersedia; status tetap berlaku selama halaman terbuka
      }
    }

    function muat() {
      try {
        const status = JSON.parse(localStorage.getItem(KUNCI));
        if (Array.isArray(status)) {
          daftar.forEach(function (butir, indeks) {
            butir.checked = status[indeks] === true;
          });
        }
      } catch (galat) {
        // biarkan semua butir pada keadaan awalnya
      }
    }

    function perbarui() {
      let terisi = 0;
      daftar.forEach(function (butir) {
        if (butir.checked) {
          terisi += 1;
        }
      });
      progres.value = terisi;
      ringkasan.textContent = terisi + " dari " + daftar.length + " butir terpenuhi.";
    }

    muat();
    perbarui();
    daftar.forEach(function (butir) {
      butir.addEventListener("change", function () {
        perbarui();
        simpan();
      });
    });
  </script>
</body>
</html>