scroll-state() của CSS

Giống như truy vấn vùng chứa; nhưng dành cho các truy vấn bị kẹt, bị chụp nhanh và bị tràn.

Ngày xuất bản: 15 tháng 1 năm 2025

Chrome 133 xây dựng dựa trên các truy vấn vùng chứa bằng cách giới thiệu các truy vấn vùng chứa trạng thái cuộn. Giờ đây, bạn có thể truy vấn và điều chỉnh trạng thái do trình duyệt quản lý cho vị trí cố định, điểm chụp nhanh cuộn và các phần tử có thể cuộn từ CSS.

Tổng quan

Trước khi truy vấn trạng thái cuộn, bạn cần sử dụng JavaScript để biết liệu một phần tử có bị kẹt, bị chụp nhanh hay có thể cuộn hay không. Giờ đây, có một phương thức hiệu quả hơn trên kênh tiêu chuẩn để biết thông tin này và điều chỉnh cho phù hợp. Ngoài ra, còn có một cách mới để kích hoạt ảnh động, đó là mở khoá ảnh động được kích hoạt bằng thao tác cuộn từ CSS.

Dưới đây là thông tin tổng quan về các truy vấn trạng thái có trong Chrome 133:

Trạng thái bị treo:
Kích hoạt thay đổi kiểu khi một phần tử bị dính vào cạnh.
Trạng thái chụp nhanh:
Kích hoạt thay đổi kiểu khi một phần tử được chụp nhanh trên một trục.
Trạng thái có thể cuộn:
Thay đổi kiểu điều kiện kích hoạt khi một phần tử bị tràn.

Tin vui là mọi kiến thức bạn đã học được từ truy vấn vùng chứa sẽ giúp bạn xử lý các truy vấn trạng thái cuộn.

Ngoài ra, còn có một vùng chưa được khám phá giữa ảnh động do cuộn và truy vấn vùng chứa trạng thái cuộn; chúng ta cần thử nghiệm với thời gian và ngữ cảnh để khám phá xem ảnh động do cuộn hay ảnh động trạng thái cuộn được kích hoạt bằng cuộn sẽ là tốt nhất. Video và bản minh hoạ sau đây minh hoạ tình huống khó xử; ảnh động được kích hoạt cố định so với ảnh động được điều khiển bằng cuộn.

(bên trái) ảnh động được kích hoạt bằng scroll-state(), (bên phải) ảnh động do cuộn điều khiển
https://codepen.io/web-dot-dev/pen/emOrBaV

Truy vấn trạng thái cuộn đầu tiên

Bước đầu tiên là xác định vùng chứa, sử dụng một giá trị mới cho thuộc tính container-type. Giống như truy vấn vùng chứa, phần tử bạn muốn truy vấn là phần tử bạn cung cấp cho container-type và tuỳ ý là container-name. Với các truy vấn trạng thái cuộn, bạn sẽ cung cấp phần tử đang chụp nhanh, bị kẹt hoặc tràn container-type: scroll-state.

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

Bước thứ hai là chọn phần tử con của vùng chứa đó sẽ phản hồi trạng thái, vì với các truy vấn vùng chứa, đây không thể là cùng một phần tử có container-type.

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

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

Bước thứ ba là dùng thử. Ví dụ CSS sau đây sẽ tạo kiểu nền màu đỏ khi phần tử .stuck-top dính vào đầu trang ở 0. Với một vài dòng bổ sung vào CSS mà chúng ta đã viết và một phần tử chứa bổ sung đại diện cho trạng thái trình duyệt, các thành phần của chúng ta sẽ thông minh hơn nhiều về môi trường xung quanh.

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

Cải tiến tăng dần

Quy tắc tại và lồng @supports có thể cho phép bạn thêm tính năng cải tiến dần dần hoặc sử dụng tính năng có điều kiện chỉ bằng một vài dòng mã bổ sung:

.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;
      }
    }
  }
}

Ngoài ra, hãy nhớ sử dụng @media (prefers-reduced-motion: no-preference) {} xung quanh chuyển động của bạn, nếu bạn kết thúc việc tạo ảnh động cho các phần tử xung quanh trang bằng các truy vấn trạng thái cuộn.

Trường hợp sử dụng

Bị kẹt

Có lẽ phần này nên được gọi là "các tình huống khó xử"? Đây là một bộ sưu tập nhỏ các trường hợp sử dụng trạng thái cố định, cùng với một phần thưởng về các ý tưởng cần được xây dựng.

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

Danh sách cú pháp đầy đủ

Thêm bóng khi bị kẹt

Một trong những trường hợp sử dụng phổ biến nhất của truy vấn bị treo là đối với các thanh điều hướng muốn thêm box-shadow khi bị treo, vì vậy, các thanh này có thể xuất hiện nổi trên nội dung mà chúng phủ lên.

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);
    }
  }
}

Kích hoạt tiêu đề bị kẹt hiện tại

Một trường hợp phản hồi giao diện người dùng cố định phổ biến khác là làm nổi bật phần tử hiện đang bị kẹt. Trong danh sách các ban nhạc được sắp xếp theo thứ tự bảng chữ cái, tính năng này có thể rất hữu ích và hỗ trợ trải nghiệm.

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;
      }
    }
  }
}

Dưới đây là một biến thể khác, trong đó tiêu đề nằm ở bên cạnh các mục trong danh sách. Có rất nhiều khả năng!

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

