Bắt đầu với Truy vấn kiểu

Khả năng truy vấn kích thước nội tuyến của thành phần mẹ và các giá trị đơn vị truy vấn vùng chứa gần đây đã được hỗ trợ ổn định trong tất cả công cụ trình duyệt hiện đại.

Hỗ trợ trình duyệt

  • 105
  • 105
  • 110
  • 16

Nguồn

Tuy nhiên, thông số kỹ thuật chứa không chỉ bao gồm các truy vấn kích thước, mà còn cho phép truy vấn các giá trị kiểu của thành phần mẹ. Trên Chromium 111, bạn có thể áp dụng vùng chứa kiểu cho các giá trị thuộc tính tuỳ chỉnh và truy vấn phần tử mẹ cho giá trị của một thuộc tính tuỳ chỉnh.

Hỗ trợ trình duyệt

  • 111
  • 111
  • x
  • x

Nguồn

Tức là chúng tôi có thể kiểm soát các kiểu trong CSS một cách hợp lý hơn, đồng thời cho phép tách biệt lớp logic và lớp dữ liệu của ứng dụng khỏi các kiểu của nó một cách hiệu quả hơn.

Thông số kỹ thuật Mô-đun vùng chứa CSS cấp 3, bao gồm các truy vấn kích thước và kiểu, cho phép truy vấn bất kỳ kiểu nào từ thành phần mẹ, bao gồm cả các cặp thuộc tính và giá trị như font-weight: 800. Tuy nhiên, trong quá trình triển khai tính năng này, truy vấn kiểu hiện chỉ hoạt động với các giá trị thuộc tính tùy chỉnh CSS. Việc này vẫn rất hữu ích cho việc kết hợp các kiểu và tách dữ liệu khỏi thiết kế. Hãy cùng tìm hiểu cách bạn sử dụng truy vấn kiểu với các thuộc tính tuỳ chỉnh CSS:

Bắt đầu sử dụng truy vấn kiểu

Giả sử chúng ta có HTML sau:

<ul class="card-list">
  <li class="card-container">
    <div class="card">
      ...
    </div>
  </li>
</ul>

Để sử dụng các truy vấn kiểu, trước tiên, bạn phải thiết lập một phần tử vùng chứa. Điều này yêu cầu một phương pháp tiếp cận hơi khác, tuỳ thuộc vào việc bạn đang truy vấn nguồn gốc trực tiếp hay gián tiếp.

Truy vấn cha mẹ trực tiếp

Sơ đồ của một truy vấn kiểu.

Không giống như truy vấn kiểu, bạn không cần áp dụng vùng chứa bằng cách sử dụng thuộc tính container-type hoặc container cho .card-container để .card có thể truy vấn các kiểu của thành phần mẹ trực tiếp. Tuy nhiên, chúng ta cần áp dụng kiểu (giá trị thuộc tính tuỳ chỉnh trong trường hợp này) cho vùng chứa (trong trường hợp này là .card-container) hoặc bất kỳ phần tử nào chứa phần tử mà chúng ta đang tạo kiểu trong DOM. Chúng ta không thể áp dụng các kiểu đang truy vấn trên thành phần trực tiếp mà chúng ta đang tạo kiểu bằng truy vấn đó vì điều này có thể gây ra vòng lặp vô hạn.

Để trực tiếp truy vấn cha mẹ, bạn có thể viết:

/* styling .card based on the value of --theme on .card-container */
@container style(--theme: warm) {
  .card {
    background-color: wheat;
    border-color: brown; 
    ...
  }
}

Bạn có thể nhận thấy truy vấn kiểu gói truy vấn bằng style(). Thao tác này dùng để phân biệt giá trị kích thước với kiểu. Ví dụ: bạn có thể viết một truy vấn cho chiều rộng của vùng chứa dưới dạng @container (min-width: 200px) { … }. Việc này sẽ áp dụng kiểu nếu vùng chứa gốc rộng ít nhất 200px. Tuy nhiên, min-width cũng có thể là một thuộc tính CSS và bạn có thể truy vấn giá trị CSS của min-width bằng cách sử dụng các truy vấn kiểu. Do đó, bạn nên sử dụng trình bao bọc style() để làm rõ sự khác biệt: @container style(min-width: 200px) { … }.

