Grid sudah siap, tetapi bilah kiri masih kosong. Bagian 4 merapikan seluruh isi <aside class="bilah-filter">: tiga panel bergaya kartu — kategori, rentang harga, dan layanan toko — plus tombol atur ulang di dasarnya. Semua yang dikerjakan di sini masih tampilan; tombol kategori belum menyaring apa pun karena JavaScript baru masuk di Bagian 5. Meski begitu, struktur dan atributnya disiapkan sekarang: tombol kategori sudah membawa data-kategori, sehingga Bagian 5 tinggal memasang pendengar klik tanpa mengubah markup lagi.

Struktur Bilah Filter #

Setiap panel adalah <section class="panel-filter"> dengan judul kecil dan isinya. Gaya .panel-filter meniru kartu produk: latar permukaan putih, border tipis --warna-garis, radius 12 px, dan padding 16 px — konsistensi visual membuat halaman terasa dirancang oleh satu tangan. .panel-judul menampilkan label panel dalam huruf kapital 12.5 px dengan jarak huruf 0.08em dan warna teks lembut, memberi hierarki tanpa perlu ukuran besar.

Karena Bagian 1 sudah menetapkan position: sticky pada .bilah-filter, ketiga panel ini ikut menempel saat katalog digulir: wadahnya yang sticky, isinya mengalir biasa. .bilah-filter juga flex column dengan gap 16 px, maka jarak antarpanel dan tombol atur ulang otomatis seragam tanpa margin manual.

Tombol Kategori dan Daftar Cek #

Tombol kategori ditulis sebagai <button> sungguhan, bukan tautan bergaya tombol, karena fungsinya mengubah keadaan halaman:

html
<button class="filter-tombol" type="button" data-kategori="indoor" aria-pressed="false">Indoor</button>

.filter-tombol tampil sebagai blok penuh dengan teks rata kiri, padding 8 px 12 px, border abu-hijau, dan cursor: pointer. Keadaan aktifnya sudah disiapkan lewat .filter-tombol.aktif — border dan teks berubah ke warna utama, latar menjadi hijau lembut — tetapi class aktif sendiri baru dipasang oleh JavaScript di Bagian 5. Atribut data-kategori pada tiap tombol adalah jembatannya: nilainya (semua, indoor, rimbu, solar) nanti dibandingkan dengan data-kategori pada kartu. aria-pressed mengumumkan keadaan tombol kepada pembaca layar, dan JavaScript akan terus menyinkronkannya.

Panel layanan toko memakai daftar cek biasa: list-style: none dan padding: 0 membersihkan bullet, label dibuat flex agar kotak cek dan teks sejajar dengan gap 8 px, dan accent-color: var(--warna-utama) mewarnai kotak cek dengan hijau palet tanpa satu pun pseudo-element. Tiga pilihan — gratis ongkir, dikirim hari ini, garansi tumbuh — sengaja hanya antarmuka; menyaring berdasarkan layanan berada di luar cakupan proyek ini.

Rentang Harga Statis dan Atur Ulang #

Panel rentang harga meniru tampilan slider tanpa <input type="range">:

css
.geser-track { position: relative; height: 6px; border-radius: var(--radius-penuh); background-color: var(--warna-garis); }
.geser-isi   { position: absolute; inset: 0 18% 0 6%; border-radius: var(--radius-penuh); background-color: var(--warna-utama); }
.geser-thumb { position: absolute; top: 50%; left: 30%; transform: translate(-50%, -50%); }

.geser-track adalah relatif dengan tinggi 6 px sebagai lintasan. .geser-isi absolut dengan inset: 0 18% 0 6% menandai area terpilih: menempel di atas, terpotong 6% dari kiri dan 18% dari kanan. .geser-thumb adalah lingkaran 16 px yang dipusatkan pada titik 30% lewat top: 50% ditambah translate(-50%, -50%). Seluruh grup diberi aria-hidden="true" karena murni dekoratif; label Rp25.000–Rp150.000 di bawahnya berupa teks biasa sehingga tetap terbaca pembaca layar. Panel ini menutup bagian tampilan: tombol .tombol--sekunder berlabel Atur ulang berdiri di dasar bilah, dengan latar hijau lembut yang menunggu JavaScript menghubungkannya ke filter di bagian penutup.

css
/* ============================================================
   Katalog Tanaman — stylesheet utama
   Urutan: token → base → kerangka halaman → layout katalog
   → tombol → kartu tanaman → grid produk → panel filter
   → state dan gerak → responsive
   ============================================================ */

/* ---------- 1. Design token ---------- */
:root {
  /* Warna semantik tema hijau */
  --warna-latar: #f3f7f2;         --warna-permukaan: #ffffff;
  --warna-teks: #1f2a1d;          --warna-teks-lembut: #5b6b57;
  --warna-garis: #d9e4d5;         --warna-utama: #2f6b3a;
  --warna-utama-pekat: #215029;   --warna-utama-kontras: #ffffff;
  --warna-utama-lembut: #e3f0e0;  --warna-aksen: #b3372b;
  --warna-indoor: #2e5f8a;        --warna-solar: #8a5a12;
  --warna-fokus: #1d4ed8;

  /* Skala spasi 4/8 px */
  --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 dan bayangan */
  --radius-kecil: 6px; --radius: 12px; --radius-penuh: 999px;
  --bayangan-1: 0 1px 2px rgba(31, 42, 29, 0.08);
  --bayangan-2: 0 6px 16px rgba(31, 42, 29, 0.14);

  /* Tipografi dan gerak */
  --font-dasar: system-ui, "Segoe UI", sans-serif;
  --tinggi-baris: 1.55;
  --durasi-cepat: 120ms; --durasi: 200ms; --pelunakan: ease-out;

  /* Kerangka halaman */
  --lebar-isi: 1120px;
  --lebar-bilah: 264px;
}

/* ---------- 2. Base ---------- */
*, *::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, figure { 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 { padding-block: var(--jarak-5); 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-3); }
.brand img { width: 40px; height: 40px; }
.brand-nama { font-size: 20px; font-weight: 700; }
.tagline { font-size: 14px; color: var(--warna-teks-lembut); }

.laman-utama { padding-block: var(--jarak-7) var(--jarak-8); }

.judul-halaman { font-size: 28px; font-weight: 800; letter-spacing: -0.01em; }
.pengantar { margin-top: var(--jarak-2); max-width: 62ch; color: var(--warna-teks-lembut); }

.laman-kaki { background-color: var(--warna-permukaan); border-top: 1px solid var(--warna-garis); }

.kaki-baris { display: flex; justify-content: space-between; flex-wrap: wrap; gap: var(--jarak-2) var(--jarak-4); padding-block: var(--jarak-5); font-size: 13.5px; color: var(--warna-teks-lembut); }

/* ---------- 4. Layout katalog: bilah filter dan isi ---------- */
.layout-katalog {
  display: grid;
  grid-template-columns: var(--lebar-bilah) minmax(0, 1fr);
  gap: var(--jarak-6);
  align-items: start;
  margin-top: var(--jarak-6);
}

.bilah-filter {
  position: sticky; top: var(--jarak-5);
  display: flex; flex-direction: column;
  gap: var(--jarak-4);
}

.isi-katalog { min-width: 0; }

/* ---------- 5. Tombol ---------- */
.tombol {
  padding: 9px 18px; border: none; border-radius: var(--radius-kecil);
  background-color: var(--warna-utama);
  color: var(--warna-utama-kontras);
  font: inherit; font-weight: 600; cursor: pointer;
}

/* ---------- 6. Kartu tanaman ---------- */
.kartu-tanaman {
  display: flex; flex-direction: column;
  overflow: hidden;
  background-color: var(--warna-permukaan);
  border: 1px solid var(--warna-garis); border-radius: var(--radius);
  box-shadow: var(--bayangan-1);
}

.media-tanaman {
  aspect-ratio: 4 / 3;
  display: flex; flex-direction: column;
  align-items: flex-start; gap: var(--jarak-2);
  padding: var(--jarak-3);
  background-color: var(--warna-utama-lembut);
}

/* Foto produk digantikan dua lapis gradasi: lapisan pertama
   kilau cahaya, lapisan kedua warna khas tiap tanaman. */
.media--monstera    { background-image: linear-gradient(160deg, rgba(255, 255, 255, 0.30) 0%, rgba(255, 255, 255, 0) 55%), linear-gradient(135deg, #8fce8f 0%, #2f6b3a 100%); }
.media--lidah       { background-image: linear-gradient(160deg, rgba(255, 255, 255, 0.22) 0%, rgba(255, 255, 255, 0) 50%), linear-gradient(135deg, #3d7a5f 0%, #163823 100%); }
.media--aglaonema   { background-image: linear-gradient(160deg, rgba(255, 255, 255, 0.30) 0%, rgba(255, 255, 255, 0) 55%), linear-gradient(135deg, #c25b5b 0%, #6b2f4a 100%); }
.media--calathea    { background-image: linear-gradient(160deg, rgba(255, 255, 255, 0.28) 0%, rgba(255, 255, 255, 0) 55%), linear-gradient(135deg, #4f8a8b 0%, #274156 100%); }
.media--pakis       { background-image: linear-gradient(160deg, rgba(255, 255, 255, 0.28) 0%, rgba(255, 255, 255, 0) 55%), linear-gradient(135deg, #679436 0%, #2c4a1e 100%); }
.media--bougenville { background-image: linear-gradient(160deg, rgba(255, 255, 255, 0.32) 0%, rgba(255, 255, 255, 0) 55%), linear-gradient(135deg, #d16ba5 0%, #7a2f5c 100%); }
.media--kamboja     { background-image: linear-gradient(160deg, rgba(255, 255, 255, 0.40) 0%, rgba(255, 255, 255, 0) 60%), linear-gradient(135deg, #f2e3b3 0%, #d9a441 100%); }
.media--melati      { background-image: linear-gradient(160deg, rgba(255, 255, 255, 0.45) 0%, rgba(255, 255, 255, 0) 60%), linear-gradient(135deg, #eef7e9 0%, #a8c98a 100%); }

.lencana {
  padding: 3px 10px; border-radius: var(--radius-penuh);
  font-size: 11px; font-weight: 700;
  letter-spacing: 0.06em; text-transform: uppercase;
}

.lencana--indoor { background-color: var(--warna-indoor); color: #ffffff; }
.lencana--rimbu  { background-color: var(--warna-utama-lembut); color: var(--warna-utama-pekat); }
.lencana--solar  { background-color: #f7e3b8; color: var(--warna-solar); }
.lencana--promo  { background-color: var(--warna-aksen); color: #ffffff; }

.badan-kartu { display: flex; flex-direction: column; gap: var(--jarak-2); flex: 1; padding: var(--jarak-4); }

.nama-tanaman { font-size: 17px; font-weight: 700; line-height: 1.3; }
.keterangan { font-size: 13.5px; color: var(--warna-teks-lembut); }

.baris-beli {
  display: flex; align-items: center;
  justify-content: space-between; gap: var(--jarak-2);
  margin-top: auto; padding-top: var(--jarak-2);
}

.harga { font-size: 16px; font-weight: 700; color: var(--warna-utama-pekat); }

/* ---------- 7. Grid produk ---------- */
.info-hasil { margin-bottom: var(--jarak-4); font-size: 14px; color: var(--warna-teks-lembut); }

.grid-produk {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: var(--jarak-5);
}

/* ---------- 8. Panel filter ---------- */
.panel-filter {
  padding: var(--jarak-4);
  background-color: var(--warna-permukaan);
  border: 1px solid var(--warna-garis); border-radius: var(--radius);
}

.panel-judul { font-size: 12.5px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--warna-teks-lembut); }

.daftar-filter { display: grid; gap: var(--jarak-2); margin-top: var(--jarak-3); }

.filter-tombol {
  padding: 8px 12px; border: 1px solid var(--warna-garis);
  border-radius: var(--radius-kecil);
  background-color: var(--warna-permukaan);
  font: inherit; font-size: 14px; text-align: left; cursor: pointer;
}

.filter-tombol.aktif { border-color: var(--warna-utama); background-color: var(--warna-utama-lembut); color: var(--warna-utama-pekat); font-weight: 600; }

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

.daftar-cek label { display: flex; align-items: center; gap: var(--jarak-2); font-size: 14px; }

.daftar-cek input { width: 16px; height: 16px; accent-color: var(--warna-utama); }

.geser-harga { margin-top: var(--jarak-4); }

.geser-track { position: relative; height: 6px; border-radius: var(--radius-penuh); background-color: var(--warna-garis); }

.geser-isi { position: absolute; inset: 0 18% 0 6%; border-radius: var(--radius-penuh); background-color: var(--warna-utama); }

.geser-thumb {
  position: absolute; top: 50%; left: 30%;
  width: 16px; height: 16px;
  border: 3px solid var(--warna-utama); border-radius: var(--radius-penuh);
  background-color: var(--warna-permukaan);
  transform: translate(-50%, -50%);
}

.geser-label { display: flex; justify-content: space-between; margin-top: var(--jarak-2); font-size: 12.5px; color: var(--warna-teks-lembut); }

.tombol--sekunder { background-color: var(--warna-utama-lembut); color: var(--warna-utama-pekat); }

.bilah-filter .tombol--sekunder { margin-top: var(--jarak-2); }
html
<!DOCTYPE html>
<html lang="id">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Katalog Tanaman</title>
  <link rel="icon" href="assets/logo.svg" type="image/svg+xml">
  <link rel="stylesheet" href="css/styles.css">
</head>
<body>
  <header class="laman-kepala">
    <div class="pembungkus kepala-baris">
      <div class="brand">
        <img src="assets/logo.svg" alt="Logo Katalog Tanaman" width="40" height="40">
        <span class="brand-nama">Katalog Tanaman</span>
      </div>
      <p class="tagline">Rumah tanam mini untuk semua rumah</p>
    </div>
  </header>

  <main class="laman-utama">
    <div class="pembungkus">
      <h1 class="judul-halaman">Katalog tanaman hias</h1>
      <p class="pengantar">Delapan tanaman pilihan untuk memulai koleksi: dari penghuni sudut ruangan sampai penyuka matahari di teras.</p>

      <div class="layout-katalog">
        <aside class="bilah-filter" aria-label="Filter katalog">
          <section class="panel-filter">
            <h2 class="panel-judul">Kategori</h2>
            <div class="daftar-filter">
              <button class="filter-tombol aktif" type="button" data-kategori="semua" aria-pressed="true">Semua tanaman</button>
              <button class="filter-tombol" type="button" data-kategori="indoor" aria-pressed="false">Indoor</button>
              <button class="filter-tombol" type="button" data-kategori="rimbu" aria-pressed="false">Rimbu</button>
              <button class="filter-tombol" type="button" data-kategori="solar" aria-pressed="false">Solar</button>
            </div>
          </section>

          <section class="panel-filter">
            <h2 class="panel-judul">Rentang harga</h2>
            <div class="geser-harga" aria-hidden="true">
              <div class="geser-track"><span class="geser-isi"></span><span class="geser-thumb"></span></div>
            </div>
            <p class="geser-label"><span>Rp25.000</span><span>Rp150.000</span></p>
          </section>

          <section class="panel-filter">
            <h2 class="panel-judul">Layanan toko</h2>
            <ul class="daftar-cek">
              <li><label><input type="checkbox"> Gratis ongkir</label></li>
              <li><label><input type="checkbox"> Dikirim hari ini</label></li>
              <li><label><input type="checkbox"> Garansi tumbuh</label></li>
            </ul>
          </section>

          <button class="tombol tombol--sekunder" id="tombol-reset" type="button">Atur ulang</button>
        </aside>

        <section class="isi-katalog" aria-label="Daftar tanaman">
          <p class="info-hasil"><span id="hitung-katalog">8</span> tanaman ditemukan</p>
          <div class="grid-produk">
            <article class="kartu-tanaman">
              <div class="media-tanaman media--monstera" role="img" aria-label="Ilustrasi gradasi monstera">
                <span class="lencana lencana--indoor">Indoor</span>
                <span class="lencana lencana--promo">Terlaris</span>
              </div>
              <div class="badan-kartu">
                <h2 class="nama-tanaman">Monstera Deliciosa</h2>
                <p class="keterangan">Daun lebar bercelah khas; tumbuh rapi di sudut ruangan.</p>
                <div class="baris-beli"><span class="harga">Rp95.000</span><button class="tombol" type="button">Tambah</button></div>
              </div>
            </article>

            <article class="kartu-tanaman">
              <div class="media-tanaman media--lidah" role="img" aria-label="Ilustrasi gradasi lidah mertua">
                <span class="lencana lencana--indoor">Indoor</span>
              </div>
              <div class="badan-kartu">
                <h2 class="nama-tanaman">Lidah Mertua</h2>
                <p class="keterangan">Daun tegak berpola garis; toleran penyiraman jarang.</p>
                <div class="baris-beli"><span class="harga">Rp45.000</span><button class="tombol" type="button">Tambah</button></div>
              </div>
            </article>

            <article class="kartu-tanaman">
              <div class="media-tanaman media--aglaonema" role="img" aria-label="Ilustrasi gradasi aglaonema">
                <span class="lencana lencana--rimbu">Rimbu</span>
                <span class="lencana lencana--promo">Baru</span>
              </div>
              <div class="badan-kartu">
                <h2 class="nama-tanaman">Aglaonema Merah</h2>
                <p class="keterangan">Daun merah marun bermotif; senang tempat teduh.</p>
                <div class="baris-beli"><span class="harga">Rp78.000</span><button class="tombol" type="button">Tambah</button></div>
              </div>
            </article>

            <article class="kartu-tanaman">
              <div class="media-tanaman media--calathea" role="img" aria-label="Ilustrasi gradasi calathea">
                <span class="lencana lencana--rimbu">Rimbu</span>
              </div>
              <div class="badan-kartu">
                <h2 class="nama-tanaman">Calathea Orbifolia</h2>
                <p class="keterangan">Daun bulat bergaris silver; butuh kelembapan tinggi.</p>
                <div class="baris-beli"><span class="harga">Rp110.000</span><button class="tombol" type="button">Tambah</button></div>
              </div>
            </article>

            <article class="kartu-tanaman">
              <div class="media-tanaman media--pakis" role="img" aria-label="Ilustrasi gradasi pakis hias">
                <span class="lencana lencana--rimbu">Rimbu</span>
              </div>
              <div class="badan-kartu">
                <h2 class="nama-tanaman">Pakis Hias</h2>
                <p class="keterangan">Rumpun daun rimbun untuk beranda yang teduh.</p>
                <div class="baris-beli"><span class="harga">Rp52.000</span><button class="tombol" type="button">Tambah</button></div>
              </div>
            </article>

            <article class="kartu-tanaman">
              <div class="media-tanaman media--bougenville" role="img" aria-label="Ilustrasi gradasi bougenville">
                <span class="lencana lencana--solar">Solar</span>
                <span class="lencana lencana--promo">Terlaris</span>
              </div>
              <div class="badan-kartu">
                <h2 class="nama-tanaman">Bougenville</h2>
                <p class="keterangan">Bunga merah muda; mekar deras di matahari penuh.</p>
                <div class="baris-beli"><span class="harga">Rp60.000</span><button class="tombol" type="button">Tambah</button></div>
              </div>
            </article>

            <article class="kartu-tanaman">
              <div class="media-tanaman media--kamboja" role="img" aria-label="Ilustrasi gradasi kamboja">
                <span class="lencana lencana--solar">Solar</span>
              </div>
              <div class="badan-kartu">
                <h2 class="nama-tanaman">Kamboja Jepang</h2>
                <p class="keterangan">Bunga kuning lembut; kuat di teras yang panas.</p>
                <div class="baris-beli"><span class="harga">Rp85.000</span><button class="tombol" type="button">Tambah</button></div>
              </div>
            </article>

            <article class="kartu-tanaman">
              <div class="media-tanaman media--melati" role="img" aria-label="Ilustrasi gradasi melati">
                <span class="lencana lencana--solar">Solar</span>
                <span class="lencana lencana--promo">Baru</span>
              </div>
              <div class="badan-kartu">
                <h2 class="nama-tanaman">Melati</h2>
                <p class="keterangan">Putih wangi sepanjang tahun; primadona pekarangan.</p>
                <div class="baris-beli"><span class="harga">Rp38.000</span><button class="tombol" type="button">Tambah</button></div>
              </div>
            </article>
          </div>
        </section>
      </div>
    </div>
  </main>

  <footer class="laman-kaki">
    <div class="pembungkus kaki-baris">
      <span>Katalog Tanaman — proyek latihan CSS</span>
      <span>Semua foto tanaman digantikan gradasi warna</span>
    </div>
  </footer>
</body>
</html>

Ringkasan #

Bilah filter kini utuh sebagai antarmuka: tiga panel bergaya kartu berisi tombol kategori beratribut data-kategori, daftar cek dengan accent-color, slider harga statis dari inset dan translate, serta tombol atur ulang. Semua keadaan interaktifnya — class aktif, angka hasil, kartu tersembunyi — sengaja menunggu. Bagian 5 melengkapi halaman: media query untuk layar sempit, main.js yang menghubungkan tombol dengan kartu, dan sentuhan akhir berupa fokus keyboard serta penghormatan terhadap setelan gerak.