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:

css
.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:

css
.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:

css
@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:

css
/* ============================================================
   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.