Header yang menyusut saat di-scroll adalah pola UI umum, yaitu elemen header tetap di bagian atas halaman bertransisi dengan lancar ke ukuran yang lebih kecil saat pengguna men-scroll ke bawah. Efek ini sering digunakan untuk memaksimalkan ruang layar untuk konten utama sekaligus menjaga elemen navigasi atau branding yang penting tetap dapat diakses. Dengan animasi berbasis scroll CSS, efek ini dapat dicapai secara deklaratif dan berperforma tinggi, dengan menautkan animasi ke posisi scroll dokumen.
Cara menerapkan
Berikut cara membuat header yang menyusut saat di-scroll:
Buat header tetap: Mulailah dengan elemen header yang ditetapkan ke bagian atas halaman dan memiliki tinggi yang telah ditentukan.
<header>HEADER</header>header { position: fixed; height: 200px; top: 0; left: 0; right: 0; }Tentukan animasi penyusutan: Buat animasi CSS yang mengubah tinggi header.
@keyframes shrink { to { height: 50px; } }Terapkan animasi dan linimasa scroll: Lampirkan animasi ke header dan gunakan fungsi
scroll()untuk menautkannya ke posisi scroll dokumen.header { animation: shrink auto linear both; animation-timeline: scroll(block root); }Menetapkan
animation-range: Gunakan propertianimation-rangeuntuk menentukan jarak scroll tempat animasi akan terjadi. Misalnya, untuk memperkecil header pada 150 piksel pertama scroll, Anda akan menggunakananimation-range: 0px 150px;.header { animation-range: 0px 150px; }
Tips: Agar konten setelah header tidak tertutup oleh header, tambahkan padding-top ke body (atau penampung konten utama) yang sama dengan tinggi awal header.
Tips: Untuk memastikan konten halaman di-scroll sinkron dengan header yang menyusut, tetapkan animation-range-end ke perbedaan antara ukuran awal dan akhir. Hal ini memastikan animasi selesai tepat saat header mencapai ukuran akhirnya. Dalam demo ini, header menyusut dari 200px menjadi 50px, sehingga animation-range-end ditetapkan ke 150px.
Kode contoh
@keyframes shrink {
to {
height: 50px;
}
}
header {
animation: shrink auto linear both;
animation-timeline: scroll(block root);
animation-range: 0px 150px;
}
Praktik Terbaik
Saat menggunakan animasi berbasis scroll, penting untuk mengikuti beberapa praktik terbaik guna memastikan pengalaman yang lancar dan mudah diakses:
- LAKUKAN penyertaan deteksi fitur: Tidak semua browser mendukung animasi berbasis scroll. Gunakan
@supports ((animation-timeline: scroll()) and (animation-range: 0% 100%))untuk memeriksa dukungan dan memberikan penggantian untuk browser yang tidak mendukungnya.- Pemeriksaan
(animation-range: 0% 100%)HARUS disertakan di sini, untuk memfilter browser yang hanya memiliki dukungan parsial. - JANGAN gunakan paket
scroll-timeline-polyfilluntuk strategi penggantian karena fiturnya tidak lengkap dan memiliki banyak masalah umum. - Jika animasi hanya dianggap sebagai dekoratif, pilih Progressive Enhancement dan JANGAN berikan penggantian.
- Pemeriksaan
- LAKUKAN penghormatan terhadap preferensi pengguna: Beberapa pengguna lebih memilih untuk memiliki lebih sedikit gerakan di web. Gunakan kueri media
prefers-reduced-motionuntuk menonaktifkan atau mengurangi animasi bagi pengguna ini. - LAKUKAN upaya untuk menganimasikan hanya properti CSS yang berperforma tinggi: Untuk animasi yang paling lancar, tetap gunakan properti animasi yang dapat ditangani oleh thread compositor browser, seperti
transformdanopacity. Menganimasikan properti lain sepertiwidthatauheightdapat menyebabkan masalah performa. - LAKUKAN penggunaan urutan deklarasi yang benar: Saat menggunakan properti singkat
animation, deklarasikananimation-timelinedananimation-rangesetelahnya untuk mencegah properti singkat mereset linimasa.
Saat menggunakan fungsi scroll() untuk membuat animasi berbasis scroll:
- OPSIONAL tentukan scroller secara eksplisit: Jika tidak menargetkan scroller ancestor terdekat, tentukan scroller yang ingin Anda gunakan secara eksplisit dengan
scroll(root)atauscroll(self).- Jika
root,nearest, atauselftidak cukup, gunakan scroll-timeline bernama.
- Jika
- OPSIONAL jelaskan sumbu yang akan dilacak: Jika tidak menargetkan sumbu
blockdefault (seperti dalam scroller horizontal), jelaskan sumbu yang akan dilacak denganscroll(block)atauscroll(inline).
Khusus untuk kasus penggunaan ini:
- Elemen yang Anda animasikan HARUS bukan
position: staticatauposition: relativesaat menggunakan persentase dianimation-range.- Hal ini karena elemen tersebut dianggap “dalam alur”. Memperkecil elemen tersebut saat Anda men-scroll akan memperkecil total jarak scroll, sehingga memengaruhi nilai yang dihitung — misalnya —
10%ke dalam scroll.
- Hal ini karena elemen tersebut dianggap “dalam alur”. Memperkecil elemen tersebut saat Anda men-scroll akan memperkecil total jarak scroll, sehingga memengaruhi nilai yang dihitung — misalnya —
Dukungan browser dan strategi penggantian
Oleh karena itu, strategi penggantian biasanya diperlukan.
Untuk browser yang tidak mendukung animasi berbasis scroll, Anda dapat menggunakan penggantian untuk membuat ulang efek visual. Penggantian biasanya dibuat dengan pemroses scroll (untuk efek ScrollTimeline) atau IntersectionObserver API (untuk efek ViewTimeline).
Di browser dengan dukungan bawaan untuk animasi berbasis scroll, SELALU gunakan implementasi CSS native karena lebih berperforma.
Perhatikan bahwa tidak semua efek dapat dibuat ulang menggunakan pendekatan penggantian.
Khusus untuk kasus penggunaan ini, skrip berikut menerapkan penggantian untuk browser yang tidak mendukung animasi berbasis scroll. Kode ini menggunakan pemroses scroll untuk melacak posisi scroll dokumen pada jarak 150px dan memperbarui tinggi header.
// Fallback for browsers that don't support scroll-driven animations
if (!CSS.supports('(animation-timeline: scroll()) and (animation-range: 0% 100%)')) {
const header = document.querySelector('header');
const initialHeight = 200;
const finalHeight = 50;
const scrollDistance = 150;
window.addEventListener('scroll', () => {
const scrollY = window.scrollY;
const scrollPercent = Math.min(1, scrollY / scrollDistance);
const newHeight = initialHeight - (initialHeight - finalHeight) * scrollPercent;
header.style.height = `${newHeight}px`;
});
}