Galeri foto berdiri atau roboh di tata letaknya: bila setiap bidang foto dibuat sama besar, halaman terasa seperti lembar stiker tanpa penekanan. Bagian 1 meletakkan fondasi yang membuat sepuluh foto nanti punya irama — grid tiga kolom dengan baris satuan tetap, lalu rentang baris dan kolom yang berbeda-beda per item. Sebelum menyentuh grid, kita menetapkan dulu kosakata visual proyek: palet gelap sebagai panggung foto, token jarak dan radius, unit lintasan, serta empat token rasio yang baru bekerja penuh di Bagian 3. Snapshot styles.css tahap ini berhenti di 141 baris dan belum memuat satu pun foto; bidang media masih berupa gradasi sementara.
Token dan Base #
Paletnya gelap secara sadar: --warna-latar hampir hitam kebiruan, --warna-permukaan naik satu tingkat untuk tombol, dan dua token teks membedakan isi utama dari teks sekunder. Aksen kuning --warna-utama dipakai untuk merek, tombol aktif, dan cincin fokus, sementara color-scheme: dark memberi tahu browser bahwa elemen bawaan seperti bilah gulir juga sebaiknya gelap.
Empat token berikut adalah jantung grid dan kontrak komposisi proyek ini:
:root {
--unit-lintasan: 96px;
--celah-galeri: 14px;
--rasio-lanskap: 16 / 9;
--rasio-panorama: 21 / 9;
--rasio-potret: 2 / 3;
--rasio-besar: 7 / 4;
}--unit-lintasan adalah tinggi satu baris grid, dan --celah-galeri menjaga jarak antar foto berasal dari satu sumber. Empat token rasio mendokumentasikan bentuk setiap tipe foto sebelum satu pun aturan grid ditulis; Bagian 3 memakainya sebagai nilai aspect-ratio yang sesungguhnya. Blok base tinggal empat aturan: box-sizing: border-box universal, body mengambil keluarga huruf dan latar dari token, margin h1/h2/p dinolkan agar jarak diputuskan per komponen, dan img dijadikan blok. Dua pelengkap kecil ikut masuk di sini: html { scroll-behavior: smooth; } untuk lompatan anchor dan ::selection berwarna kuning.
Kerangka Halaman #
Kerangkanya tiga lapis. .pembungkus mengunci lebar maksimum 1160 px dan menengahkan diri dengan margin-inline: auto. Header .kepala-galeri menata merek dan tagline dalam satu baris flex space-between dengan align-items: baseline, sehingga teks kecil sejajar garis dasar teks besar. Bagian .pengantar membatasi lebar teks pada 66ch supaya panjang barisnya nyaman dibaca, dan .kaki-galeri mencerminkan header dengan garis di sisi atasnya lewat .kaki-isi.
Grid Tiga Kolom dengan Unit Baris #
Aturan inti bagian ini hanya enam deklarasi:
.galeri {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
grid-auto-rows: var(--unit-lintasan);
grid-auto-flow: dense;
gap: var(--celah-galeri);
}Baris per baris: repeat(3, minmax(0, 1fr)) membagi ruang menjadi tiga kolom sama lebar, dan batas bawah 0 mencegah isi yang lebar memaksa satu kolom melar hingga halaman bergulir menyamping. grid-auto-rows: 96px menetapkan tinggi semua baris implisit pada satu unit — inilah kunci iramanya, karena tinggi foto nanti dikendalikan lewat jumlah baris yang direntang, bukan lewat tinggi piksel per foto. grid-auto-flow: dense menyuruh algoritme penempatan kembali ke lubang kecil yang tertinggal setelah foto tinggi atau lebar lewat.
Setiap item lalu membentang sesuai karakternya:
| Kelas | Rentang | Rasio kira-kira | Peran di galeri |
|---|---|---|---|
| tanpa kelas | 1 kolom × 2 baris | 16:9 | foto lanskap standar |
.galeri-item--lebar | 2 kolom × 3 baris | 21:9 | foto lebar semipanorama |
.galeri-item--tinggi | 1 kolom × 5 baris | 2:3 | foto potret |
.galeri-item--besar | 2 kolom × 4 baris | 7:4 | foto pembuka besar |
.galeri-item--panorama | 3 kolom × 3 baris | 3,6:1 | bentang penuh tiga kolom |
Nilai bawaan grid-row: span 2 hidup di .galeri-item sendiri, jadi foto tanpa kelas pun tidak setinggi satu unit. Di dalamnya, .galeri-tautan diberi flex: 1; min-height: 0 dan .galeri-media mengisi height: 100% — untuk saat ini media hanya berisi gradasi abu gelap sebagai penanda tempat, karena foto sungguhan baru masuk di Bagian 2. Keterangan foto juga belum ditata; ia akan bergaya mulai Bagian 4.
Snapshot tahap ini:
/* ============================================================
Galeri Sudut Cahaya — stylesheet tahap 1
Fondasi: token, base, kerangka halaman, bilah filter, dan grid galeri
============================================================ */
/* ---------- 1. Design token ---------- */
:root {
color-scheme: dark;
--warna-latar: #10151a;
--warna-permukaan: #1b232b;
--warna-teks: #f1f4f6;
--warna-teks-lembut: #9db0bd;
--warna-garis: #2b3742;
--warna-utama: #e8a13d;
--warna-utama-teks: #241703;
--warna-fokus: #ffd479;
--jarak-1: 4px;
--jarak-2: 8px;
--jarak-3: 12px;
--jarak-4: 16px;
--jarak-5: 24px;
--jarak-6: 32px;
--jarak-7: 48px;
--radius-sedang: 12px;
--radius-penuh: 999px;
--lebar-halaman: 1160px;
--unit-lintasan: 96px;
--celah-galeri: 14px;
--rasio-lanskap: 16 / 9;
--rasio-panorama: 21 / 9;
--rasio-potret: 2 / 3;
--rasio-besar: 7 / 4;
--durasi-gerak: 220ms;
}
/* ---------- 2. Base ---------- */
*,
*::before,
*::after { box-sizing: border-box; }
body {
margin: 0;
font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
line-height: 1.6;
background: var(--warna-latar);
color: var(--warna-teks);
}
h1, h2, p { margin: 0; }
img { display: block; max-width: 100%; }
html { scroll-behavior: smooth; }
::selection { background: var(--warna-utama); color: var(--warna-utama-teks); }
/* ---------- 3. Kerangka halaman ---------- */
.pembungkus {
max-width: var(--lebar-halaman);
margin-inline: auto;
padding-inline: var(--jarak-4);
}
.kepala-galeri {
display: flex;
flex-wrap: wrap;
align-items: baseline;
justify-content: space-between;
gap: var(--jarak-2);
padding-block: var(--jarak-5);
border-bottom: 1px solid var(--warna-garis);
}
.merek { font-size: 1.35rem; font-weight: 700; letter-spacing: 0.02em; color: var(--warna-utama); }
.tagline { color: var(--warna-teks-lembut); font-size: 0.95rem; }
.pengantar { max-width: 66ch; padding-block: var(--jarak-6) var(--jarak-4); }
.pengantar h2 { font-size: clamp(1.5rem, 1rem + 2.2vw, 2.1rem); line-height: 1.25; margin-bottom: var(--jarak-3); }
.pengantar p { color: var(--warna-teks-lembut); }
.kaki-galeri { margin-top: var(--jarak-7); border-top: 1px solid var(--warna-garis); }
.kaki-isi {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: var(--jarak-2);
padding-block: var(--jarak-5);
color: var(--warna-teks-lembut);
font-size: 0.9rem;
}
/* ---------- 4. Bilah filter ---------- */
.bilah-filter {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--jarak-2);
padding-block: var(--jarak-4);
}
.tombol-filter {
font: inherit;
font-size: 0.92rem;
padding: var(--jarak-1) var(--jarak-4);
border: 1px solid var(--warna-garis);
border-radius: var(--radius-penuh);
background: var(--warna-permukaan);
color: var(--warna-teks);
cursor: pointer;
}
.hitung-galeri { margin-left: auto; color: var(--warna-teks-lembut); font-size: 0.9rem; }
.hitung-galeri span { font-weight: 700; color: var(--warna-teks); }
/* ---------- 5. Grid galeri ---------- */
.galeri {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
grid-auto-rows: var(--unit-lintasan);
grid-auto-flow: dense;
gap: var(--celah-galeri);
padding-block: var(--jarak-2) var(--jarak-5);
}
.galeri-item {
position: relative;
margin: 0;
display: flex;
flex-direction: column;
grid-row: span 2;
}
.galeri-item--lebar { grid-column: span 2; grid-row: span 3; }
.galeri-item--tinggi { grid-row: span 5; }
.galeri-item--besar { grid-column: span 2; grid-row: span 4; }
.galeri-item--panorama { grid-column: span 3; grid-row: span 3; }
/* ---------- 6. Media foto (versi awal) ---------- */
.galeri-tautan { display: block; flex: 1; min-height: 0; border-radius: var(--radius-sedang); }
.galeri-media { height: 100%; overflow: hidden; border-radius: var(--radius-sedang); background: linear-gradient(165deg, #2b3a44 0%, #17222a 70%); }Ringkasan #
Fondasi halaman sudah berdiri: token gelap, kerangka tiga lapis, dan grid tiga kolom berunit baris tetap dengan lima pola rentang item. Grid itu masih memamerkan bidang gradasi kosong. Bagian 2 memasukkan sepuluh foto sungguhan berbentuk SVG data URI dan memangkasnya rapi dengan object-fit: cover.