Tạo kiểu cho thành phần mẹ không trực tiếp

Nếu muốn truy vấn kiểu cho bất kỳ phần tử nào không phải là thành phần mẹ trực tiếp, bạn cần cung cấp container-name cho phần tử đó. Ví dụ: chúng ta có thể áp dụng kiểu cho .card dựa trên kiểu .card-list bằng cách cung cấp container-name cho .card-list và tham chiếu kiểu này trong truy vấn kiểu.

/* styling .card based on the value of --moreGlobalVar on .card-list */
@container cards style(--moreGlobalVar: value) {
  .card {
    ...
  }
}

Nhìn chung, phương pháp hay nhất là đặt tên cho vùng chứa để giúp bạn hiểu rõ những gì bạn đang truy vấn và giúp bạn có thể truy cập các vùng chứa đó dễ dàng hơn. Một ví dụ về lợi ích của việc này là nếu bạn muốn tạo kiểu trực tiếp cho các phần tử trong .card. Nếu không có vùng chứa được đặt tên trên .card-container, người dùng sẽ không thể truy vấn trực tiếp vùng chứa đó.

Nhưng trên thực tế, tất cả những điều này sẽ có ý nghĩa hơn rất nhiều. Hãy cùng tham khảo một số ví dụ sau:

Truy vấn kiểu trong thực tế

Hình ảnh minh hoạ có nhiều thẻ sản phẩm, một số thẻ có thẻ &quot;mới&quot; hoặc &quot;số lượng ít&quot; và thẻ &quot;số lượng hàng ít&quot; trên nền màu đỏ.

Truy vấn kiểu đặc biệt hữu ích khi bạn có thành phần có thể sử dụng lại với nhiều biến thể, hoặc khi bạn không có quyền kiểm soát tất cả các kiểu của mình nhưng cần áp dụng các thay đổi trong một số trường hợp nhất định. Ví dụ này cho thấy một bộ thẻ sản phẩm có cùng thành phần thẻ. Một số thẻ sản phẩm có thông tin chi tiết/ghi chú bổ sung như "Mới" hoặc "Số lượng có ít", do một thuộc tính tuỳ chỉnh có tên là --detail kích hoạt. Ngoài ra, nếu sản phẩm thuộc hàng "Còn hàng thấp" thì sản phẩm đó sẽ có nền đường viền màu đỏ đậm. Loại thông tin này có thể do máy chủ hiển thị và có thể áp dụng cho các thẻ thông qua các kiểu cùng dòng như sau:

 <div class="product-list">
  <div class="product-card-container" style="--detail: new">
    <div class="product-card">
      <div class="media">
        <img .../>
      <div class="comment-block"></div>
    </div>
  </div>
  <div class="meta">
    ...
  </div>
  </div>
  <div class="product-card-container" style="--detail: low-stock">
    ...
  </div>
  <div class="product-card-container">
    ...
  </div>
  ...
</div>

Với dữ liệu có cấu trúc này, bạn có thể chuyển các giá trị đến --detail và sử dụng thuộc tính tuỳ chỉnh CSS này để áp dụng các kiểu:

@container style(--detail: new) {
  .comment-block {
    display: block;
  }
  
  .comment-block::after {
    content: 'New';
    border: 1px solid currentColor;
    background: white;
    ...
  }
}

@container style(--detail: low-stock) {
  .comment-block {
    display: block;
  }
  
  .comment-block::after {
    content: 'Low Stock';
    border: 1px solid currentColor;
    background: white;
    ...
  }
  
  .media-img {
    border: 2px solid brickred;
  }
}

Đoạn mã ở trên cho phép chúng ta áp dụng một khối cho --detail: low-stock--detail: new, nhưng bạn có thể nhận thấy một số phần dư thừa trong khối mã. Hiện tại, không có cách nào để truy vấn riêng sự hiện diện của --detail với @container style(--detail). Điều này cho phép chia sẻ các kiểu hiệu quả hơn và giảm tình trạng lặp lại. Tính năng này hiện đang được thảo luận trong nhóm làm việc.

Thẻ thời tiết

