Áp dụng CSS có điều kiện dựa trên tính năng hỗ trợ quy tắc At-Rule

Tính năng web: at-rule()

Sử dụng @supports at-rule(@<at-rule name>) {…} để áp dụng có điều kiện một số quy tắc hoặc nội dung khai báo CSS chỉ khi trình duyệt hỗ trợ một quy tắc at-rule nhất định. Quy tắc này có thể chứa toàn bộ quy tắc hoặc được dùng kết hợp với quy tắc lồng nhau của CSS để áp dụng một số nội dung khai báo theo điều kiện.

.foo {
  background: var(--accent-color);

  @supports at-rule(@property) {
    background-image: var(--rainbow-gradient);
    animation: sliding-rainbow 1s linear infinite;
  }
}

KHÔNG sử dụng @supports at-rule() để áp dụng có điều kiện quy tắc at-rule đang được phát hiện và không áp dụng gì khác.

/* BAD: CSS already ignores rules the browser doesn't understand, so this is unnecessary */
@supports at-rule(@starting-style) {
  @starting-style {
    /* ... */
  }
}

Phát hiện tính năng hỗ trợ cho quy tắc at-rule khi @supports at-rule() không phù hợp

Khi không thể sử dụng at-rule(), do trình duyệt hỗ trợ hoặc do cần phát hiện sâu hơn, bạn có thể làm theo hướng dẫn bên dưới.

Thử thuộc tính, giá trị hoặc bộ chọn trước

@supports đã có thể phát hiện các thuộc tính, giá trị và bộ chọn (thông qua @supports selector(...)). Trong nhiều trường hợp, bạn có thể sử dụng các thuộc tính, giá trị và bộ chọn này để phát hiện khả năng hỗ trợ cho một tính năng mà không cần kiểm tra chính quy tắc-at.

Ví dụ: thay vì @supports at-rule(@function) để phát hiện khả năng hỗ trợ cho các hàm tuỳ chỉnh, bạn có thể sử dụng @supports (color: --foo()).

Trong các trường hợp khác, có thể có các lớp giả hoặc phần tử giả chỉ có sẵn khi một quy tắc at-rule nhất định được hỗ trợ, có thể được dùng để phát hiện khả năng hỗ trợ.

Phát hiện các loại truy vấn vùng chứa

at-rule() CHỈ cho phép phát hiện các quy tắc at-rule theo tên. Tính năng này KHÔNG cho phép phát hiện cú pháp hoặc chỉ số mô tả khác. Ví dụ: cách này sẽ không hiệu quả:

@supports at-rule(@container style(--a: 1)) {
  /* ... */
}

Đối với nhiều loại truy vấn vùng chứa, bạn có thể sử dụng container-type với giá trị phù hợp. Ví dụ: @supports (container-type: scroll-state) cho truy vấn trạng thái cuộn hoặc truy vấn @supports (container-type: size)container-type: anchoredforanchored()`.

Tuy nhiên, không có container-type cho truy vấn kiểu. QUAN TRỌNG: ĐỪNG thử @supports (container-type: style) để phát hiện tính năng hỗ trợ cho truy vấn kiểu, vì cách này sẽ không hoạt động.

Thay vào đó, bạn có thể thử một truy vấn kiểu luôn đúng:

@container style(--a: 1) or (not (style(--a: 1))) {
  /* Rules here will only be applied if style queries are supported */
}

Điều này sẽ khớp trong mọi trình duyệt hỗ trợ truy vấn kiểu, bất kể bạn sử dụng tên hoặc giá trị thuộc tính nào, miễn là chúng giống nhau trên cả hai.

Thuộc tính tuỳ chỉnh đã đăng ký (@property)

Trong một số trường hợp, nếu có thể giảm CSS có điều kiện cần áp dụng xuống một thuộc tính, thì bạn có thể sử dụng một thuộc tính tuỳ chỉnh chuyên dụng được đăng ký cho mục đích này:

/* Use a custom property to conditionally include the dash animation */
@property --progress-dash-animation {
  syntax: "*";
  inherits: false;
  initial-value: , progress-dash 3s ease-in-out infinite;
}

.spinner {
  /* The dash animation is only included if @property is supported */
  animation:
    progress-spin var(--_used-spinner-duration) linear infinite
    var(--progress-dash-animation, );
}

Để có giải pháp đa năng hơn, bạn có thể đăng ký một thuộc tính không kế thừa và đặt thuộc tính đó thành (dấu cách) trên phần tử gốc:

{# Testcase: https://codepen.io/leaverou/pen/KwWzpVr #}

@property --supports-atproperty {
    syntax: "*";
    inherits: false;
}

:root {
    --supports-atproperty: ;
}

body {
  /* The background is only applied if @property is supported */
    background: var(--supports-atproperty, green);
}

Lưu ý:

  • Cả hai cách này chỉ hoạt động nếu khoảng trắng là một phương án thay thế được chấp nhận. Đối với các trường hợp khác, bạn sẽ cần sử dụng JS như mô tả bên dưới.
  • Giải pháp chung phụ thuộc vào tính không kế thừa, vì vậy, giải pháp này sẽ không hoạt động đối với các nội dung khai báo trên phần tử gốc.

Sử dụng JavaScript khi không còn cách nào khác

Đối với toàn bộ quy tắc at-rule, hãy kiểm tra sự hiện diện của một số giao diện nhất định trong JavaScript. Ví dụ: nếu globalThis.CSSFunctionRule được xác định, thì @function sẽ được hỗ trợ.

Nếu cần kiểm thử sâu hơn (ví dụ: đối với một số khúc dạo đầu, quy tắc lồng nhau, chỉ số mô tả), bạn có thể thiết lập một quy trình kiểm thử và kiểm tra cách phân tích cú pháp:

const sheet = new CSSStyleSheet();
sheet.replaceSync(`
  @page {
    @top-left {
      content: "test";
    }
  }
`);
const SUPPORTS_PAGE_MARGINS = Boolean(sheet.cssRules[0]?.cssRules?.[0]);

QUAN TRỌNG:

  • Khi sử dụng JS để phát hiện khả năng hỗ trợ các tính năng CSS, KHÔNG áp dụng trực tiếp các kiểu từ JS. Thay vào đó, hãy áp dụng một lớp cho phần tử gốc và phân nhánh từ đó trong CSS.
  • Xin lưu ý rằng các truy vấn vùng chứa được phân tích cú pháp ngay cả khi không nhận dạng được phần mở đầu và không có cách nào để phát hiện khả năng hỗ trợ cho một số loại truy vấn vùng chứa nhất định từ cấu trúc CSSOM.
  • KHÔNG sử dụng CSS.registerProperty để phát hiện khả năng hỗ trợ cho @property vì khả năng hỗ trợ của thuộc tính này rộng hơn một chút.

Chiến lược dự phòng

Trừ phi tính năng này nằm trong mục tiêu hỗ trợ của bạn, hãy CHỈ sử dụng @supports (at-rule()) để phát hiện các quy tắc tại đó có phạm vi hỗ trợ hẹp hơn so với chính tính năng @supports (at-rule()).

Để áp dụng CSS có điều kiện dựa trên khả năng hỗ trợ các quy tắc at-rule được cung cấp trước tính năng này, hãy làm theo hướng dẫn trong phần trên.

Để phát hiện khả năng hỗ trợ cho chính tính năng @supports at-rule(), bạn có thể sử dụng một quy tắc at-rule được đảm bảo tồn tại trong tất cả trình duyệt hỗ trợ @supports at-rule(), chẳng hạn như @supports at-rule(@media) { ... }.