Sebelum satu hero atau kartu pun digambar, landing page berdiri di atas keputusan kecil yang dipakai berulang: warna, jarak, kelengkungan sudut, dan ukuran teks. Pada bagian pertama ini kita menyiapkan file styles/landing.css, mendefinisikan token di :root dengan palet hangat khas kedai kopi, lalu membangun tiga komponen paling dasar: tombol primer, tombol kedua, dan badge. Semua itu masih tampil di atas halaman polos tanpa layout — layout menyusul pada Bagian 2, dan komponen-komponen ini akan dipasang di sana. Pola kerjanya sama seperti Proyek: Website Portofolio
, tetapi palet, nilai radius, dan konteks brand-nya berbeda.
Tujuan Bagian dan Prasyarat #
Di akhir bagian ini, halaman kita menampilkan hierarki teks dan sepasang tombol di atas latar krem hangat. Target spesifiknya:
- Delapan token warna, delapan token spasi, tiga token radius, dan tiga token tipografi didefinisikan sekali di
:root. - Reset ringkas
* { margin: 0; box-sizing: border-box }sehingga setiap jarak adalah keputusan kita. - Wadah
.wadahselebar maksimum 1120px yang terpusat otomatis. .tomboldengan dua varian warna dan.badgeberlatar primer alpha 10%.
Konsep yang dipakai sudah dibahas pada chapter Custom Properties , Color Syntax , Font Size dan Weight , serta Box Sizing . Buka kembali chapter tersebut bila ada yang terasa asing.
Menulis Token dan Komponen Dasar #
Buat folder proyek landing-page-responsif/ berisi index.html dan subfolder styles/. Tulis potongan pertama styles/landing.css. Urutan kelompoknya penting untuk keterbacaan: token paling atas, reset, aturan body, wadah, lalu komponen.
:root {
/* Warna semantik — brand kedai Kopi Senja */
--warna-primer: #c2410c;
--warna-primer-tua: #9a3412;
--warna-latar: #fffbeb;
--warna-permukaan: #ffffff;
--warna-teks: #292524;
--warna-teks-lembut: #57534e;
--warna-teks-balik: #ffffff;
--warna-garis: #e7e5e4;
/* Skala spasi */
--spasi-1: 4px;
--spasi-2: 8px;
--spasi-3: 12px;
--spasi-4: 16px;
--spasi-6: 24px;
--spasi-8: 32px;
--spasi-12: 48px;
--spasi-16: 64px;
/* Bentuk */
--radius-sedang: 12px;
--radius-besar: 16px;
--radius-penuh: 999px;
/* Tipografi */
--font-utama: system-ui, sans-serif;
--teks-dasar: 16px;
--teks-lg: 20px;
}
* {
margin: 0;
box-sizing: border-box;
}
body {
font-family: var(--font-utama);
font-size: var(--teks-dasar);
line-height: 1.6;
color: var(--warna-teks);
background-color: var(--warna-latar);
}
.wadah {
max-width: 1120px;
margin: 0 auto;
padding: 0 var(--spasi-6);
}
.tombol {
display: inline-block;
padding: var(--spasi-3) var(--spasi-4);
border-radius: var(--radius-sedang);
font-size: var(--teks-dasar);
font-weight: 500;
text-decoration: none;
}
.tombol-primer {
background-color: var(--warna-primer);
color: var(--warna-teks-balik);
}
.tombol-kedua {
background-color: var(--warna-permukaan);
color: var(--warna-primer);
border: 1px solid var(--warna-primer);
}
.badge {
display: inline-block;
padding: var(--spasi-1) var(--spasi-3);
border-radius: var(--radius-penuh);
background-color: rgb(194 65 12 / 10%);
color: var(--warna-primer-tua);
font-size: 14px;
font-weight: 600;
}
.judul-bagian {
font-size: 28px;
font-weight: 700;
}
.deskripsi-bagian {
margin-top: var(--spasi-2);
max-width: 560px;
color: var(--warna-teks-lembut);
}Hasil di Browser:
Body berlatar rgb(255, 251, 235) dengan teks rgb(41, 37, 36)
sebesar 16px dan line-height 25.6px. Wadah terpusat dengan
margin kiri 80px pada viewport 1280px. Tombol primer berlatar
rgb(194, 65, 12) dengan padding 12px 16px dan radius 12px.
Badge berlatar rgba(194, 65, 12, 0.1) berbentuk kapsul.Menyiapkan Markup Pembawa Komponen #
Markup awal sengaja belum memuat layout: hanya judul bagian, deskripsi, dua tombol, dan satu badge di dalam wadah. Struktur ini cukup untuk memverifikasi fondasi sebelum Bagian 2 menambah hero dan grid.
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Kopi Senja — Kedai Kopi Lereng Selatan</title>
<link rel="stylesheet" href="styles/landing.css">
</head>
<body>
<div class="wadah">
<h2 class="judul-bagian">Menu Unggulan</h2>
<p class="deskripsi-bagian">
Tiga andalan kedai yang diseduh dari biji pilihan
petani lereng selatan.
</p>
<a class="tombol tombol-primer" href="#menu">Lihat Menu</a>
<a class="tombol tombol-kedua" href="#cerita">Cerita Kami</a>
<span class="badge">Diseduh setiap pagi</span>
</div>
</body>
</html>Bedah Aturan Utama #
Mari kita urai aturan-aturan kunci di atas:
- Blok
:root: dua puluh dua custom properties menyimpan seluruh keputusan desain dalam satu tempat. Nama token bersifat semantik —--warna-primer, bukan--warna-oranye— sehingga pergantian brand tidak memaksa pencarian-penggantian di seluruh file. - Aturan
* { margin: 0; box-sizing: border-box }: reset dua baris yang menghapus margin bawaan dan membuatwidthmencakup padding serta border. Dua komponen tombol dengan ukuran padding berbeda tetap mudah diprediksi luasnya. - Aturan
body:line-height: 1.6pada font 16px dihitung browser menjadi 25.6px dan diwarisi seluruh teks;colordanbackground-colormenarik token dari:root. - Aturan
.wadah:max-width: 1120px+margin: 0 automemusatkan konten. Pada viewport 1280px, browser menghitung margin kiri-kanan wadah menjadi 80px. Padding horizontal 24px menjaga jarak minimum di tepi. - Aturan
.tomboldan variannya: semua sifat bentuk dan ukuran hidup di kelas dasar.tombol; varian hanya mengatur warna. Pola komposisi ini yang membuat Bagian 4 nanti cukup menambah satu baristransitionuntuk menganimasikan kedua varian sekaligus. - Aturan
.badge:rgb(194 65 12 / 10%)adalah warna primer dengan alpha 10% — tetap terasa satu keluarga warna tanpa token tambahan.border-radius: var(--radius-penuh)menghasilkan bentuk kapsul.
Uji Bagian Ini #
Halaman uji bagian ini tersedia di /tests/css/proyek/landing-page-responsif/visual-foundation.html
, dengan spesifikasi lengkap pada tests/visual-foundation.spec.js. Asersi yang diverifikasi:
| Elemen | Properti | Diharapkan |
|---|---|---|
body | background-color | rgb(255, 251, 235) |
body | color | rgb(41, 37, 36) |
body | line-height | 25.6px |
.wadah | max-width | 1120px |
.wadah | margin-left | 80px |
.tombol-primer | background-color | rgb(194, 65, 12) |
.tombol-primer | padding-top | 12px |
.tombol-primer | border-radius | 12px |
.tombol-kedua | border-top-color | rgb(194, 65, 12) |
.badge | background-color | rgba(194, 65, 12, 0.1) |
.badge | border-radius | 999px |
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
styles/landing.css
Unduh berkas/* =========================================================
Proyek: Landing Page Responsif — styles/landing.css
File utuh proyek yang berkembang mengikuti empat bagian:
Bagian 1 (Visual Foundation), Bagian 2 (Flexbox dan Grid),
Bagian 3 (Responsive Navigation), Bagian 4 (Animations dan
Polish). Komentar "Bagian N" menandai kelompok aturan yang
dibahas pada setiap bagian.
========================================================= */
/* Bagian 1: Visual Foundation ----------------------------- */
:root {
/* Warna semantik — brand kedai Kopi Senja */
--warna-primer: #c2410c;
--warna-primer-tua: #9a3412;
--warna-latar: #fffbeb;
--warna-permukaan: #ffffff;
--warna-teks: #292524;
--warna-teks-lembut: #57534e;
--warna-teks-balik: #ffffff;
--warna-garis: #e7e5e4;
/* Skala spasi */
--spasi-1: 4px;
--spasi-2: 8px;
--spasi-3: 12px;
--spasi-4: 16px;
--spasi-6: 24px;
--spasi-8: 32px;
--spasi-12: 48px;
--spasi-16: 64px;
/* Bentuk */
--radius-sedang: 12px;
--radius-besar: 16px;
--radius-penuh: 999px;
/* Tipografi */
--font-utama: system-ui, sans-serif;
--teks-dasar: 16px;
--teks-lg: 20px;
}
* {
margin: 0;
box-sizing: border-box;
}
body {
font-family: var(--font-utama);
font-size: var(--teks-dasar);
line-height: 1.6;
color: var(--warna-teks);
background-color: var(--warna-latar);
}
.wadah {
max-width: 1120px;
margin: 0 auto;
padding: 0 var(--spasi-6);
}
.tombol {
display: inline-block;
padding: var(--spasi-3) var(--spasi-4);
border-radius: var(--radius-sedang);
font-size: var(--teks-dasar);
font-weight: 500;
text-decoration: none;
/* Bagian 4: menambah baris transition di bawah ini */
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.tombol-primer {
background-color: var(--warna-primer);
color: var(--warna-teks-balik);
}
.tombol-kedua {
background-color: var(--warna-permukaan);
color: var(--warna-primer);
border: 1px solid var(--warna-primer);
}
.badge {
display: inline-block;
padding: var(--spasi-1) var(--spasi-3);
border-radius: var(--radius-penuh);
background-color: rgb(194 65 12 / 10%);
color: var(--warna-primer-tua);
font-size: 14px;
font-weight: 600;
/* Bagian 4: menambah baris animation di bawah ini */
animation: denyut 2s ease-in-out infinite;
}
.judul-bagian {
font-size: 28px;
font-weight: 700;
}
.deskripsi-bagian {
margin-top: var(--spasi-2);
max-width: 560px;
color: var(--warna-teks-lembut);
}
/* Bagian 2: Flexbox dan Grid ------------------------------ */
.hero {
display: flex;
align-items: center;
gap: var(--spasi-12);
padding: var(--spasi-16) 0;
}
.hero-teks {
flex: 1 1 55%;
}
.hero-gambar {
flex: 1 1 45%;
display: flex;
}
.gambar-hero {
width: 100%;
aspect-ratio: 4 / 3;
border-radius: var(--radius-besar);
object-fit: cover;
}
.judul-hero {
font-size: clamp(32px, 16px + 3vw, 52px);
line-height: 1.15;
}
.teks-aksen {
color: var(--warna-primer);
}
.deskripsi-hero {
margin-top: var(--spasi-4);
font-size: 18px;
color: var(--warna-teks-lembut);
}
.hero-aksi {
display: flex;
gap: var(--spasi-4);
margin-top: var(--spasi-6);
}
.keunggulan {
display: flex;
gap: var(--spasi-6);
}
.keunggulan-item {
flex: 1;
padding: var(--spasi-6);
background-color: var(--warna-permukaan);
border: 1px solid var(--warna-garis);
border-radius: var(--radius-sedang);
}
.keunggulan-judul {
font-size: 18px;
font-weight: 600;
}
.keunggulan-isi {
margin-top: var(--spasi-2);
font-size: 14px;
color: var(--warna-teks-lembut);
}
.grid-menu {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--spasi-6);
margin-top: var(--spasi-8);
}
.kartu-menu {
display: flex;
flex-direction: column;
background-color: var(--warna-permukaan);
border: 1px solid var(--warna-garis);
border-radius: var(--radius-sedang);
overflow: hidden;
/* Bagian 4: menambah baris transition di bawah ini */
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.gambar-menu {
width: 100%;
aspect-ratio: 3 / 2;
object-fit: cover;
}
.kartu-menu-isi {
display: grid;
gap: var(--spasi-2);
padding: var(--spasi-6);
}
.kartu-menu-nama {
font-size: var(--teks-lg);
font-weight: 600;
}
.kartu-menu-harga {
color: var(--warna-primer);
font-weight: 600;
}
.kartu-menu-deskripsi {
font-size: 14px;
color: var(--warna-teks-lembut);
}
/* Bagian 3: Responsive Navigation ------------------------- */
.navigasi {
position: sticky;
top: 0;
z-index: 20;
background-color: rgb(255 251 235 / 90%);
backdrop-filter: blur(8px);
border-bottom: 1px solid var(--warna-garis);
}
.navigasi-isi {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--spasi-6);
height: 64px;
}
.logo {
font-size: var(--teks-lg);
font-weight: 700;
color: var(--warna-teks);
text-decoration: none;
}
.navigasi-menu {
display: flex;
gap: var(--spasi-6);
list-style: none;
}
.navigasi-tautan {
color: var(--warna-teks-lembut);
font-weight: 500;
text-decoration: none;
}
.navigasi-tautan:hover {
color: var(--warna-primer);
}
.navigasi-tautan:focus-visible {
outline: 2px solid var(--warna-primer);
outline-offset: 2px;
}
/* Checkbox hamburger disembunyikan secara visual, tetap
bisa di-fokus dan dibaca screen reader */
.menu-toggle {
position: absolute;
width: 1px;
height: 1px;
opacity: 0;
}
.tombol-menu {
display: none;
padding: var(--spasi-2) var(--spasi-4);
border: 1px solid var(--warna-garis);
border-radius: var(--radius-sedang);
background-color: var(--warna-permukaan);
font-size: 14px;
font-weight: 500;
cursor: pointer;
}
/* Fokus keyboard dari checkbox yang tersembunyi ditunjukkan
pada label hamburger di sebelahnya */
.menu-toggle:focus-visible + .tombol-menu {
outline: 2px solid var(--warna-primer);
outline-offset: 2px;
}
@media (max-width: 899px) {
.hero {
flex-direction: column;
align-items: flex-start;
}
.hero-gambar {
width: 100%;
}
.keunggulan {
flex-direction: column;
}
.grid-menu {
grid-template-columns: 1fr;
}
.tombol-menu {
display: inline-flex;
}
.navigasi-menu {
display: none;
position: absolute;
top: 64px;
left: 0;
right: 0;
flex-direction: column;
gap: var(--spasi-1);
padding: var(--spasi-4) var(--spasi-6);
background-color: var(--warna-permukaan);
border-bottom: 1px solid var(--warna-garis);
}
/* Panel menu terbuka tanpa JavaScript memakai :checked */
.menu-toggle:checked ~ .navigasi-menu {
display: flex;
}
}
/* Bagian 4: Animations dan Polish ------------------------- */
@keyframes denyut {
0%, 100% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.06);
opacity: 0.85;
}
}
.tombol-primer:hover {
transform: translateY(-2px);
box-shadow: 0 8px 16px rgb(154 52 18 / 25%);
}
.tombol-kedua:hover {
transform: translateY(-2px);
background-color: rgb(194 65 12 / 8%);
}
.kartu-menu:hover {
transform: translateY(-4px);
box-shadow: 0 12px 24px rgb(41 37 36 / 12%);
}
.pita-cta {
margin-top: var(--spasi-16);
padding: var(--spasi-12) var(--spasi-8);
background-color: var(--warna-primer);
color: var(--warna-teks-balik);
text-align: center;
border-radius: var(--radius-besar);
box-shadow: 0 12px 24px rgb(154 52 18 / 25%);
}
.pita-cta-judul {
font-size: 24px;
font-weight: 700;
}
.pita-cta-isi {
margin-top: var(--spasi-2);
}
.kaki {
display: flex;
justify-content: space-between;
gap: var(--spasi-4);
margin-top: var(--spasi-16);
padding: var(--spasi-8) 0;
border-top: 1px solid var(--warna-garis);
font-size: 14px;
color: var(--warna-teks-lembut);
}
html {
scroll-behavior: smooth;
}
/* Pengguna yang mengaktifkan reduce motion menerima halaman
tanpa animasi: keyframes dan transition dimatikan */
@media (prefers-reduced-motion: reduce) {
html {
scroll-behavior: auto;
}
.badge {
animation: none;
}
.tombol,
.kartu-menu {
transition: none;
}
}
tests/animations-dan-polish.spec.js
Unduh berkas// ============================================================
// Spesifikasi uji — Proyek: Landing Page Responsif
// Bagian 4: Animations dan Polish
// ============================================================
// Berkas ini merangkum asersi yang diverifikasi oleh harness
// pada halaman uji (viewport 1280x720):
// /tests/css/proyek/landing-page-responsif/animations-dan-polish.html
// Setiap entri memasangkan elemen probe dengan properti CSS
// dan nilai computed yang diharapkan.
const asersi = [
// Guliran antarbagian bergerak halus lewat scroll-behavior.
{ elemen: "html", properti: "scroll-behavior", diharapkan: "smooth" },
// Tombol bertransisi pada properti transform dan box-shadow.
{ elemen: ".tombol-primer", properti: "transition-property", diharapkan: "transform, box-shadow" },
{ elemen: ".tombol-primer", properti: "transition-duration", diharapkan: "0.2s, 0.2s" },
// Kartu menu bertransisi dengan nilai yang sama seperti tombol.
{ elemen: ".kartu-menu", properti: "transition-property", diharapkan: "transform, box-shadow" },
{ elemen: ".kartu-menu", properti: "transition-duration", diharapkan: "0.2s, 0.2s" },
// Badge berdenyut: keyframes denyut, 2 detik, tanpa henti.
{ elemen: ".badge", properti: "animation-name", diharapkan: "denyut" },
{ elemen: ".badge", properti: "animation-duration", diharapkan: "2s" },
{ elemen: ".badge", properti: "animation-timing-function", diharapkan: "ease-in-out" },
{ elemen: ".badge", properti: "animation-iteration-count", diharapkan: "infinite" },
// Pita CTA berlatar primer dengan bayangan kehangatan primer-tua.
{ elemen: ".pita-cta", properti: "background-color", diharapkan: "rgb(194, 65, 12)" },
{ elemen: ".pita-cta", properti: "color", diharapkan: "rgb(255, 255, 255)" },
{ elemen: ".pita-cta", properti: "border-radius", diharapkan: "16px" },
{ elemen: ".pita-cta", properti: "text-align", diharapkan: "center" },
{ elemen: ".pita-cta", properti: "box-shadow", diharapkan: "rgba(154, 52, 18, 0.25) 0px 12px 24px 0px" },
// Judul pita CTA 24px dengan bobot 700.
{ elemen: ".pita-cta-judul", properti: "font-size", diharapkan: "24px" },
{ elemen: ".pita-cta-judul", properti: "font-weight", diharapkan: "700" },
// Footer dua sisi: flex dengan ruas terpisah dan garis atas.
{ elemen: ".kaki", properti: "display", diharapkan: "flex" },
{ elemen: ".kaki", properti: "justify-content", diharapkan: "space-between" },
{ elemen: ".kaki", properti: "border-top-width", diharapkan: "1px" },
];
tests/flexbox-dan-grid.spec.js
Unduh berkas// ============================================================
// Spesifikasi uji — Proyek: Landing Page Responsif
// Bagian 2: Flexbox dan Grid
// ============================================================
// Berkas ini merangkum asersi yang diverifikasi oleh harness
// pada halaman uji (viewport 1280x720):
// /tests/css/proyek/landing-page-responsif/flexbox-dan-grid.html
// Setiap entri memasangkan elemen probe dengan properti CSS
// dan nilai computed yang diharapkan.
const asersi = [
// Hero adalah flex container dengan item terpusat vertikal.
{ elemen: ".hero", properti: "display", diharapkan: "flex" },
{ elemen: ".hero", properti: "align-items", diharapkan: "center" },
{ elemen: ".hero", properti: "gap", diharapkan: "48px" },
// Teks hero: basis 55% yang menyusut menjadi 563.2px akibat gap.
{ elemen: ".hero-teks", properti: "flex-grow", diharapkan: "1" },
{ elemen: ".hero-teks", properti: "flex-shrink", diharapkan: "1" },
{ elemen: ".hero-teks", properti: "flex-basis", diharapkan: "55%" },
{ elemen: ".hero-teks", properti: "width", diharapkan: "563.2px" },
// Gambar hero: basis 45%, lebar akhir 460.8px.
{ elemen: ".hero-gambar", properti: "flex-basis", diharapkan: "45%" },
{ elemen: ".hero-gambar", properti: "width", diharapkan: "460.8px" },
// Kotak gambar menjaga rasio 4:3 sehingga tingginya 345.6px.
{ elemen: ".gambar-hero", properti: "aspect-ratio", diharapkan: "4 / 3" },
{ elemen: ".gambar-hero", properti: "height", diharapkan: "345.6px" },
{ elemen: ".gambar-hero", properti: "border-radius", diharapkan: "16px" },
// Judul hero fluid mencapai batas atas clamp 52px pada 1280px.
{ elemen: ".judul-hero", properti: "font-size", diharapkan: "52px" },
{ elemen: ".judul-hero", properti: "line-height", diharapkan: "59.8px" },
// Baris tombol aksi adalah flex dengan gap 16px.
{ elemen: ".hero-aksi", properti: "display", diharapkan: "flex" },
{ elemen: ".hero-aksi", properti: "gap", diharapkan: "16px" },
// Item keunggulan memakai flex: 1 = grow 1, shrink 1, basis 0%.
{ elemen: ".keunggulan-item", properti: "flex-grow", diharapkan: "1" },
{ elemen: ".keunggulan-item", properti: "flex-shrink", diharapkan: "1" },
{ elemen: ".keunggulan-item", properti: "flex-basis", diharapkan: "0%" },
// Grid menu tiga kolom sama lebar dengan gap 24px.
{ elemen: ".grid-menu", properti: "display", diharapkan: "grid" },
{ elemen: ".grid-menu", properti: "gap", diharapkan: "24px" },
{ elemen: ".grid-menu", properti: "grid-template-columns", diharapkan: "341.328px 341.328px 341.344px" },
// Kartu menu berlatar permukaan putih dengan radius 12px.
{ elemen: ".kartu-menu", properti: "background-color", diharapkan: "rgb(255, 255, 255)" },
{ elemen: ".kartu-menu", properti: "border-radius", diharapkan: "12px" },
{ elemen: ".kartu-menu", properti: "display", diharapkan: "flex" },
];
tests/responsive-navigation.spec.js
Unduh berkas// ============================================================
// Spesifikasi uji — Proyek: Landing Page Responsif
// Bagian 3: Responsive Navigation
// ============================================================
// Berkas ini merangkum asersi yang diverifikasi oleh harness
// pada halaman uji (viewport 1280x720):
// /tests/css/proyek/landing-page-responsif/responsive-navigation.html
// Setiap entri memasangkan elemen probe dengan properti CSS
// dan nilai computed yang diharapkan.
const asersi = [
// Navigation menempel di atas: sticky dengan jarak 0 dari tepi.
{ elemen: ".navigasi", properti: "position", diharapkan: "sticky" },
{ elemen: ".navigasi", properti: "top", diharapkan: "0px" },
{ elemen: ".navigasi", properti: "z-index", diharapkan: "20" },
// Latar krem semi transparan 90% dengan efek blur di belakangnya.
{ elemen: ".navigasi", properti: "background-color", diharapkan: "rgba(255, 251, 235, 0.9)" },
{ elemen: ".navigasi", properti: "backdrop-filter", diharapkan: "blur(8px)" },
// Baris navigasi setinggi 64px dengan ruas logo dan menu terpisah.
{ elemen: ".navigasi-isi", properti: "height", diharapkan: "64px" },
{ elemen: ".navigasi-isi", properti: "justify-content", diharapkan: "space-between" },
{ elemen: ".navigasi-isi", properti: "gap", diharapkan: "24px" },
{ elemen: ".navigasi-isi", properti: "display", diharapkan: "flex" },
// Logo memakai ukuran 20px dengan bobot 700.
{ elemen: ".logo", properti: "font-size", diharapkan: "20px" },
{ elemen: ".logo", properti: "font-weight", diharapkan: "700" },
// Menu tersusun horizontal dengan jarak antartautan 24px.
{ elemen: ".navigasi-menu", properti: "display", diharapkan: "flex" },
{ elemen: ".navigasi-menu", properti: "gap", diharapkan: "24px" },
// Tautan memakai warna teks lembut tanpa garis bawah.
{ elemen: ".navigasi-tautan", properti: "color", diharapkan: "rgb(87, 83, 78)" },
{ elemen: ".navigasi-tautan", properti: "font-weight", diharapkan: "500" },
{ elemen: ".navigasi-tautan", properti: "text-decoration-line", diharapkan: "none" },
// Checkbox hamburger disembunyikan secara visual.
{ elemen: ".menu-toggle", properti: "position", diharapkan: "absolute" },
{ elemen: ".menu-toggle", properti: "opacity", diharapkan: "0" },
{ elemen: ".menu-toggle", properti: "width", diharapkan: "1px" },
// Tombol hamburger hanya tampil pada breakpoint 899px ke bawah.
{ elemen: ".tombol-menu", properti: "display", diharapkan: "none" },
];
tests/visual-foundation.spec.js
Unduh berkas// ============================================================
// Spesifikasi uji — Proyek: Landing Page Responsif
// Bagian 1: Visual Foundation
// ============================================================
// Berkas ini merangkum asersi yang diverifikasi oleh harness
// pada halaman uji (viewport 1280x720):
// /tests/css/proyek/landing-page-responsif/visual-foundation.html
// Setiap entri memasangkan elemen probe dengan properti CSS
// dan nilai computed yang diharapkan.
const asersi = [
// body mengonsumsi token: latar krem hangat dan teks stone-800.
{ elemen: "body", properti: "font-size", diharapkan: "16px" },
{ elemen: "body", properti: "line-height", diharapkan: "25.6px" },
{ elemen: "body", properti: "color", diharapkan: "rgb(41, 37, 36)" },
{ elemen: "body", properti: "background-color", diharapkan: "rgb(255, 251, 235)" },
// Wadah konten dibatasi 1120px dan terpusat di viewport 1280px.
{ elemen: ".wadah", properti: "max-width", diharapkan: "1120px" },
{ elemen: ".wadah", properti: "margin-left", diharapkan: "80px" },
// Judul bagian memakai ukuran 28px dengan bobot 700.
{ elemen: ".judul-bagian", properti: "font-size", diharapkan: "28px" },
{ elemen: ".judul-bagian", properti: "font-weight", diharapkan: "700" },
// Deskripsi bagian memakai warna teks lembut dan lebar terbatas.
{ elemen: ".deskripsi-bagian", properti: "color", diharapkan: "rgb(87, 83, 78)" },
{ elemen: ".deskripsi-bagian", properti: "max-width", diharapkan: "560px" },
// Tombol primer: latar oranye terbakar, teks putih, padding dari token.
{ elemen: ".tombol-primer", properti: "background-color", diharapkan: "rgb(194, 65, 12)" },
{ elemen: ".tombol-primer", properti: "color", diharapkan: "rgb(255, 255, 255)" },
{ elemen: ".tombol-primer", properti: "padding-top", diharapkan: "12px" },
{ elemen: ".tombol-primer", properti: "padding-left", diharapkan: "16px" },
{ elemen: ".tombol-primer", properti: "border-radius", diharapkan: "12px" },
// Tombol kedua: permukaan putih dengan teks dan border primer.
{ elemen: ".tombol-kedua", properti: "background-color", diharapkan: "rgb(255, 255, 255)" },
{ elemen: ".tombol-kedua", properti: "color", diharapkan: "rgb(194, 65, 12)" },
{ elemen: ".tombol-kedua", properti: "border-top-color", diharapkan: "rgb(194, 65, 12)" },
// Badge memakai warna primer dengan alpha 10% dan radius penuh.
{ elemen: ".badge", properti: "background-color", diharapkan: "rgba(194, 65, 12, 0.1)" },
{ elemen: ".badge", properti: "color", diharapkan: "rgb(154, 52, 18)" },
{ elemen: ".badge", properti: "border-radius", diharapkan: "999px" },
{ elemen: ".badge", properti: "font-size", diharapkan: "14px" },
];