Ví dụ trước sử dụng một thuộc tính tuỳ chỉnh duy nhất với nhiều giá trị có thể có để áp dụng kiểu. Tuy nhiên, bạn có thể kết hợp việc này bằng cách sử dụng và truy vấn nhiều thuộc tính tuỳ chỉnh. Hãy xem ví dụ về thẻ thời tiết sau:

Bản minh hoạ thẻ thời tiết.

Để tạo kiểu cho độ dốc và biểu tượng nền cho các thẻ này, hãy tìm đặc điểm thời tiết, chẳng hạn như “nhiều mây”, “mưa” hoặc “nắng”:

@container style(--sunny: true) {
  .weather-card {
    background: linear-gradient(-30deg, yellow, orange);
  }
  
  .weather-card:after {
    content: url(<data-uri-for-demo-brevity>);
    background: gold;
  }
}

Bằng cách này, bạn có thể tạo kiểu cho từng thẻ dựa trên các đặc điểm riêng biệt của thẻ. Tuy nhiên, bạn cũng có thể tạo kiểu cho các kiểu kết hợp đặc trưng (thuộc tính tuỳ chỉnh), bằng cách sử dụng trình kết hợp and theo cách tương tự như đối với truy vấn nội dung đa phương tiện. Ví dụ: một ngày có cả mây và nắng sẽ trông như sau:

@container style(--sunny: true) and style(--cloudy: true) {
    .weather-card {
      background: linear-gradient(24deg, pink, violet);
    }
  
  .weather-card:after {
      content: url(<data-uri-for-demo-brevity>);
      background: violet;
  }
}

Tách dữ liệu khỏi thiết kế

Trong cả hai bản minh hoạ này, có lợi ích về mặt cấu trúc khi tách lớp dữ liệu (DOM sẽ hiển thị trên trang) khỏi các kiểu được áp dụng. Kiểu được viết dưới dạng biến thể có thể tồn tại trong kiểu thành phần, trong khi điểm cuối có thể gửi dữ liệu mà sau đó sử dụng để tạo kiểu cho thành phần. Bạn có thể sử dụng một giá trị duy nhất (chẳng hạn như trong trường hợp đầu tiên), cập nhật giá trị --detail hoặc nhiều biến, chẳng hạn như trong trường hợp thứ hai (đặt --rainy, --cloudy hoặc --sunny. Và điều tuyệt vời nhất là bạn cũng có thể kết hợp các giá trị này, kiểm tra cả --sunny--cloudy có thể cho thấy kiểu mây rải rác.

Quá trình cập nhật giá trị thuộc tính tuỳ chỉnh thông qua JavaScript có thể diễn ra liền mạch, trong khi thiết lập mô hình DOM (tức là trong khi tạo thành phần trong khung) hoặc cập nhật bất cứ lúc nào bằng cách sử dụng <parentElem>.style.setProperty('--myProperty’, <value>). I

Dưới đây là bản minh hoạ mà trong một vài dòng mã, cập nhật --theme của một nút và áp dụng các kiểu bằng truy vấn kiểu và thuộc tính tuỳ chỉnh đó (--theme):

Tạo kiểu cho thẻ bằng truy vấn kiểu, JavaScript dùng để cập nhật giá trị thuộc tính tuỳ chỉnh là:

const themePicker = document.querySelector('#theme-picker')
const btnParent = document.querySelector('.btn-section');

themePicker.addEventListener('input', (e) => {
  btnParent.style.setProperty('--theme', e.target.value);
})

Các tính năng được nêu chi tiết trong bài viết này chỉ là phần khởi đầu. Bạn có thể kỳ vọng nhiều hơn từ các truy vấn vùng chứa để xây dựng giao diện động, phản hồi nhanh. Đối với các truy vấn kiểu cụ thể, vẫn còn một số vấn đề chưa được xử lý. Một là triển khai truy vấn kiểu cho kiểu CSS ngoài thuộc tính tuỳ chỉnh. Việc này đã là một phần của cấp thông số kỹ thuật hiện tại, nhưng chưa được triển khai trong bất kỳ trình duyệt nào. Theo dự kiến, hoạt động đánh giá ngữ cảnh boolean sẽ được thêm vào cấp thông số kỹ thuật hiện tại khi vấn đề còn tồn đọng được giải quyết, đồng thời lên kế hoạch truy vấn dải ô cho cấp thông số tiếp theo.