Halaman harga yang meyakinkan tidak dimulai dari kartu paket, melainkan dari panggungnya. Sebelum satu pun angka ditulis, kita siapkan dulu kerangka halaman: token desain yang mengatur warna dan jarak, pembungkus yang menjaga lebar konten tetap terbaca, header dengan logo dan navigasi, seksi hero berisi judul dan pengantar, serta footer. Bagian ini menyelesaikan seluruh lapisan tersebut, sehingga Bagian 2 cukup menaruh kartu harga di atas fondasi yang rapi. Berkas styles.css pada tahap ini memuat tiga seksi pertama dari sepuluh seksi yang akan kita bangun sepanjang proyek.

Design Token Warna dan Spasi #

Palet halaman ini dipimpin warna indigo. Token --warna-utama memakai nilai #4f46e5, lalu mendapat tiga turunan: --warna-utama-pekat untuk keadaan hover tombol, --warna-utama-kontras untuk teks di atas tombol utama, --warna-utama-lembut untuk latar kepala tabel, dan --warna-utama-tinta untuk teks kepala tabel yang lebih gelap. Warna netral dikelompokkan per-peran: --warna-latar untuk latar halaman, --warna-permukaan untuk bidang kartu dan header, --warna-teks dan --warna-teks-lembut untuk dua tingkat teks, serta --warna-garis untuk border tipis. Dua token tambahan menyiapkan aksen: --warna-sukses untuk tanda centang fitur, dan pasangan --warna-hemat-latar plus --warna-hemat-teks untuk lencana “Hemat 2 bulan”.

Skala spasi --jarak-1 sampai --jarak-8 mengikuti basis 4 px dan 8 px: 4, 8, 12, 16, 24, 32, 48, 64. Tiga token --radius-* mengatur kelengkungan sudut, dari tombol 8 px, kartu 12 px, sampai --radius-penuh untuk lencana bentuk pil. Tiga token --bayangan-* menampung tiga tingkat elevasi; dua tingkat tertingginya memakai rona indigo, bukan hitam murni, agar bayangan menyatu dengan palet. Token --durasi dan --pelunakan dipakai seluruh transisi, sementara --lebar-isi membatasi lebar konten pada 1080 px.

Kerangka Halaman: Pembungkus, Header, dan Hero #

Pola pembatas lebar menjadi aturan pertama kerangka ini:

css
.pembungkus {
  max-width: var(--lebar-isi);
  margin-inline: auto;
  padding-inline: var(--jarak-4);
}

Perkecil dan perbesar lebar jendela demo untuk melihat margin-inline: auto dan padding-inline bekerja pada bidang bergaris putus-putus yang menggambarkan viewport. Selector .pembungkus dipakai berulang di header, main, dan footer, sehingga seluruh kolom isi halaman sejajar pada satu garis tepi yang sama.

Header memakai flexbox: .kepala-baris menata logo di kiri dan navigasi di kanan lewat justify-content: space-between, dengan flex-wrap: wrap agar kedua kelompok boleh menumpuk bila layar menyempit. Seksi hero berada di tengah halaman dengan text-align: center; judulnya memakai clamp(1.8rem, 4vw, 2.6rem) sehingga ukurannya mengikuti lebar layar tanpa patahan, dan pengantarnya dibatasi max-width: 56ch agar panjang baris tetap nyaman dibaca. Baris .pilih-tagihan di bawahnya menampung saklar tagihan; markupnya sudah kita tulis sekarang, sedangkan sulih riasnya menyusul di Bagian 4.

Base Reset Minimal #

Reset yang dipakai sengaja pendek. Selector universal *, *::before, *::after menerapkan box-sizing: border-box agar padding tidak menambah lebar elemen. Margin h1–h3, p, dan ul dinolkan supaya jarak antarelemen diputuskan per komponen, bukan oleh bawaan browser; inilah sebabnya tiap komponen nanti mendeklarasikan margin dan gap miliknya sendiri. img dibuat display: block dengan max-width: 100% agar logo tidak meluber dari header-nya.

css
/* ============================================================
   Halaman Harga Layanan — stylesheet utama
   Urutan: token → base → kerangka halaman → galeri harga →
   kartu harga → tabel banding → tombol → saklar tagihan →
   responsif → gerak dan fokus
   ============================================================ */

/* ---------- 1. Design token ---------- */
:root {
  /* Warna semantik, tema terang */
  --warna-latar: #f8fafc;        --warna-permukaan: #ffffff;
  --warna-teks: #0f172a;         --warna-teks-lembut: #64748b;
  --warna-garis: #e2e8f0;        --warna-utama: #4f46e5;
  --warna-utama-pekat: #4338ca;  --warna-utama-kontras: #ffffff;
  --warna-utama-lembut: #e0e7ff; --warna-utama-tinta: #312e81;
  --warna-fokus: #1d4ed8;        --warna-sukses: #15803d;
  --warna-hemat-latar: #ccfbf1;  --warna-hemat-teks: #0f766e;
  --warna-lintasan: #cbd5e1;

  /* 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: 12px;
  --radius-penuh: 999px;

  /* Elevasi bayangan, tiga tingkat */
  --bayangan-1: 0 1px 2px rgba(15, 23, 42, 0.06);
  --bayangan-2: 0 6px 16px rgba(79, 70, 229, 0.14);
  --bayangan-3: 0 16px 32px rgba(79, 70, 229, 0.18);

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

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

/* ---------- 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, 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 {
  padding-block: var(--jarak-4);
  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-4); flex-wrap: wrap;
}

.brand { display: flex; align-items: center; gap: var(--jarak-2); }
.brand img { width: 36px; height: 36px; }
.brand-nama { font-size: 19px; font-weight: 800; }

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

.hero { padding-block: var(--jarak-7) var(--jarak-5); text-align: center; }
.judul-halaman { font-size: clamp(1.8rem, 4vw, 2.6rem); line-height: 1.2; letter-spacing: -0.02em; }
.pengantar { max-width: 56ch; margin: var(--jarak-3) auto 0; color: var(--warna-teks-lembut); }

.pilih-tagihan { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: var(--jarak-3); margin-top: var(--jarak-5); }

.pilih-label { font-size: 15px; font-weight: 600; color: var(--warna-teks-lembut); }

.lencana-hemat { padding: 3px 12px; border-radius: var(--radius-penuh); background-color: var(--warna-hemat-latar); color: var(--warna-hemat-teks); font-size: 12.5px; font-weight: 700; }

.laman-kaki { margin-top: var(--jarak-8); padding-block: var(--jarak-5); 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); font-size: 13.5px; color: var(--warna-teks-lembut); }

Ringkasan #

Tahap pertama belum menghasilkan satu pun harga di layar, tetapi menetapkan kosakata desain halaman: warna per-peran, skala spasi 4/8 px, radius, tiga tingkat bayangan, dan kerangka halaman yang sejajar di satu pembungkus. Base reset memastikan semua browser berangkat dari titik yang sama. Sejak Bagian 2, setiap aturan baru akan memanggil token ini, dan galeri harga mulai mengisinya.