Sampai Bagian 4, navigasi hanya tiga tautan berjajar — aman di desktop, tetapi pada ponsel tiga tautan plus brand akan berdesakan. Bagian 5 menyelesaikannya dengan pola paling umum di web: tautan disembunyikan di layar sempit, tombol hamburger ditampilkan, dan JavaScript kecil bertugas membuka-menutupnya. Ini juga kemunculan pertama main.js dalam proyek, ditulis vanilla tanpa pustaka dan hanya 33 baris. Prinsip yang kita pegang: CSS mengatur penampilan menu, HTML menyediakan semantik button dan nav, sedangkan JavaScript hanya menukar satu class dan satu atribut.

Tombol Hamburger dari Pseudo-Element #

Ikon tiga garis tidak memakai gambar maupun ikon font, melainkan satu span dan dua pseudo-element:

css
.tombol-nav { display: none; flex-direction: column; align-items: center; justify-content: center;
  width: 44px; height: 44px; padding: 0;
  border: 1px solid var(--warna-garis);
  border-radius: var(--radius-kecil);
  background-color: var(--warna-permukaan); cursor: pointer; }

.tombol-nav-garis { position: relative; display: block; width: 20px; height: 2px; background-color: var(--warna-teks); }

.tombol-nav-garis::before, .tombol-nav-garis::after {
  content: ""; position: absolute; left: 0;
  width: 20px; height: 2px; background-color: var(--warna-teks);
}

Baris demi baris: display: none menyembunyikan tombol di desktop karena navigasi di sana sudah terlihat; width: 44px; height: 44px memenuhi ukuran target sentuh yang disarankan untuk jari; flex-direction: column dengan dua properti perataan menaruh garis tengah tepat di pusat tombol. Garis atas dan bawah lahir dari ::before dan ::after yang diposisikan top: -6px dan top: 6px relatif terhadap garis tengah. Teks “Buka atau tutup menu navigasi” di dalam tombol dibungkus class .sr-only, sehingga tak terlihat oleh mata tetapi tetap terbaca oleh pembaca layar.

Media Query dan JavaScript Penggerak #

Perilaku pindah perangkat dititipkan pada satu media query:

css
@media (max-width: 720px) {
  .tombol-nav { display: flex; }
  .nav-utama {
    display: none;
    flex-direction: column;
    gap: var(--jarak-2);
    width: 100%;
    padding-block: var(--jarak-3);
  }
  .nav-utama.terbuka { display: flex; }
}

Di bawah 720 px tombol muncul, navigasi disembunyikan, dan class terbuka (dipasang lewat JavaScript) membuatnya tampil lagi sebagai kolom penuh. Karena .kepala-baris sudah punya flex-wrap: wrap sejak Bagian 2, navigasi otomatis turun ke baris kedua di bawah brand.

main.js hanya mengelola dua hal. Fungsi aturMenu(terbuka) memanggil classList.toggle("terbuka", terbuka) lalu menyelaraskan aria-expanded pada tombol — atribut inilah yang memberi tahu pembaca layar bahwa menu sedang terbuka atau tertutup. Pendengar klik pada tombol memanggil aturMenu dengan kebalikan keadaan saat ini, dan pendengar kedua menutup menu ketika salah satu tautan dipilih supaya daftar tidak menutupi konten yang baru dituju. Seluruh perubahan bersifat sementara di memori; tidak ada data yang disimpan di browser.

Snapshot Berkas Tahap Ini #

Tiga berkas berikut adalah kondisi proyek selelah Bagian 5 — pertama kalinya main.js ikut dalam snapshot:

css
/* ============================================================
   Portofolio Desainer — stylesheet utama
   Kondisi: selelah Bagian 5
   Urutan seksi: token, base, kerangka, tombol, hero,
   galeri proyek, tentang, kontak, footer, navigasi responsif,
   responsif, tipografi seksi, keadaan interaksi, aksesibilitas
   ============================================================ */

/* ---------- 1. Design token ---------- */
:root {
  /* Warna semantik, tema terang */
  --warna-latar: #faf7f2;         --warna-permukaan: #ffffff;
  --warna-teks: #1c1917;          --warna-teks-lembut: #78716c;
  --warna-garis: #e7e5e4;         --warna-utama: #be123c;
  --warna-utama-pekat: #9f1239;   --warna-utama-kontras: #ffffff;
  --warna-utama-lembut: #ffe4e6;  --warna-utama-tinta: #881337;
  --warna-aksen: #0f766e;         --warna-aksen-lembut: #ccfbf1;

  /* Skala spasi, basis empat dan delapan piksel */
  --jarak-1: 4px;  --jarak-2: 8px;
  --jarak-3: 12px; --jarak-4: 16px;
  --jarak-5: 24px; --jarak-6: 32px;
  --jarak-7: 48px; --jarak-8: 64px;

  /* Radius sudut */
  --radius-kecil: 8px; --radius: 14px;
  --radius-penuh: 999px;

  /* Elevasi bayangan */
  --bayangan-1: 0 1px 2px rgba(28, 25, 23, 0.06);
  --bayangan-2: 0 8px 20px rgba(28, 25, 23, 0.10);
  --bayangan-3: 0 16px 32px rgba(190, 18, 60, 0.16);

  /* Tipografi dan gerak */
  --font-dasar: system-ui, "Segoe UI", sans-serif;
  --tinggi-baris: 1.65;

  --durasi: 200ms; --pelunakan: ease-out;

  /* Kerangka halaman */
  --lebar-isi: 1020px;
}

/* ---------- 2. Base dan reset ---------- */
*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0; font-family: var(--font-dasar); line-height: var(--tinggi-baris);
  background-color: var(--warna-latar); color: var(--warna-teks);
}

h1, h2, h3, p, ul { margin: 0; }
img { display: block; max-width: 100%; }

/* ---------- 3. Kerangka halaman ---------- */
.pembungkus {
  max-width: var(--lebar-isi);
  margin-inline: auto; padding-inline: var(--jarak-4);
}

.laman-kepala {
  position: sticky; top: 0; z-index: 20;
  padding-block: var(--jarak-3);
  background-color: var(--warna-permukaan);
  border-bottom: 1px solid var(--warna-garis);
}

.kepala-baris {
  display: flex; align-items: center;
  justify-content: space-between;
  gap: var(--jarak-3); flex-wrap: wrap;
}

.brand { display: flex; align-items: center; gap: var(--jarak-2); }
.brand img { width: 34px; height: 34px; }
.brand-nama { font-size: 18px; font-weight: 800; letter-spacing: -0.01em; }

.nav-utama { display: flex; gap: var(--jarak-4); }
.nav-utama a { color: var(--warna-teks-lembut); font-size: 15px; font-weight: 600; text-decoration: none; }
.nav-utama a:hover { color: var(--warna-utama); }

/* ---------- 4. Tombol ---------- */
.tombol {
  display: inline-block;
  padding: 10px 20px; border: none;
  border-radius: var(--radius-kecil);
  background-color: var(--warna-utama); color: var(--warna-utama-kontras);
  font: inherit; font-size: 15px; font-weight: 600;
  text-decoration: none; cursor: pointer;
}

.tombol--garis {
  border: 1px solid var(--warna-utama);
  background-color: transparent; color: var(--warna-utama);
}

/* ---------- 5. Hero dan perkenalan ---------- */
.hero { padding-block: var(--jarak-8) var(--jarak-7); max-width: 680px; }

.hero-pembuka {
  font-size: 14px; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--warna-utama);
}

.judul-halaman {
  margin-top: var(--jarak-3);
  font-size: clamp(2rem, 5vw, 3rem);
  line-height: 1.15; letter-spacing: -0.02em;
}

.pengantar { margin-top: var(--jarak-4); max-width: 52ch; font-size: 17px; color: var(--warna-teks-lembut); }

.hero-aksi { display: flex; flex-wrap: wrap; gap: var(--jarak-3); margin-top: var(--jarak-5); }

.section-judul { font-size: 24px; font-weight: 800; letter-spacing: -0.01em; }

.perkenalan {
  max-width: 62ch; padding-block: var(--jarak-6);
  border-top: 1px solid var(--warna-garis);
}

.perkenalan p + p { margin-top: var(--jarak-3); }

/* ---------- 6. Galeri proyek ---------- */
.seksi-karya { margin-top: var(--jarak-7); }

.galeri-proyek {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--jarak-5); margin-top: var(--jarak-5);
}

.kartu-proyek {
  overflow: hidden;
  background-color: var(--warna-permukaan); border: 1px solid var(--warna-garis);
  border-radius: var(--radius); box-shadow: var(--bayangan-1);
}

.sampul { display: grid; place-items: center; aspect-ratio: 16 / 10; }

