Dengan fondasi token dan tombol siap, tiga blok layout utama landing page dapat dibangun berdampingan: hero, baris keunggulan, dan grid menu. Ketiganya memperlihatkan pembagian tugas yang sehat antara flexbox dan grid — flexbox untuk satu baris item yang menyesuaikan diri, grid untuk lajur kolom yang sama lebar. Hero memakai dua item berbasis 55% dan 45%; keunggulan memakai flex: 1 agar ketiga kartu berbagi ruang secara adil; menu memakai repeat(3, 1fr). Judul hero menyusut dan membesar mengikuti layar memakai clamp() yang sudah kita kenal di Calc, Min, dan Max
.
Tujuan Bagian dan Prasyarat #
Target bagian ini:
- Hero flex dua kolom dengan gap 48px, item terpusat vertikal, dan
.gambar-heroberbanding rasio 4:3. - Judul hero fluid
clamp(32px, 16px + 3vw, 52px)yang menyentuh batas atas 52px pada viewport 1280px. - Baris keunggulan tiga kartu
.keunggulan-itemdenganflex: 1(grow 1, shrink 1, basis 0%). - Grid menu
repeat(3, 1fr)dengan gap 24px dan kartu berlatar permukaan.
Konsep pendukungnya: Flex Container dan Items , Flex Grow, Shrink, dan Basis , Grid Container dan Items , Repeat dan Minmax , dan Aspect Ratio .
Membangun Hero Flexbox Dua Kolom #
Blok berikut ditambahkan di bawah blok Bagian 1 pada styles/landing.css:
.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);
}Markup hero dan sepasang tombol dari Bagian 1 menyusul di index.html:
<section class="wadah" id="beranda">
<div class="hero">
<div class="hero-teks">
<span class="badge">Diseduh setiap pagi</span>
<h1 class="judul-hero">Kopi <span class="teks-aksen">Senja</span></h1>
<p class="deskripsi-hero">
Biji lereng selatan, disangrai sendiri, diseduh
saat kamu datang.
</p>
<div class="hero-aksi">
<a class="tombol tombol-primer" href="#menu">Pesan Sekarang</a>
<a class="tombol tombol-kedua" href="#cerita">Cerita Kami</a>
</div>
</div>
<div class="hero-gambar">
<img class="gambar-hero" src="assets/gelas-kopi.svg" alt="Ilustrasi gelas kopi susu dengan uap">
</div>
</div>
</section>Hasil di Browser:
Hero terbagi dua kolom 563.2px dan 460.8px dengan gap 48px.
Judul terukur 52px (batas atas clamp pada viewport 1280px)
dengan line-height 59.8px. Kotak gambar menjaga rasio 4:3
sehingga tingginya 345.6px. Grid menu menghasilkan tiga
tiga kolom 1fr dengan gap 24px.Menyusun Keunggulan dan Grid Menu #
Dua blok berikut menyelesaikan Bagian 2:
.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);
}
.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;
}
.gambar-menu {
width: 100%;
aspect-ratio: 3 / 2;
object-fit: cover;
}
.kartu-menu-isi {
display: grid;
gap: var(--spasi-2);
padding: var(--spasi-6);
}<section class="wadah" id="keunggulan">
<div class="keunggulan">
<div class="keunggulan-item">Biji segar setiap minggu</div>
<div class="keunggulan-item">Disangrai di tempat</div>
<div class="keunggulan-item">Barista bersertifikat</div>
</div>
</section>
<section class="wadah" id="menu">
<h2 class="judul-bagian">Menu Unggulan</h2>
<div class="grid-menu">
<article class="kartu-menu">
<img class="gambar-menu" src="assets/menu-kopi-susu.svg" alt="Ilustrasi kopi susu senja">
<div class="kartu-menu-isi">
<h3 class="kartu-menu-nama">Kopi Susu Senja</h3>
<span class="kartu-menu-harga">Rp22.000</span>
<p class="kartu-menu-deskripsi">Espresso, susu, dan gula aren.</p>
</div>
</article>
<article class="kartu-menu">
<img class="gambar-menu" src="assets/menu-americano.svg" alt="Ilustrasi americano dingin">
<div class="kartu-menu-isi">
<h3 class="kartu-menu-nama">Americano Dingin</h3>
<span class="kartu-menu-harga">Rp18.000</span>
<p class="kartu-menu-deskripsi">Dua shot espresso dan air es.</p>
</div>
</article>
<article class="kartu-menu">
<img class="gambar-menu" src="assets/menu-croissant.svg" alt="Ilustrasi croissant mentega">
<div class="kartu-menu-isi">
<h3 class="kartu-menu-nama">Croissant Mentega</h3>
<span class="kartu-menu-harga">Rp16.000</span>
<p class="kartu-menu-deskripsi">Dipanggang pagi, renyah sepanjang hari.</p>
</div>
</article>
</div>
</section>Bedah Aturan Kunci #
.hero(flex 55/45): dua deklarasiflex: 1 1 55%danflex 1 1 45%menjumlah basis menjadi 100%; ditambah gap 48px, total melebihi konten wadah (1072px) sehingga kedua item menyusut sebanding. Hasil akhirnya 563.2px dan 460.8px — basis persen tetap terbaca di panel computed, sedangkanwidthmemperlihatkan hasil akhirnya..gambar-hero:aspect-ratio: 4 / 3denganwidth: 100%membuat tinggi kotak dihitung otomatis: 460.8 × 3/4 = 345.6px. Gambarobject-fit: covermengisi kotak tanpa melar..judul-hero:clamp(32px, 16px + 3vw, 52px); pada 1280px nilai tengahnya 16 + 38.4 = 54.4px sehingga yang berlaku batas atas 52px, dengan line-height 59.8px..keunggulan-item:flex: 1adalah singkatanflex: 1 1 0%— basis nol membuat lebar tidak dipengaruhi isi teks; ketiga kartu murni berbagi ruang secara sama..grid-menu:repeat(3, 1fr)di konten 1072px dengan gap 24px memberi ruang per track (1072 − 48) / 3 = 341,33px. Sisa pecahan piksel tidak dibagi rata: browser menampilkannya sebagai341.328px 341.328px 341.344px— track terakhir menerima kelebihan satu piksel perangkat. Inilah nilai yang dijanjikan halaman uji.
Uji Bagian Ini #
Halaman uji bagian ini tersedia di /tests/css/proyek/landing-page-responsif/flexbox-dan-grid.html
, dengan spesifikasi lengkap pada tests/flexbox-dan-grid.spec.js. Asersi yang diverifikasi:
| Elemen | Properti | Diharapkan |
|---|---|---|
.hero | gap | 48px |
.hero | align-items | center |
.hero-teks | flex-basis | 55% |
.hero-teks | width | 563.2px |
.hero-gambar | width | 460.8px |
.gambar-hero | aspect-ratio | 4 / 3 |
.gambar-hero | height | 345.6px |
.judul-hero | font-size | 52px |
.judul-hero | line-height | 59.8px |
.keunggulan-item | flex-basis | 0% |
.grid-menu | grid-template-columns | 341.328px 341.328px 341.344px |
.kartu-menu | border-radius | 12px |
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" },
];