Tràn ý tưởng

Dưới đây là danh sách các bản minh hoạ cố định có thể truyền cảm hứng cho bạn thêm một chút gia vị vào bản minh hoạ hoặc xoá JavaScript của chúng bằng các truy vấn trạng thái cuộn. Bạn nên thử tạo một ứng dụng mà bạn thích, điều này sẽ giúp bạn ghi nhớ cú pháp và ý tưởng 😏.

Snapped

Với các truy vấn trạng thái chụp nhanh, chúng ta có thể xoá một số trách nhiệm khỏi JavaScript và Sự kiện chụp nhanh, đồng thời chuyển việc xử lý sang CSS.

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

Danh sách cú pháp đầy đủ

Xin nhắc lại một chút, trong trường hợp bạn đã bỏ qua phần Truy vấn trạng thái cuộn đầu tiên, vùng chứa cho truy vấn chụp nhanh là phần tử có scroll-snap-align trên đó và phần tử có thể thích ứng phải là phần tử con của phần tử đó. Điều này có nghĩa là bạn cần có 3 phần tử để thiết lập:

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

Tăng cường hình ảnh cho mục được chụp nhanh

Thông thường, thanh cuộn được chụp ở giữa sẽ làm nổi bật hoặc giới thiệu mục được chụp ở giữa. Trong ví dụ về lời chứng thực này, từ khoá not được sử dụng để tất cả lời chứng thực chưa được chụp nhanh đều có độ mờ thấp, trong khi lời chứng thực đã chụp nhanh sẽ ở trạng thái hiển thị tự nhiên.

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;
        }
      }
    }
  }
}

Hiển thị chú thích cho mục được chụp nhanh

Đây là một ví dụ điển hình về cách truy vấn trạng thái cuộn cho phép ảnh động được kích hoạt bằng thao tác cuộn. Đây cũng là một ví dụ điển hình về thời điểm cần tuân thủ tính năng giảm chuyển động trong 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);
          }
        }
      }
    }
  }
}

Tạo ảnh động trong các phần tử của trang trình bày

Việc tạo ảnh động cho các thành phần của bản trình bày hoặc bản trình chiếu là một việc rất phổ biến khi diễn thuyết. Trước đây, việc viết trình quan sát giao lộ cho việc này khá phiền phức, tất cả những gì nó làm là đặt một lớp trên trang trình bày. Giờ đây, chúng ta không cần JavaScript nữa.

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);
        }
      }
    }
  }
}

Bạn có thể nhận thấy rằng tất cả các truy vấn trạng thái CSS được chụp nhanh đều hoạt động như scrollsnapchanging, thay vì scrollsnapchange. Điều này giúp bạn có được hook sớm nhất có thể để cung cấp phản hồi trực quan về phần tử được chụp nhanh. Nếu quá vội vàng, hãy cân nhắc sử dụng sự kiện JavaScript.

Có thể cuộn

Truy vấn trạng thái cuộn sẽ rất hữu ích trong việc hiển thị các tính năng trực quan cho thời điểm một vùng cuộn thực sự có thể cuộn được. Trước khi có truy vấn trạng thái cuộn, đây là thông tin khó biết.

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

Danh sách cú pháp đầy đủ

Cho biết thao tác cuộn bằng bóng

Có một mẹo CSS nổi tiếng của Lea Verou sử dụng background-attachment: local để đạt được hiệu ứng tương tự như thế này, cũng như cách thực hiện bằng ảnh động do cuộn điều khiển. Mỗi kỹ thuật đều có những điểm đánh đổi, chúng ta cần tìm hiểu thời điểm và vị trí phù hợp nhất để áp dụng từng kỹ thuật.

Ví dụ sau đây sử dụng một phần tử cố định duy nhất trải dài trên cuộn. Độ mờ của hiệu ứng chuyển màu ở trên cùng và hiệu ứng chuyển màu ở dưới cùng được tạo ảnh động bằng @property khi truy vấn trạng thái cuộn theo ngữ cảnh được áp dụng: @container scroll-state(scrollable: top).

Ngoài ra, hãy lưu ý rằng đây là vùng chứa đầu tiên vừa là vùng chứa size vừa là vùng chứa 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%);
    }
  }
}

Câu lệnh mũi tên

Đôi khi, việc hiển thị mũi tên có thể giúp người dùng phát hiện ra một khu vực có thể cuộn. Các mũi tên này thường chỉ hướng cuộn và biến mất khi không còn cần thiết nữa. Bạn có thể làm việc đó bằng mã sau.

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;
}

Quay lại đầu trang

Một cách tương tác phổ biến khác với trạng thái cuộn là nút tiện lợi "cuộn lên trên cùng". Mã sau đây sẽ khiến nút cuộn lên trên cùng biến mất khi không có nơi nào để cuộn lên.

Giải pháp này hơi ngược lại nhưng cho phép bạn giảm lượng CSS. Vị trí tự nhiên của nút nằm trong khung hiển thị, vì vậy, bạn cần yêu cầu nút ẩn khi không còn nơi nào để cuộn lên nữa.

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

Tiếp tục nghiên cứu

Nếu bạn muốn tìm hiểu thêm, sau đây là một số tài nguyên, từ thông tin chi tiết về thông số kỹ thuật đến các bài viết hữu ích khác về chủ đề này: