scroll-state() CSS

Seperti kueri penampung; tetapi untuk kueri yang macet, terkunci, dan meluap.

Dipublikasikan: 15 Jan 2025

Chrome 133 dibuat berdasarkan kueri penampung dengan memperkenalkan kueri penampung status scroll. Status yang dikelola browser untuk posisi melekat, titik snap scroll, dan elemen yang dapat di-scroll kini dapat dikueri dan disesuaikan dari CSS.

Ringkasan

Sebelum kueri status scroll, Anda harus menggunakan JavaScript untuk memahami apakah elemen macet, terkunci, atau dapat di-scroll. Sekarang ada metode yang lebih berperforma di jalur standar untuk mengetahui informasi ini dan beradaptasi dengan sesuai. Ada juga cara baru untuk memicu animasi, yang membuka animasi yang dipicu scroll dari CSS.

Berikut adalah ringkasan kueri status yang tersedia mulai Chrome 133:

Status macet:
Memicu perubahan gaya saat elemen menempel ke tepi.
Status snap:
Memicu perubahan gaya saat elemen disambungkan ke sumbu.
Status yang dapat di-scroll:
Memicu perubahan gaya saat elemen meluap.

Kabar baiknya adalah semua yang telah Anda pelajari dari kueri penampung akan membantu Anda menggunakan kueri status scroll.

Ada juga wilayah yang belum dijelajahi antara animasi yang didorong scroll dan kueri penampung status scroll; kita perlu bereksperimen dengan pengaturan waktu dan konteks untuk mengetahui apakah animasi yang didorong scroll atau animasi status scroll yang dipicu scroll akan menjadi yang terbaik. Video dan demo berikut mengilustrasikan masalah; animasi yang dipicu sticky dibandingkan dengan animasi yang didorong scroll.

(kiri) animasi yang dipicu scroll-state(), (kanan) animasi yang didorong scroll
https://codepen.io/web-dot-dev/pen/emOrBaV

Kueri status scroll pertama

Langkah pertama adalah menentukan penampung, menggunakan nilai baru untuk properti container-type. Seperti kueri penampung, elemen yang ingin Anda buat kuerinya adalah elemen yang Anda beri container-type dan secara opsional container-name. Dengan kueri status scroll, Anda memberikan elemen yang terkunci, macet, atau memiliki container-type: scroll-state tambahan.

.stuck-top {
  container-type: scroll-state;
  position: sticky;
  top: 0px;
}

Langkah kedua adalah memilih turunan penampung tersebut yang akan merespons status. Seperti kueri penampung, elemen ini tidak boleh sama dengan elemen yang memiliki container-type.

.stuck-top {
  container-type: scroll-state;
  position: sticky;
  top: 0px;

  > nav {
    @container scroll-state(stuck: top) {
      background: Highlight;
      color: HighlightText;
    }
  }
}

Langkah ketiga adalah mencobanya. Contoh CSS berikut akan menata gaya latar belakang menjadi merah saat elemen .stuck-top menempel ke bagian atas di 0. Dengan beberapa baris tambahan ke CSS yang telah kita tulis dan elemen penampung tambahan yang mem-proxy status browser, komponen kita akan jauh lebih cerdas dalam memahami lingkungannya.

https://codepen.io/web-dot-dev/pen/ByBxpwR

Progressive Enhancement

Aturan dan bertingkat @supports memungkinkan Anda menambahkan progressive enhancement atau penggunaan fitur bersyarat hanya dalam beberapa baris kode tambahan:

.stuck-top {
  container-type: scroll-state;
  position: sticky;
  top: 0px;

  @supports (container-type: scroll-state) {
    > nav {
      @container scroll-state(stuck: top) {
        background: Highlight;
        color: HighlightText;
      }
    }
  }
}

Selain itu, jangan lupa untuk menggunakan @media (prefers-reduced-motion: no-preference) {} di sekitar gerakan, jika Anda akhirnya menganimasikan elemen di sekitar halaman dengan kueri status scroll.

Kasus Penggunaan

Macet

Mungkin bagian ini sebaiknya disebut "situasi sulit?" Ini adalah kumpulan kecil kasus penggunaan status melekat, ditambah bagian bonus ide yang perlu dibuat.

@container scroll-state(stuck: top) {}
@container scroll-state(stuck: bottom) {}

Daftar sintaksis lengkap

Menambahkan bayangan saat macet

Salah satu kasus penggunaan yang paling umum untuk kueri yang macet adalah untuk menu navigasi yang ingin menambahkan box-shadow saat macet, sehingga menu tersebut dapat muncul mengambang di atas konten yang di-overlay.

https://codepen.io/web-dot-dev/pen/GgKdryj
.stuck-top {
  container-type: scroll-state;
  position: sticky;
  top: 0px;

  > nav {
    transition: box-shadow .3s ease;

    @container scroll-state(stuck: top) {
      box-shadow: var(--shadow-5);
    }
  }
}

Mengaktifkan header yang macet saat ini

Skenario masukan UI melekat umum lainnya adalah menandai elemen yang saat ini macet. Dalam daftar band yang diurutkan menurut abjad, hal ini dapat sangat membantu dan mendukung pengalaman.

https://codepen.io/web-dot-dev/pen/pvzVRaK
.sticky-slide {
  dt {
    container-type: scroll-state;
    position: sticky;
    inset-block-start: 0;
    inset-inline: 0;

    > header {
      transition: 
        background .3s ease,
        box-shadow .5s ease;

      @container scroll-state(stuck: top) {
        background: hsl(265 100% 27%);
        box-shadow: 0 5px 5px #0003;
      }
    }
  }
}

Berikut adalah varian lain, dengan header di sisi item daftar. Ada banyak kemungkinannya.

https://codepen.io/web-dot-dev/pen/azoGpGg

Overflow ide

Berikut adalah daftar demo melekat yang mungkin menginspirasi Anda untuk menambahkan sedikit bumbu ke demo, atau menghapus JavaScript-nya, dengan kueri status scroll. Sebaiknya coba buat aplikasi yang Anda sukai. Hal ini akan membantu Anda mengingat sintaks dan ide 😏.

Diambil

Dengan kueri status yang disematkan, kita dapat menghapus beberapa tanggung jawab dari JavaScript dan Peristiwa Snap, serta memindahkan penanganan ke CSS.

@container scroll-state(snapped: x) {}
@container scroll-state(snapped: y) {}
@container scroll-state(snapped: inline) {}
@container scroll-state(snapped: block) {}

Daftar sintaksis lengkap

Pengingat kecil, jika Anda melewati bagian Kueri status scroll pertama, penampung untuk kueri snap adalah elemen dengan scroll-snap-align di dalamnya, dan elemen yang dapat beradaptasi harus merupakan turunan dari elemen tersebut. Artinya, ada tiga elemen yang diperlukan untuk menyiapkannya:

a scroll container with `scroll-snap-type`
⤷ a snap target with both `scroll-snap-align` and `container-type: scroll-state`
    ⤷ a child of the snap target that can query the container for snap state

Meningkatkan item yang diambil secara visual

Hal ini sangat umum dengan penggeser yang dijepret di tengah untuk menyoroti atau menampilkan item yang dijepret di tengah. Dalam contoh testimonial ini, kata kunci not digunakan sehingga semua testimonial yang tidak di-snap memiliki opasitas rendah, sedangkan testimonial yang di-snap berada dalam status presentasi alaminya.

https://codepen.io/web-dot-dev/pen/NPKMdBX
.demo {
  overflow: auto hidden;
  scroll-snap-type: x mandatory;

  > article {
    container-type: scroll-state;
    scroll-snap-align: center;

    @supports (container-type: scroll-state) {
      > * {
        transition: opacity .5s ease;

        @container not scroll-state(snapped: x) {
          opacity: .25;
        }
      }
    }
  }
}

Menampilkan teks untuk item yang diambil

Ini adalah contoh yang baik tentang cara kueri status scroll mengaktifkan animasi yang dipicu scroll. Ini juga merupakan contoh yang baik tentang kapan mengikuti gerakan yang dikurangi akan bernilai dalam CSS.

https://codepen.io/web-dot-dev/pen/XJrqpBG
.demo {
  overflow-x: auto;
  scroll-behavior-x: contain;
  scroll-snap-type: x mandatory;

  > .card {
    container-type: scroll-state;
    scroll-snap-align: center;

    @supports (container-type: scroll-state) {
      @media (prefers-reduced-motion: no-preference) {
        figcaption {
          transform: translateY(100%);

          @container scroll-state(snapped: x) {
            transform: translateY(0);
          }
        }
      }
    }
  }
}

Menganimasikan elemen slide

Animasi elemen slide show atau presentasi saat memberikan presentasi adalah hal yang umum. Dulu, menulis observer interseksi untuk hal ini cukup menjengkelkan, yang hanya menetapkan class pada slide. Sekarang kita tidak memerlukan JavaScript.