.sampul--kopi { background: linear-gradient(135deg, #7c2d12, #ea580c); }
.sampul--setor { background: linear-gradient(135deg, #1e3a8a, #3b82f6); }
.sampul--katalog { background: linear-gradient(135deg, #831843, #ec4899); }
.sampul--lumbung { background: linear-gradient(135deg, #134e4a, #14b8a6); }
.sampul--senja { background: linear-gradient(135deg, #312e81, #6366f1); }
.sampul--rupa { background: linear-gradient(135deg, #18181b, #71717a); }

.sampul-huruf { font-size: 64px; font-weight: 800; color: rgba(255, 255, 255, 0.92); }

.kartu-isi { padding: var(--jarak-4) var(--jarak-4) var(--jarak-5); }
.kartu-nama { font-size: 18px; font-weight: 800; }
.kartu-peran { margin-top: 2px; font-size: 13px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--warna-utama); }
.kartu-catatan { margin-top: var(--jarak-2); font-size: 14.5px; color: var(--warna-teks-lembut); }

/* ---------- 7. Tentang dan keahlian ---------- */
.seksi-tentang { margin-top: var(--jarak-7); }

.tentang-grid {
  display: grid; grid-template-columns: 1.4fr 1fr;
  gap: var(--jarak-6); margin-top: var(--jarak-4);
}

.tentang-bio p + p { margin-top: var(--jarak-3); }
.tentang-bio p { color: var(--warna-teks-lembut); }

.tentang-label {
  font-size: 13px; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--warna-aksen);
}

.daftar-chip {
  display: flex; flex-wrap: wrap;
  gap: var(--jarak-2); margin-top: var(--jarak-3);
  padding: 0; list-style: none;
}

.chip {
  padding: 6px 14px;
  border: 1px solid var(--warna-garis);
  border-radius: var(--radius-penuh);
  background-color: var(--warna-permukaan);
  font-size: 14px; font-weight: 600;
  color: var(--warna-teks-lembut);
}

/* ---------- 8. Kontak ---------- */
.seksi-kontak { margin-top: var(--jarak-7); }

.panel-kontak {
  display: grid; justify-items: start;
  gap: var(--jarak-3); padding: var(--jarak-7) var(--jarak-6);
  border-radius: var(--radius);
  background: linear-gradient(135deg, var(--warna-utama-tinta), var(--warna-utama));
  color: var(--warna-utama-kontras);
}

.panel-kontak p { max-width: 46ch; color: #ffe4e6; }

.panel-kontak .tombol { background-color: var(--warna-permukaan); color: var(--warna-utama-tinta); }

/* ---------- 9. Footer ---------- */
.laman-kaki {
  margin-top: var(--jarak-8); padding-block: var(--jarak-6);
  background-color: var(--warna-permukaan);
  border-top: 1px solid var(--warna-garis);
}

.kaki-baris {
  display: flex; align-items: center;
  justify-content: space-between;
  gap: var(--jarak-3); flex-wrap: wrap;
  font-size: 14px; color: var(--warna-teks-lembut);
}

/* ---------- 10. Navigasi responsif ---------- */
.tombol-nav {
  display: none; flex-direction: column;
  align-items: center; justify-content: center;
  width: 44px; height: 44px; padding: 0;
  border: 1px solid var(--warna-garis);
  border-radius: var(--radius-kecil);
  background-color: var(--warna-permukaan); cursor: pointer;
}

.tombol-nav-garis { position: relative; display: block; width: 20px; height: 2px; background-color: var(--warna-teks); }

.tombol-nav-garis::before, .tombol-nav-garis::after {
  content: ""; position: absolute; left: 0;
  width: 20px; height: 2px; background-color: var(--warna-teks);
}

.tombol-nav-garis::before { top: -6px; }
.tombol-nav-garis::after { top: 6px; }

.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }

/* ---------- 11. Responsif ---------- */
@media (max-width: 720px) {
  .tombol-nav { display: flex; }

  .nav-utama {
    display: none;
    flex-direction: column;
    gap: var(--jarak-2);
    width: 100%;
    padding-block: var(--jarak-3);
  }

  .nav-utama.terbuka { display: flex; }
}
html
<!DOCTYPE html>
<html lang="id">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Lina Kartika — Portofolio Desainer</title>
  <link rel="icon" href="assets/logo.svg" type="image/svg+xml">
  <link rel="stylesheet" href="css/styles.css">
  <script src="js/main.js" defer></script>
</head>
<body>
  <header class="laman-kepala">
    <div class="pembungkus kepala-baris">
      <div class="brand">
        <img src="assets/logo.svg" alt="Logo Lina Kartika" width="34" height="34">
        <span class="brand-nama">Lina Kartika</span>
      </div>
      <button class="tombol-nav" id="tombol-nav" type="button" aria-expanded="false" aria-controls="nav-utama">
        <span class="sr-only">Buka atau tutup menu navigasi</span>
        <span class="tombol-nav-garis" aria-hidden="true"></span>
      </button>
      <nav class="nav-utama" id="nav-utama" aria-label="Navigasi utama">
        <a href="#karya">Karya</a>
        <a href="#tentang">Tentang</a>
        <a href="#kontak">Kontak</a>
      </nav>
    </div>
  </header>

  <main id="konten-utama">
    <div class="pembungkus">

      <section class="hero" aria-labelledby="judul-hero">
        <p class="hero-pembuka">Portofolio Desainer Produk</p>
        <h1 class="judul-halaman" id="judul-hero">Saya Lina — saya merancang produk digital yang nyaman dipakai.</h1>
        <p class="pengantar">Enam tahun terakhir saya membantu startup dan UMKM mengubah ide menjadi antarmuka yang jelas: mulai dari riset pengguna, wireframe, sistem desain, sampai dukungan implementasi bersama programmer.</p>
        <div class="hero-aksi">
          <a class="tombol" href="#karya">Lihat karya terpilih</a>
          <a class="tombol tombol--garis" href="#kontak">Hubungi saya</a>
        </div>
      </section>

      <section class="perkenalan" aria-labelledby="judul-perkenalan">
        <h2 class="section-judul" id="judul-perkenalan">Perkenalan</h2>
        <p>Saya desainer produk yang berbasis di Yogyakarta. Latar belakang saya di desain komunikasi visual membuat saya peka pada hierarki, tipografi, dan ruang kosong — tiga hal yang menentukan apakah sebuah halaman nyaman dibaca atau tidak.</p>
        <p>Di luar pekerjaan klien, saya menulis catatan praktik desain dan menggambar pola batik digital. Halaman portofolio ini sendiri saya bangun dari nol memakai HTML dan CSS murni sebagai latihan menyatukan seluruh materi kursus.</p>
      </section>


      <section class="seksi-karya" id="karya" aria-labelledby="judul-karya">
        <h2 class="section-judul" id="judul-karya">Karya Terpilih</h2>
        <div class="galeri-proyek">
          <article class="kartu-proyek">
            <div class="sampul sampul--kopi" aria-hidden="true"><span class="sampul-huruf">K</span></div>
            <div class="kartu-isi">
              <h3 class="kartu-nama">Kopi Relung</h3>
              <p class="kartu-peran">Identitas Visual</p>
              <p class="kartu-catatan">Merek kedai kopi rakyat: logotype, kemasan, dan menu papan dari satu sistem warna hangat.</p>
            </div>
          </article>
          <article class="kartu-proyek">
            <div class="sampul sampul--setor" aria-hidden="true"><span class="sampul-huruf">S</span></div>
            <div class="kartu-isi">
              <h3 class="kartu-nama">Setor</h3>
              <p class="kartu-peran">Desain UI Mobile</p>
              <p class="kartu-catatan">Aplikasi pencatat tabungan harian dengan alur setor dan tarik tiga langkah.</p>
            </div>
          </article>
          <article class="kartu-proyek">
            <div class="sampul sampul--katalog" aria-hidden="true"><span class="sampul-huruf">W</span></div>
            <div class="kartu-isi">
              <h3 class="kartu-nama">Warnai Katalog</h3>
              <p class="kartu-peran">Desain Web</p>
              <p class="kartu-catatan">Katalog produk kerajinan dengan kartu responsif dan halaman detail yang ringan.</p>
            </div>
          </article>
          <article class="kartu-proyek">
            <div class="sampul sampul--lumbung" aria-hidden="true"><span class="sampul-huruf">L</span></div>
            <div class="kartu-isi">
              <h3 class="kartu-nama">Lumbung Data</h3>
              <p class="kartu-peran">Desain Produk</p>
              <p class="kartu-catatan">Dasbor panen untuk kelompok tani: tabel, grafik, dan mode gelap yang bisa diandalkan.</p>
            </div>
          </article>
          <article class="kartu-proyek">
            <div class="sampul sampul--senja" aria-hidden="true"><span class="sampul-huruf">A</span></div>
            <div class="kartu-isi">
              <h3 class="kartu-nama">Senja Literasi</h3>
              <p class="kartu-peran">Tipografi Editorial</p>
              <p class="kartu-catatan">Majalah daring sastra muda dengan skala huruf besar dan kolom baca 65 karakter.</p>
            </div>
          </article>
          <article class="kartu-proyek">
            <div class="sampul sampul--rupa" aria-hidden="true"><span class="sampul-huruf">R</span></div>
            <div class="kartu-isi">
              <h3 class="kartu-nama">Ruang Rupa</h3>
              <p class="kartu-peran">Desain Interaksi</p>
              <p class="kartu-catatan">Galeri digital karya seni grafis dengan turun pandu dan kartu karya yang bisa dibandingkan.</p>
            </div>
          </article>
        </div>
      </section>


      <section class="seksi-tentang" id="tentang" aria-labelledby="judul-tentang">
        <h2 class="section-judul" id="judul-tentang">Tentang Saya</h2>
        <div class="tentang-grid">
          <div class="tentang-bio">
            <p>Saya memulai karier sebagai desainer grafis di percetakan, lalu pindah ke produk digital karena tertarik pada sisi perilaku pengguna. Sejak itu saya bergabung dengan dua studio dan satu tim produk, menangani riset, desain antarmuka, dan sistem desain.</p>
            <p>Cara kerja saya sederhana: pahami dulu masalahnya, gambar solusi serendah mungkin, uji dengan pengguna nyata, lalu rawat bersama programmer. Desain yang baik menurut saya adalah yang diam-diam membuat tugas pengguna selesai lebih cepat.</p>
          </div>
          <div class="tentang-keahlian">
            <h3 class="tentang-label">Keahlian</h3>
            <ul class="daftar-chip">
              <li class="chip">Riset Pengguna</li>
              <li class="chip">Wireframe</li>
              <li class="chip">Figma</li>
              <li class="chip">Sistem Desain</li>
              <li class="chip">Desain Antarmuka</li>
              <li class="chip">Tipografi</li>
              <li class="chip">Prototype</li>
              <li class="chip">HTML &amp; CSS</li>
            </ul>
          </div>
        </div>
      </section>


      <section class="seksi-kontak" id="kontak" aria-labelledby="judul-kontak">
        <div class="panel-kontak">
          <h2 class="section-judul" id="judul-kontak">Mari Berkolaborasi</h2>
          <p>Saya membuka slot proyek untuk kuartal berikutnya, terutama pada bidang produk keuangan dan pendidikan. Ceritakan kebutuhan Anda, dan saya akan membalas dalam dua hari kerja.</p>
          <a class="tombol" href="mailto:[email protected]">Kirim surel ke saya</a>
        </div>
      </section>


    </div>
  </main>



  <footer class="laman-kaki">
    <div class="pembungkus kaki-baris">
      <span>Lina Kartika — proyek latihan CSS</span>
      <span>Desain produk, antarmuka, dan identitas visual</span>
    </div>
  </footer>
</body>
</html>
js
/* ============================================================
   main.js — navigasi Portofolio Desainer.
   Skrip ini membuka dan menutup menu navigasi pada layar sempit
   dan menyelaraskan aria-expanded pada tombolnya. Tidak ada data
   yang disimpan di sisi browser.
   ============================================================ */
(function () {
  "use strict";

  var tombolNav = document.getElementById("tombol-nav");
  var navUtama = document.getElementById("nav-utama");

  /* Buka atau tutup menu, lalu laporkan statusnya ke aria-expanded. */
  function aturMenu(terbuka) {
    navUtama.classList.toggle("terbuka", terbuka);
    tombolNav.setAttribute("aria-expanded", terbuka ? "true" : "false");
  }

  if (tombolNav && navUtama) {
    tombolNav.addEventListener("click", function () {
      aturMenu(!navUtama.classList.contains("terbuka"));
    });

    /* Menu ikut tertutup setelah salah satu tautan dipilih. */
    navUtama.addEventListener("click", function (acara) {
      if (acara.target.tagName === "A") {
        aturMenu(false);
      }
    });
  }

})();

Ringkasan #

Navigasi kini bekerja di semua lebar layar: berjajar di desktop, runtuh rapi di balik hamburger yang melaporkan status aria-expanded. Bagian 6 naik satu lapis lagi, menyempurnakan sistem tipografi dan warna yang selama ini masih memakai gaya awal.