Halaman yang baik menjawab sentuhan. Ketika pengguna mengarahkan kursor ke tombol atau kartu, ada umpan balik kecil yang menyatakan “elemen ini bisa dipakai”. Bagian 7 menambahkan lapisan itu dengan dua alat dari materi transitions dan animations: transisi dua properti pada tombol, kartu proyek, dan chip, lalu satu keyframes untuk animasi masuk hero yang berjalan bertingkat. Karena token --durasi dan --pelunakan sudah disiapkan sejak Bagian 1, seluruh gerak di halaman memakai kecepatan dan pelunakan yang sama — konsistensi inilah yang membedakan animasi dari gaya bebas.
Transisi pada Tombol, Kartu, dan Chip #
Setiap komponen interaktif mendapat dua aturan: deklarasi transition dan keadaan :hover atau :active:
.tombol { transition: background-color var(--durasi) var(--pelunakan), transform var(--durasi) var(--pelunakan); }
.tombol:hover { background-color: var(--warna-utama-pekat); }
.tombol:active { transform: translateY(1px); }Baris demi baris: deklarasi transition menulis dua pasang property dan durasi dipisah koma, keduanya memakai token 200 ms; saat :hover hanya background-color berubah, browser menganimasikan peralihan warna rose ke rose pekat secara halus; saat :active tombol turun 1 px, meniru tekanan fisik. Pola yang sama diterapkan ke .kartu-proyek dengan arah berbeda — kartu justru terangkat:
.kartu-proyek { transition: box-shadow var(--durasi) var(--pelunakan), transform var(--durasi) var(--pelunakan); }
.kartu-proyek:hover { transform: translateY(-4px); box-shadow: var(--bayangan-3); }Mengangkat kartu 4 px sambil menaikkan bayangan ke tingkat tiga membuatnya terasa mendekat ke pembaca. Hanya transform dan box-shadow yang berubah, dan itu disengaja: kedua properti tersebut dianimasikan browser dengan lancar, berbeda dari width atau top yang memicu perhitungan tata letak ulang di setiap frame.
Chip keahlian ikut diberi nyala ringan — :hover menukar border ke teal dan latar ke --warna-aksen-lembut. Dua tombol latar gelap juga mendapat perlakuan khusus: .panel-kontak .tombol:hover dan .tombol--garis:hover menukar latar menjadi --warna-utama-lembut, bukan rose pekat, supaya tetap terbaca di atas gradien panel.
Animasi Masuk Hero Bertingkat #
Berbeda dengan transisi yang menunggu interaksi, animasi masuk berjalan begitu halaman terbuka:
@keyframes naik-halus {
from { opacity: 0; transform: translateY(16px); }
to { opacity: 1; transform: translateY(0); }
}
.hero > * { animation: naik-halus 640ms var(--pelunakan) backwards; }
.hero > *:nth-child(2) { animation-delay: 90ms; }
.hero > *:nth-child(3) { animation-delay: 180ms; }
.hero > *:nth-child(4) { animation-delay: 270ms; }Keyframe naik-halus mendefinisikan dua keadaan: mulai transparan dan turun 16 px, berakhir penuh pada posisi normal. Selector .hero > * menerapkannya pada empat anak langsung hero, dan kata kunci backwards membuat tiap elemen memegang keadaan from selama menunggu delay — tanpa itu, elemen akan muncul lalu berkedip saat animasinya dimulai. Tiga aturan :nth-child memberi jeda 90, 180, dan 270 ms, sehingga pembuka, judul, pengantar, dan tombol muncul bergiliran seperti tiruan sapaan.
Sekali per halaman sudah cukup: animasi ini hanya di hero, bukan di semua seksi, agar halaman tetap terasa tenang. Catatan penting: gerak ini belum dijaga untuk pengguna yang menghindarinya — penanganan prefers-reduced-motion menjadi tugas utama Bagian 8.
Snapshot Berkas Tahap Ini #
Berkas berikut adalah kondisi styles.css selelah Bagian 7:
/* ============================================================
Portofolio Desainer — stylesheet utama
Kondisi: selelah Bagian 7
Urutan seksi: token, base, kerangka, tombol, hero,
galeri proyek, tentang, kontak, footer, navigasi responsif,
responsif, tipografi seksi, keadaan interaksi, aksesibilitas
============================================================ */
/* ---------- 1. Design token ---------- */
:root {
/* Warna semantik, tema terang */
--warna-latar: #faf7f2; --warna-permukaan: #ffffff;
--warna-teks: #1c1917; --warna-teks-lembut: #78716c;
--warna-garis: #e7e5e4; --warna-utama: #be123c;
--warna-utama-pekat: #9f1239; --warna-utama-kontras: #ffffff;
--warna-utama-lembut: #ffe4e6; --warna-utama-tinta: #881337;
--warna-aksen: #0f766e; --warna-aksen-lembut: #ccfbf1;
/* 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: 14px;
--radius-penuh: 999px;
/* Elevasi bayangan */
--bayangan-1: 0 1px 2px rgba(28, 25, 23, 0.06);
--bayangan-2: 0 8px 20px rgba(28, 25, 23, 0.10);
--bayangan-3: 0 16px 32px rgba(190, 18, 60, 0.16);
/* Tipografi dan gerak */
--font-dasar: system-ui, "Segoe UI", sans-serif;
--tinggi-baris: 1.65;
--font-judul: Georgia, "Times New Roman", serif;
--ukuran-display: clamp(2.2rem, 5vw, 3.2rem);
--ukuran-seksi: clamp(1.45rem, 3vw, 1.85rem);
--durasi: 200ms; --pelunakan: ease-out;
/* Kerangka halaman */
--lebar-isi: 1020px;
}
/* ---------- 2. Base dan reset ---------- */
*, *::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 {
position: sticky; top: 0; z-index: 20;
padding-block: var(--jarak-3);
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-3); flex-wrap: wrap;
}
.brand { display: flex; align-items: center; gap: var(--jarak-2); }
.brand img { width: 34px; height: 34px; }
.brand-nama { font-size: 18px; font-weight: 800; letter-spacing: -0.01em; }
.nav-utama { display: flex; gap: var(--jarak-4); }
.nav-utama a { color: var(--warna-teks-lembut); font-size: 15px; font-weight: 600; text-decoration: none; }
.nav-utama a:hover { color: var(--warna-utama); }
/* ---------- 4. Tombol ---------- */
.tombol {
display: inline-block;
padding: 10px 20px; border: none;
border-radius: var(--radius-kecil);
background-color: var(--warna-utama); color: var(--warna-utama-kontras);
font: inherit; font-size: 15px; font-weight: 600;
text-decoration: none; cursor: pointer;
}
.tombol--garis {
border: 1px solid var(--warna-utama);
background-color: transparent; color: var(--warna-utama);
}
/* ---------- 5. Hero dan perkenalan ---------- */
.hero { padding-block: var(--jarak-8) var(--jarak-7); max-width: 680px; }
.hero-pembuka {
font-size: 14px; font-weight: 700;
letter-spacing: 0.14em; text-transform: uppercase;
color: var(--warna-utama);
}
.judul-halaman {
margin-top: var(--jarak-3);
font-family: var(--font-judul);
font-size: var(--ukuran-display);
line-height: 1.15; letter-spacing: -0.02em;
}
.pengantar { margin-top: var(--jarak-4); max-width: 52ch; font-size: 17px; color: var(--warna-teks-lembut); }
.hero-aksi { display: flex; flex-wrap: wrap; gap: var(--jarak-3); margin-top: var(--jarak-5); }
.section-judul { font-size: 24px; font-weight: 800; letter-spacing: -0.01em; }
.perkenalan {
max-width: 62ch; padding-block: var(--jarak-6);
border-top: 1px solid var(--warna-garis);
}
.perkenalan p + p { margin-top: var(--jarak-3); }
/* ---------- 6. Galeri proyek ---------- */
.seksi-karya { margin-top: var(--jarak-7); }
.galeri-proyek {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: var(--jarak-5); margin-top: var(--jarak-5);
}
.kartu-proyek {
overflow: hidden;
background-color: var(--warna-permukaan); border: 1px solid var(--warna-garis);
border-radius: var(--radius); box-shadow: var(--bayangan-1);
}
.sampul { display: grid; place-items: center; aspect-ratio: 16 / 10; }
.sampul--kopi { background: linear-gradient(135deg, #7c2d12, #ea580c); }
.sampul--setor { background: linear-gradient(135deg, #1e3a8a, #3b82f6); }
.sampul--katalog { background: linear-gradient(135deg, #831843, #ec4899); }
.sampul--lumbung { background: linear-gradient(135deg, #134e4a, #14b8a6); }
.sampul--senja { background: linear-gradient(135deg, #312e81, #6366f1); }
.sampul--rupa { background: linear-gradient(135deg, #18181b, #71717a); }
.sampul-huruf { font-size: 64px; font-weight: 800; color: rgba(255, 255, 255, 0.92); }
.kartu-isi { padding: var(--jarak-4) var(--jarak-4) var(--jarak-5); }
.kartu-nama { font-size: 18px; font-weight: 800; }
.kartu-peran { margin-top: 2px; font-size: 13px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--warna-aksen); }
.kartu-catatan { margin-top: var(--jarak-2); font-size: 14.5px; color: var(--warna-teks-lembut); }
/* ---------- 7. Tentang dan keahlian ---------- */
.seksi-tentang { margin-top: var(--jarak-7); }
.tentang-grid {
display: grid; grid-template-columns: 1.4fr 1fr;
gap: var(--jarak-6); margin-top: var(--jarak-4);
}
.tentang-bio p + p { margin-top: var(--jarak-3); }
.tentang-bio p { color: var(--warna-teks-lembut); }
.tentang-label {
font-size: 13px; font-weight: 700;
letter-spacing: 0.1em; text-transform: uppercase;
color: var(--warna-aksen);
}
.daftar-chip {
display: flex; flex-wrap: wrap;
gap: var(--jarak-2); margin-top: var(--jarak-3);
padding: 0; list-style: none;
}
.chip {
padding: 6px 14px;
border: 1px solid var(--warna-garis);
border-radius: var(--radius-penuh);
background-color: var(--warna-permukaan);
font-size: 14px; font-weight: 600;
color: var(--warna-teks-lembut);
}
/* ---------- 8. Kontak ---------- */
.seksi-kontak { margin-top: var(--jarak-7); }
.panel-kontak {
display: grid; justify-items: start;
gap: var(--jarak-3); padding: var(--jarak-7) var(--jarak-6);
border-radius: var(--radius);
background: linear-gradient(135deg, var(--warna-utama-tinta), var(--warna-utama));
color: var(--warna-utama-kontras);
}
.panel-kontak p { max-width: 46ch; color: #ffe4e6; }
.panel-kontak .tombol { background-color: var(--warna-permukaan); color: var(--warna-utama-tinta); }
/* ---------- 9. Footer ---------- */
.laman-kaki {
margin-top: var(--jarak-8); padding-block: var(--jarak-6);
background-color: var(--warna-permukaan);
border-top: 1px solid var(--warna-garis);
}
.kaki-baris {
display: flex; align-items: center;
justify-content: space-between;
gap: var(--jarak-3); flex-wrap: wrap;
font-size: 14px; color: var(--warna-teks-lembut);
}
/* ---------- 10. Navigasi responsif ---------- */
.tombol-nav {
display: none; flex-direction: column;
align-items: center; justify-content: center;
width: 44px; height: 44px; padding: 0;
border: 1px solid var(--warna-garis);
border-radius: var(--radius-kecil);
background-color: var(--warna-permukaan); cursor: pointer;
}
.tombol-nav-garis { position: relative; display: block; width: 20px; height: 2px; background-color: var(--warna-teks); }
.tombol-nav-garis::before, .tombol-nav-garis::after {
content: ""; position: absolute; left: 0;
width: 20px; height: 2px; background-color: var(--warna-teks);
}
.tombol-nav-garis::before { top: -6px; }
.tombol-nav-garis::after { top: 6px; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
/* ---------- 11. Responsif ---------- */
@media (max-width: 720px) {
.tombol-nav { display: flex; }
.nav-utama {
display: none;
flex-direction: column;
gap: var(--jarak-2);
width: 100%;
padding-block: var(--jarak-3);
}
.nav-utama.terbuka { display: flex; }
}
/* ---------- 12. Tipografi seksi ---------- */
.section-judul {
font-family: var(--font-judul);
font-size: var(--ukuran-seksi); line-height: 1.25;
}
.section-judul::after {
content: ""; display: block; width: 56px; height: 3px;
margin-top: var(--jarak-2); border-radius: var(--radius-penuh);
background-color: var(--warna-utama);
}
.panel-kontak .section-judul::after { background-color: var(--warna-utama-kontras); }
/* ---------- 13. Keadaan interaksi ---------- */
.tombol { transition: background-color var(--durasi) var(--pelunakan), transform var(--durasi) var(--pelunakan); }
.tombol:hover { background-color: var(--warna-utama-pekat); }
.tombol:active { transform: translateY(1px); }
.tombol--garis:hover { background-color: var(--warna-utama-lembut); }
.panel-kontak .tombol:hover { background-color: var(--warna-utama-lembut); }
.kartu-proyek { transition: box-shadow var(--durasi) var(--pelunakan), transform var(--durasi) var(--pelunakan); }
.kartu-proyek:hover { transform: translateY(-4px); box-shadow: var(--bayangan-3); }
.chip { transition: border-color var(--durasi) var(--pelunakan), background-color var(--durasi) var(--pelunakan); }
.chip:hover { border-color: var(--warna-aksen); background-color: var(--warna-aksen-lembut); }
@keyframes naik-halus {
from { opacity: 0; transform: translateY(16px); }
to { opacity: 1; transform: translateY(0); }
}
.hero > * { animation: naik-halus 640ms var(--pelunakan) backwards; }
.hero > *:nth-child(2) { animation-delay: 90ms; }
.hero > *:nth-child(3) { animation-delay: 180ms; }
.hero > *:nth-child(4) { animation-delay: 270ms; }Ringkasan #
Semua elemen interaktif kini menjawab kursor dengan transisi yang konsisten, dan hero membuka halaman dengan animasi bertingkat yang sopan. Bagian 8 memastikan halaman yang ramai ini tetap adil: dapat diakses lewat papan ketik, kontras, dan pengaturan gerak pengguna.