https://codepen.io/web-dot-dev/pen/dPbeNqY
html {
  scroll-snap-type: y mandatory;
}

section {
  container-type: scroll-state;
  scroll-snap-align: start;
  scroll-snap-stop: always;

  @supports (container-type: scroll-state) {
    @media (prefers-reduced-motion: no-preference) {
      > h1 {
        transition: opacity .5s ease, transform .5s var(--ease-spring-3);
        transition-delay: .5s;
        opacity: 0;
        transform: scale(1.25);

        @container scroll-state(snapped: block) {
          opacity: 1;
          transform: scale(1);
        }
      }
    }
  }
}

Anda mungkin melihat bahwa semua kueri status CSS yang diambil berperilaku seperti scrollsnapchanging, bukan scrollsnapchange. Hal ini memberi Anda hook paling awal untuk memberikan masukan visual tentang elemen yang diambil. Jika terlalu cepat, pertimbangkan peristiwa JavaScript.

Dapat di-scroll

Kueri status yang dapat di-scroll akan sangat membantu dalam menampilkan kemampuan visual saat area scroll benar-benar dapat di-scroll. Sebelum kueri status scroll, informasi ini sulit diketahui.

@container scroll-state(scrollable: top) {}
@container scroll-state(scrollable: right) {}
@container scroll-state(scrollable: bottom) {}
@container scroll-state(scrollable: left) {}

Daftar sintaksis lengkap

Menunjukkan scroll dengan bayangan

Ada trik CSS terkenal dari Lea Verou yang menggunakan background-attachment: local untuk mendapatkan efek yang mirip dengan ini, serta cara melakukannya dengan animasi berbasis scroll. Setiap teknik memiliki konsekuensi. Kita harus mempelajari kapan dan di mana setiap teknik ini paling sesuai.

Contoh berikut menggunakan satu elemen melekat yang membentang di scrollport. Gradien di bagian atas dan gradien di bagian bawah memiliki opasitas yang dianimasikan dengan @property saat kueri status scroll kontekstual diterapkan: @container scroll-state(scrollable: top).

Perhatikan juga bahwa ini adalah penampung pertama yang merupakan penampung size dan scroll-state.

https://codepen.io/web-dot-dev/pen/OPLZWBj
.scroll-container {
  container-type: scroll-state size;
  overflow: auto;

  &::after {
    content: " ";

    background: var(--_shadow-top), var(--_shadow-bottom);
    transition: 
      --_scroll-shadow-color-1-opacity .5s ease,
      --_scroll-shadow-color-2-opacity .5s ease;

    @container scroll-state(scrollable: top) {
      --_scroll-shadow-color-1-opacity: var(--_shadow-color-opacity, 25%);
    }

    @container scroll-state(scrollable: bottom) {
      --_scroll-shadow-color-2-opacity: var(--_shadow-color-opacity, 25%);
    }
  }
}

Perintah panah

Terkadang, menampilkan panah dapat membantu pengguna mengetahui bahwa area dapat di-scroll. Ini cenderung mengarah ke arah yang dapat di-scroll, dan menghilang setelah tidak diperlukan lagi. Anda dapat melakukannya dengan kode berikut.

https://codepen.io/web-dot-dev/pen/OPLZWBj
@container scroll-state((scrollable: top) or (not (scrollable: bottom))) {
  translate: 0 calc(100% + 10px);
}

@container scroll-state((scrollable: top) and (not (scrollable: bottom))) {
  translate: 0 calc(100% + 10px);
  rotate: .5turn;
}

Kembali ke atas

Interaksi status scroll populer lainnya adalah tombol praktis "scroll ke atas". Kode berikut menyebabkan tombol scroll ke atas menghilang saat tidak ada tempat untuk men-scroll ke atas.

Solusi ini sedikit terbalik, tetapi memungkinkan Anda mengurangi jumlah CSS. Tempat alami tombol berada dalam tampilan, jadi Anda perlu memberi tahu tombol untuk disembunyikan jika tidak ada tempat untuk men-scroll ke atas lagi.

https://codepen.io/web-dot-dev/pen/OPLZWBj
@container not scroll-state(scrollable: top) {
  translate: 0 calc(100% + 10px);
}

Studi lanjutan

Jika Anda mencari informasi selengkapnya, berikut beberapa referensi yang mencakup berbagai hal, mulai dari detail spesifikasi hingga artikel menarik lainnya yang membahas topik ini: