Puluhan chapter sebelumnya selalu berhenti pada satu properti dalam satu waktu; kini saatnya merangkai semuanya menjadi halaman yang benar-benar bisa dinikmati. Proyek ini membangun Galeri Sudut Cahaya: halaman galeri bertema gelap berisi sepuluh foto dalam tiga kategori — Pemandangan, Urban, dan Makro — yang tersusun pada grid berirama menyerupai masonry. Setiap foto digambar sendiri sebagai SVG data URI di atas latar gradasi, sehingga halaman tetap utuh tanpa satu pun permintaan gambar ke luar. Kita mulai dari token dan grid, memangkas foto dengan object-fit, mengunci komposisi lewat aspect-ratio, menaikkan keterangan menjadi overlay, lalu menutup dengan efek hover, tata letak responsif, dan penyaring kategori berbasis JavaScript kecil. Lima bagian di bawah menyimpan snapshot berkasnya masing-masing, sehingga stylesheet tumbuh terpantau dari 141 baris menjadi 230 baris.
Perilaku yang Diharapkan #
Halaman akhirnya memiliki header dengan merek dan tagline, pengantar singkat, bilah filter dengan penghitung, grid sepuluh foto, dan footer. Perilaku berikut bisa dicoba langsung pada demo dan pada berkas akhir:
- Grid tiga kolom dengan unit baris 96 px: setiap item membentang 2-5 baris atau 2-3 kolom, dan
grid-auto-flow: densemengisi lubang yang tertinggal di samping foto tinggi. - Mengarahkan kursor ke sebuah foto memperbesar gambarnya 6% dan menaikkan keterangan overlay dari dasar bidang; pada peranti sentuh, keterangan selalu tampil.
- Tombol filter Semua, Pemandangan, Urban, dan Makro memindahkan
aria-pressed, menyembunyikan foto kategori lain lewat class.tersembunyi, dan memperbarui hitungan “Menampilkan N foto”. - Di bawah 940 px grid menyusut menjadi dua kolom; di bawah 620 px menjadi satu kolom dengan tinggi bidang foto dikunci
aspect-ratio. prefers-reduced-motion: reducemematikan seluruh transisi, sedangkan:focus-visiblememberi cincin kuning pada tautan foto dan tombol filter.
Demo berikut memperagakan inti perilakunya — rentang grid, keterangan overlay, dan efek hover — dengan versi mini empat foto:
Struktur Folder #
Pola foldernya sama dengan proyek CSS lain di course ini: src/ menyimpan kondisi akhir, folder part-N- menyimpan snapshot tahapan, dan salinan berkas utama diletakkan di akar bundle agar mudah diperiksa.
galeri-fotografi/
├── _index.md # halaman overview (berkas ini)
├── beranda.html # salinan cepat dari src/index.html
├── styles.css # salinan cepat dari src/css/styles.css
├── main.js # salinan cepat dari src/js/main.js
├── src/ # kondisi akhir proyek
│ ├── index.html
│ ├── css/styles.css
│ └── js/main.js
├── part-1-gallery-grid/ # snapshot styles.css tahap 1
├── part-2-image-cropping-dengan-object-fit/ # snapshot styles.css + beranda.html
├── part-3-aspect-ratio/ # snapshot styles.css + beranda.html
├── part-4-caption-dan-overlay/ # snapshot styles.css + beranda.html
└── part-5-responsive-gallery/ # snapshot ketiga berkas (= src)Tahapan Pengerjaan #
- Bagian 1 — Grid Galeri menetapkan token tema gelap, base ringkas, kerangka halaman, bilah filter, dan grid tiga kolom berunit baris tetap dengan rentang baris-kolom per item. Snapshot:
styles.css. - Bagian 2 — Pemangkasan Foto dengan object-fit memasukkan sepuluh foto SVG data URI dan memangkasnya dengan
object-fit: coverdi atas latar cadangan per kategori. Snapshot:styles.css+beranda.html. - Bagian 3 — Rasio Foto dengan aspect-ratio menjadikan empat token rasio tindakan nyata pada tampilan satu kolom. Snapshot:
styles.css+beranda.html. - Bagian 4 — Keterangan dan Overlay mengubah figcaption polos menjadi overlay gradasi inset di dasar foto. Snapshot:
styles.css+beranda.html. - Bagian 5 — Galeri Responsif dan Penyaring menambah transisi, efek hover, gaya fokus, breakpoint dua kolom, serta
main.jsuntuk filter kategori. Snapshot: ketiga berkas, identik dengansrc/.
Berkas Sumber Lengkap #
Ketiga berkas berikut adalah kondisi akhir proyek, persis seperti yang tersimpan di folder src/ dan disalin utuh oleh Bagian 5.
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Galeri Sudut Cahaya</title>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
<header class="kepala-galeri pembungkus">
<h1 class="merek">Galeri Sudut Cahaya</h1>
<p class="tagline">Koleksi foto pemandangan, urban, dan makro</p>
</header>
<main class="pembungkus">
<section class="pengantar">
<h2>Sepuluh momen pilihan</h2>
<p>Sepuluh foto dikurasi dari tiga kategori dan disusun pada grid yang ritmenya mengikuti rasio masing-masing bidang. Gunakan bilah filter untuk menyaring koleksi, dan arahkan kursor ke foto untuk melihat keterangannya.</p>
</section>
<div class="bilah-filter" id="bilah-filter" role="group" aria-label="Saring foto menurut kategori">
<button type="button" class="tombol-filter" data-kategori="semua" aria-pressed="true">Semua</button>
<button type="button" class="tombol-filter" data-kategori="pemandangan" aria-pressed="false">Pemandangan</button>
<button type="button" class="tombol-filter" data-kategori="urban" aria-pressed="false">Urban</button>
<button type="button" class="tombol-filter" data-kategori="makro" aria-pressed="false">Makro</button>
<p class="hitung-galeri">Menampilkan <span id="hitung-galeri">10</span> foto</p>
</div>
<div class="galeri">
<figure class="galeri-item galeri-item--lebar" data-kategori="pemandangan">
<a class="galeri-tautan" href="#senja-di-deraung"><span class="galeri-media galeri-media--pemandangan">
<img class="galeri-foto" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 840 360'%3E%3Cdefs%3E%3ClinearGradient id='a' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%23f0a35e'/%3E%3Cstop offset='1' stop-color='%23a8432c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='840' height='360' fill='url(%23a)'/%3E%3Ccircle cx='610' cy='150' r='42' fill='%23ffe0a3'/%3E%3Cpolygon points='0,360 250,170 520,360' fill='%23602a38'/%3E%3Cpolygon points='330,360 590,215 840,360' fill='%23401c2b'/%3E%3C/svg%3E" alt="Siluet perbukitan bertingkat dengan matahari terbenam di Deraung" width="840" height="360" loading="lazy">
</span></a>
<figcaption class="galeri-keterangan"><strong class="galeri-judul">Senja di Deraung</strong> <span class="galeri-meta">Pemandangan · 35 mm · f/8 · 1/125 dtk</span></figcaption>
</figure>
<figure class="galeri-item" data-kategori="pemandangan">
<a class="galeri-tautan" href="#kabut-telaga-raya"><span class="galeri-media galeri-media--pemandangan">
<img class="galeri-foto" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 800 450'%3E%3Cdefs%3E%3ClinearGradient id='b' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%239fc4bd'/%3E%3Cstop offset='1' stop-color='%2357837c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='800' height='450' fill='url(%23b)'/%3E%3Ccircle cx='190' cy='110' r='34' fill='%23e8f2ee'/%3E%3Cpolygon points='0,340 240,190 470,340' fill='%23314b4a'/%3E%3Cpolygon points='260,340 520,215 800,340' fill='%23243738'/%3E%3Crect y='340' width='800' height='110' fill='%232c4344'/%3E%3C/svg%3E" alt="Kabut pagi menyelimuti telaga dan dua bukit berlapis" width="800" height="450" loading="lazy">
</span></a>
<figcaption class="galeri-keterangan"><strong class="galeri-judul">Kabut Telaga Raya</strong> <span class="galeri-meta">Pemandangan · 24 mm · f/11 · 1/60 dtk</span></figcaption>
</figure>
<figure class="galeri-item" data-kategori="urban">
<a class="galeri-tautan" href="#gang-lampu-neon"><span class="galeri-media galeri-media--urban">
<img class="galeri-foto" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 800 450'%3E%3Crect width='800' height='450' fill='%23141821'/%3E%3Crect x='40' y='60' width='180' height='390' fill='%231d2430'/%3E%3Crect x='250' y='20' width='220' height='430' fill='%23232c3b'/%3E%3Crect x='500' y='90' width='260' height='360' fill='%231d2430'/%3E%3Crect x='250' y='20' width='220' height='16' fill='%23e84d8a'/%3E%3Crect x='40' y='60' width='180' height='10' fill='%2340c4b5'/%3E%3Crect x='500' y='90' width='260' height='10' fill='%23e8a13d'/%3E%3Crect x='285' y='80' width='24' height='36' fill='%2340c4b5'/%3E%3Crect x='330' y='80' width='24' height='36' fill='%23e84d8a'/%3E%3Crect x='375' y='80' width='24' height='36' fill='%2340c4b5'/%3E%3Crect x='540' y='140' width='20' height='28' fill='%23e8a13d'/%3E%3Crect x='580' y='140' width='20' height='28' fill='%2340c4b5'/%3E%3Crect x='620' y='140' width='20' height='28' fill='%23e84d8a'/%3E%3C/svg%3E" alt="Dinding gedung bertumpuk dengan jalur lampu neon merah, hijau, dan kuning" width="800" height="450" loading="lazy">
</span></a>
<figcaption class="galeri-keterangan"><strong class="galeri-judul">Gang Lampu Neon</strong> <span class="galeri-meta">Urban · 50 mm · f/2 · 1/30 dtk</span></figcaption>
</figure>
<figure class="galeri-item galeri-item--tinggi" data-kategori="makro">
<a class="galeri-tautan" href="#tetes-embun-pakis"><span class="galeri-media galeri-media--makro">
<img class="galeri-foto" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 600 900'%3E%3Cdefs%3E%3ClinearGradient id='c' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%232f6b3a'/%3E%3Cstop offset='1' stop-color='%2312301c'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='600' height='900' fill='url(%23c)'/%3E%3Cellipse cx='300' cy='480' rx='210' ry='380' fill='%233d7d47'/%3E%3Cpath d='M300 120 L300 860' stroke='%23245530' stroke-width='10'/%3E%3Ccircle cx='300' cy='430' r='58' fill='%23cfe8d5'/%3E%3Ccircle cx='282' cy='410' r='14' fill='%23ffffff'/%3E%3C/svg%3E" alt="Tetes embun besar di permukaan daun pakis berwarna hijau pekat" width="600" height="900" loading="lazy">
</span></a>
<figcaption class="galeri-keterangan"><strong class="galeri-judul">Tetes Embun Pakis</strong> <span class="galeri-meta">Makro · 90 mm · f/4 · 1/200 dtk</span></figcaption>
</figure>
<figure class="galeri-item galeri-item--panorama" data-kategori="urban">
<a class="galeri-tautan" href="#jembatan-kota-batavia"><span class="galeri-media galeri-media--urban">
<img class="galeri-foto" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1050 300'%3E%3Cdefs%3E%3ClinearGradient id='d' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%232c3d55'/%3E%3Cstop offset='1' stop-color='%231a2433'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='1050' height='300' fill='url(%23d)'/%3E%3Crect x='140' y='70' width='16' height='180' fill='%230e131c'/%3E%3Crect x='510' y='40' width='16' height='210' fill='%230e131c'/%3E%3Crect x='880' y='70' width='16' height='180' fill='%230e131c'/%3E%3Cpolyline points='0,140 148,120 300,150 518,90 740,150 888,120 1050,140' fill='none' stroke='%23566b8a' stroke-width='4'/%3E%3Crect y='250' width='1050' height='50' fill='%230e131c'/%3E%3Ccircle cx='100' cy='240' r='8' fill='%23ffd479'/%3E%3Ccircle cx='350' cy='240' r='8' fill='%23ffd479'/%3E%3Ccircle cx='620' cy='240' r='8' fill='%23ffd479'/%3E%3Ccircle cx='900' cy='240' r='8' fill='%23ffd479'/%3E%3C/svg%3E" alt="Bentang jembatan kabel dengan tiga menara dan lampu kuning saat petang" width="1050" height="300" loading="lazy">
</span></a>
<figcaption class="galeri-keterangan"><strong class="galeri-judul">Jembatan Kota Batavia</strong> <span class="galeri-meta">Urban · 70 mm · f/9 · 2 dtk · tripod</span></figcaption>
</figure>
<figure class="galeri-item" data-kategori="pemandangan">
<a class="galeri-tautan" href="#bukit-lavanya"><span class="galeri-media galeri-media--pemandangan">
<img class="galeri-foto" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 800 450'%3E%3Cdefs%3E%3ClinearGradient id='e' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%23cfa9d9'/%3E%3Cstop offset='1' stop-color='%238a5f9e'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='800' height='450' fill='url(%23e)'/%3E%3Ccircle cx='640' cy='95' r='30' fill='%23f2e6f5'/%3E%3Cpolygon points='0,450 190,260 420,450' fill='%235d3a75'/%3E%3Cpolygon points='250,450 480,290 720,450' fill='%233f2657'/%3E%3Cpolygon points='0,450 640,340 800,450 0,450' fill='%232a183c'/%3E%3C/svg%3E" alt="Rangkaian bukit ungu berlapis di bawah langit senja lavender" width="800" height="450" loading="lazy">
</span></a>
<figcaption class="galeri-keterangan"><strong class="galeri-judul">Bukit Lavanya</strong> <span class="galeri-meta">Pemandangan · 85 mm · f/5.6 · 1/250 dtk</span></figcaption>
</figure>
<figure class="galeri-item galeri-item--besar" data-kategori="urban">
<a class="galeri-tautan" href="#fasad-kaca-merdeka"><span class="galeri-media galeri-media--urban">
<img class="galeri-foto" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 700 400'%3E%3Crect width='700' height='400' fill='%2322303f'/%3E%3Crect x='30' y='30' width='150' height='100' fill='%233a556e'/%3E%3Crect x='210' y='30' width='150' height='100' fill='%23517893'/%3E%3Crect x='390' y='30' width='150' height='100' fill='%233a556e'/%3E%3Crect x='570' y='30' width='100' height='100' fill='%23517893'/%3E%3Crect x='30' y='160' width='150' height='100' fill='%23517893'/%3E%3Crect x='210' y='160' width='150' height='100' fill='%233a556e'/%3E%3Crect x='390' y='160' width='150' height='100' fill='%23517893'/%3E%3Crect x='570' y='160' width='100' height='100' fill='%233a556e'/%3E%3Crect x='30' y='290' width='640' height='80' fill='%231a2531'/%3E%3C/svg%3E" alt="Fasad kaca gedung perkantoran berpola jendela biru keabu-abuan" width="700" height="400" loading="lazy">
</span></a>
<figcaption class="galeri-keterangan"><strong class="galeri-judul">Fasad Kaca Merdeka</strong> <span class="galeri-meta">Urban · 24 mm · f/8 · 1/160 dtk</span></figcaption>
</figure>
<figure class="galeri-item" data-kategori="makro">
<a class="galeri-tautan" href="#mata-kupu-kupu"><span class="galeri-media galeri-media--makro">
<img class="galeri-foto" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 800 450'%3E%3Cdefs%3E%3ClinearGradient id='f' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%23274023'/%3E%3Cstop offset='1' stop-color='%230f1d10'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='800' height='450' fill='url(%23f)'/%3E%3Cellipse cx='330' cy='225' rx='150' ry='95' fill='%23d98e32' transform='rotate(-18 330 225)'/%3E%3Cellipse cx='470' cy='225' rx='150' ry='95' fill='%23d98e32' transform='rotate(18 470 225)'/%3E%3Cellipse cx='400' cy='225' rx='18' ry='95' fill='%231d1509'/%3E%3Ccircle cx='290' cy='200' r='26' fill='%23f2e2b6'/%3E%3Ccircle cx='510' cy='200' r='26' fill='%23f2e2b6'/%3E%3C/svg%3E" alt="Sayap kupu-kupu jingga dengan bercak krem terlihat dari dekat" width="800" height="450" loading="lazy">
</span></a>
<figcaption class="galeri-keterangan"><strong class="galeri-judul">Mata Kupu-Kupu</strong> <span class="galeri-meta">Makro · 100 mm · f/2.8 · 1/320 dtk</span></figcaption>
</figure>
<figure class="galeri-item galeri-item--tinggi" data-kategori="makro">
<a class="galeri-tautan" href="#heliconia-merah"><span class="galeri-media galeri-media--makro">
<img class="galeri-foto" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 600 900'%3E%3Crect width='600' height='900' fill='%23101c14'/%3E%3Cpath d='M320 900 L300 260' stroke='%23245530' stroke-width='14' fill='none'/%3E%3Cpolygon points='300,260 180,320 260,380' fill='%23c23b3b'/%3E%3Cpolygon points='300,340 420,390 330,450' fill='%23c23b3b'/%3E%3Cpolygon points='300,420 170,470 270,530' fill='%23e86a5a'/%3E%3Cpolygon points='300,500 430,550 330,610' fill='%23c23b3b'/%3E%3Cpolygon points='300,580 180,630 280,690' fill='%23e86a5a'/%3E%3C/svg%3E" alt="Juntungan bunga heliconia merah bertingkat di latar hijau gelap" width="600" height="900" loading="lazy">
</span></a>
<figcaption class="galeri-keterangan"><strong class="galeri-judul">Heliconia Merah</strong> <span class="galeri-meta">Makro · 60 mm · f/5.6 · 1/125 dtk</span></figcaption>
</figure>
<figure class="galeri-item galeri-item--lebar" data-kategori="pemandangan">
<a class="galeri-tautan" href="#danau-warna-fajar"><span class="galeri-media galeri-media--pemandangan">
<img class="galeri-foto" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 840 360'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%23f2c9a0'/%3E%3Cstop offset='1' stop-color='%23b86a4e'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='840' height='360' fill='url(%23g)'/%3E%3Ccircle cx='420' cy='160' r='38' fill='%23ffe9c9'/%3E%3Cpolygon points='0,250 220,170 430,250' fill='%234f3450'/%3E%3Cpolygon points='360,250 600,160 840,250' fill='%233a2440'/%3E%3Crect y='250' width='840' height='110' fill='%23302a44'/%3E%3Cpolygon points='380,250 420,290 460,250' fill='%23ffe9c9'/%3E%3C/svg%3E" alt="Danau tenang saat fajar dengan pantulan matahari di antara dua bukit" width="840" height="360" loading="lazy">
</span></a>
<figcaption class="galeri-keterangan"><strong class="galeri-judul">Danau Warna Fajar</strong> <span class="galeri-meta">Pemandangan · 28 mm · f/13 · 1/45 dtk</span></figcaption>
</figure>
</div>
</main>
<footer class="kaki-galeri">
<div class="pembungkus kaki-isi">
<p>Galeri Sudut Cahaya — koleksi latihan CSS Grid dan object-fit</p>
<p>Semua foto digambar sebagai SVG data URI</p>
</div>
</footer>
<script src="js/main.js"></script>
</body>
</html>/* ============================================================
Galeri Sudut Cahaya — stylesheet utama
Urutan: token → base → kerangka halaman → bilah filter
→ grid galeri → media foto dan object-fit → keterangan overlay
→ hover, fokus, dan transisi → responsive
============================================================ */
/* ---------- 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 dan object-fit ---------- */
.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);
}
.galeri-foto {
width: 100%;
height: 100%;
object-fit: cover;
}
.galeri-media--pemandangan { background: linear-gradient(165deg, #2e6f5e 0%, #17453c 70%); }
.galeri-media--urban { background: linear-gradient(165deg, #3d4a5c 0%, #1f2733 70%); }
.galeri-media--makro { background: linear-gradient(165deg, #6d4a2f 0%, #2f1f14 70%); }
/* ---------- 7. Keterangan dan overlay ---------- */
.galeri-keterangan {
position: absolute;
inset: auto 0 0 0;
display: flex;
flex-direction: column;
gap: 2px;
padding: var(--jarak-6) var(--jarak-4) var(--jarak-3);
border-radius: 0 0 var(--radius-sedang) var(--radius-sedang);
background: linear-gradient(to top, rgba(9, 13, 17, 0.92) 0%, rgba(9, 13, 17, 0.55) 55%, rgba(9, 13, 17, 0) 100%);
color: var(--warna-teks);
}
.galeri-judul { font-size: 0.98rem; font-weight: 600; line-height: 1.35; }
.galeri-meta { font-size: 0.78rem; letter-spacing: 0.04em; color: var(--warna-teks-lembut); }
/* ---------- 8. Hover, fokus, dan transisi ---------- */
.galeri-foto { transition: transform var(--durasi-gerak) ease; }
.galeri-tautan:hover .galeri-foto { transform: scale(1.06); }
.galeri-keterangan {
opacity: 0;
transform: translateY(10px);
transition: opacity var(--durasi-gerak) ease, transform var(--durasi-gerak) ease;
}
.galeri-tautan:hover + .galeri-keterangan,
.galeri-item:focus-within .galeri-keterangan { opacity: 1; transform: translateY(0); }
@media (hover: none) {
.galeri-keterangan { opacity: 1; transform: none; transition: none; }
}
.tombol-filter { transition: background var(--durasi-gerak) ease, color var(--durasi-gerak) ease, border-color var(--durasi-gerak) ease; }
.tombol-filter:hover { border-color: var(--warna-utama); color: var(--warna-utama); }
.tombol-filter[aria-pressed="true"] { background: var(--warna-utama); border-color: var(--warna-utama); color: var(--warna-utama-teks); font-weight: 600; }
.galeri-tautan:focus-visible { outline: 3px solid var(--warna-fokus); outline-offset: 3px; }
.tombol-filter:focus-visible { outline: 3px solid var(--warna-fokus); outline-offset: 2px; }
.galeri-item.tersembunyi { display: none; }
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
.galeri-foto, .galeri-keterangan, .tombol-filter { transition: none; }
.galeri-tautan:hover .galeri-foto { transform: none; }
.galeri-keterangan { transform: none; }
}
/* ---------- 9. Responsive ---------- */
@media (max-width: 940px) {
.galeri { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.galeri-item--panorama { grid-column: span 2; }
.galeri-item--lebar { grid-column: span 2; grid-row: span 3; }
.galeri-item--besar { grid-column: span 2; grid-row: span 4; }
.galeri-item--tinggi { grid-row: span 5; }
}
@media (max-width: 620px) {
.galeri { grid-template-columns: minmax(0, 1fr); grid-auto-rows: auto; }
.galeri-item, .galeri-item--lebar, .galeri-item--besar, .galeri-item--panorama, .galeri-item--tinggi { grid-column: auto; grid-row: auto; }
.galeri-media { height: auto; aspect-ratio: var(--rasio-lanskap); }
.galeri-item--tinggi .galeri-media { aspect-ratio: var(--rasio-potret); }
.galeri-item--lebar .galeri-media, .galeri-item--panorama .galeri-media { aspect-ratio: var(--rasio-panorama); }
.galeri-item--besar .galeri-media { aspect-ratio: var(--rasio-besar); }
.hitung-galeri { margin-left: 0; width: 100%; }
.kaki-isi { flex-direction: column; }
}// ============================================================
// Galeri Sudut Cahaya — penyaring kategori galeri
// id yang dirujuk: bilah-filter dan hitung-galeri (beranda.html)
// ============================================================
"use strict";
const grupFilter = document.getElementById("bilah-filter");
const tombolFilter = grupFilter ? Array.from(grupFilter.querySelectorAll(".tombol-filter")) : [];
const itemGaleri = Array.from(document.querySelectorAll(".galeri-item"));
const hitungGaleri = document.getElementById("hitung-galeri");
// Menuliskan jumlah foto yang lolos saringan ke elemen penghitung.
function perbaruiHitungan(jumlah) {
if (hitungGaleri) {
hitungGaleri.textContent = String(jumlah);
}
}
// Menandai satu tombol sebagai aktif lewat atribut aria-pressed.
function tandaiTombolAktif(tombolAktif) {
tombolFilter.forEach(function (tombol) {
const aktif = tombol === tombolAktif;
tombol.setAttribute("aria-pressed", aktif ? "true" : "false");
});
}
// Menyembunyikan foto yang kategorinya tidak cocok memakai class tersembunyi.
function saringGaleri(kategori) {
let jumlahTampil = 0;
itemGaleri.forEach(function (item) {
const cocok = kategori === "semua" || item.dataset.kategori === kategori;
item.classList.toggle("tersembunyi", !cocok);
if (cocok) {
jumlahTampil += 1;
}
});
perbaruiHitungan(jumlahTampil);
}
// Satu pendengar klik untuk setiap tombol filter.
tombolFilter.forEach(function (tombol) {
tombol.addEventListener("click", function () {
tandaiTombolAktif(tombol);
saringGaleri(tombol.dataset.kategori);
});
});
// Kondisi awal: tombol Semua aktif, seluruh koleksi tampil, hitungan 10.
if (tombolFilter.length > 0) {
saringGaleri("semua");
}Menetapkan token tema gelap, base ringkas, kerangka halaman, bilah filter, dan grid tiga kolom berunit baris tetap dengan rentang baris-kolom untuk ritme galeri.
Buka → # Bagian 2: Pemangkasan Foto dengan object-fitMemasukkan sepuluh foto SVG data URI, menata area media, dan memangkas gambar dengan object-fit: cover di atas latar cadangan per kategori.
Buka → # Bagian 3: Rasio Foto dengan aspect-ratioMenjadikan empat token rasio sebagai nilai aspect-ratio yang sesungguhnya pada tampilan satu kolom, sehingga setiap foto menjaga bentuk komposisinya.
Buka → # Bagian 4: Keterangan dan OverlayMengubah figcaption polos menjadi overlay menempel di dasar foto dengan gradasi inset, lengkap dengan tipografi judul dan baris meta.
Buka → # Bagian 5: Galeri Responsif dan PenyaringMenambahkan transisi dan efek hover, gaya fokus, breakpoint dua kolom, serta main.js yang menyalakan filter kategori beserta penghitungnya.
Buka →