Các lớp phân tầng sắp hiển thị trên trình duyệt của bạn

Lớp phủ tầng (quy tắc CSS @layer) sẽ có trên Chromium 99, Firefox 97 và Safari 15.4 Beta. Chúng cho phép kiểm soát rõ ràng hơn các tệp CSS của bạn để ngăn ngừa xung đột về đặc điểm về kiểu. Điều này đặc biệt hữu ích đối với những cơ sở mã lớn, hệ thống thiết kế và khi quản lý kiểu của bên thứ ba trong ứng dụng.

Việc phân lớp CSS một cách rõ ràng sẽ ngăn ghi đè kiểu không mong muốn và thúc đẩy cấu trúc CSS tốt hơn.

Đặc tính CSS và phân tầng

Tính đặc trưng của CSS là cách CSS quyết định kiểu áp dụng cho phần tử nào. Các bộ chọn khác nhau mà bạn có thể sử dụng sẽ xác định tính cụ thể của bất kỳ quy tắc kiểu nào. Ví dụ: các phần tử sẽ ít cụ thể hơn so với lớp hoặc thuộc tính, và điều này cũng ít cụ thể hơn mã nhận dạng. Đây là một phần yếu tố của quá trình học CSS.

Mọi người thường sử dụng các quy ước đặt tên của CSS như BEM để tránh vô tình ghi đè tính đặc trưng. Bằng cách đặt cho mọi đối tượng một tên lớp duy nhất, mọi thứ sẽ được đặt trên cùng một tầng cụ thể. Tuy nhiên, không phải lúc nào bạn cũng có thể duy trì các kiểu có tổ chức như vậy, đặc biệt là khi làm việc với mã và hệ thống thiết kế của bên thứ ba.

Hình ảnh BEM của một thẻ với các lớp
Ví dụ minh hoạ về cách đặt tên BEM trên keepuptodate.com.

Mục đích của lớp phủ tầng là giải quyết vấn đề này. Họ giới thiệu một lớp mới vào tháp CSS. Với kiểu phân lớp, mức độ ưu tiên của lớp luôn tốt hơn tính đặc trưng của bộ chọn.

Ví dụ: bộ chọn .post a.link có tính đặc hiệu cao hơn .card a. Nếu cố gắng tạo kiểu cho một đường liên kết, thì bên trong một thẻ, bạn sẽ thấy bộ chọn cụ thể hơn sẽ được áp dụng trong bài đăng.

Bằng cách sử dụng @layer, bạn có thể hiểu rõ hơn về đặc trưng của kiểu của từng kiểu và đảm bảo rằng kiểu của đường liên kết thẻ ghi đè kiểu của đường liên kết bài đăng, mặc dù mức độ cụ thể có thể thấp hơn về mặt số nếu tất cả CSS của bạn nằm trên cùng một mặt phẳng. Điều này là do mức độ ưu tiên phân tầng. Kiểu phân lớp tạo ra "máy bay" tầng mới.

Hình minh hoạ từ bản minh hoạ của dự án về giao diện người dùng phân tách

@layer trong thực tế

Bản minh hoạ thể hiện màu đường liên kết khi nhập
Xem bản minh hoạ về Codepen.

Ví dụ này cho thấy sức mạnh của các lớp theo tầng, bằng cách sử dụng @layer. Có một số đường liên kết xuất hiện: một số đường liên kết không được áp dụng thêm tên lớp, một đường liên kết có lớp .link và một đường liên kết có lớp .pink. Sau đó, CSS thêm ba lớp: base, typographyutilities như sau:

@layer base {
  a {
    font-weight: 800;
    color: red; /* ignored */
  }

  .link {
    color: blue; /* ignored */
  }
}

@layer typography {
  a {
    color: green; /* styles *all* links */
  }
}

@layer utilities {
  .pink {
    color: hotpink;  /* styles *all* .pink's */
  }
}

Cuối cùng, tất cả các đường liên kết đều có màu xanh lục hoặc màu hồng. Điều này là do: mặc dù .link có tính đặc trưng ở cấp bộ chọn cao hơn a, nhưng có một kiểu màu trên a@layer có mức độ ưu tiên cao hơn. a { color: green } ghi đè .link { color: blue } khi quy tắc màu xanh lục nằm trong một lớp sau quy tắc màu xanh dương.

Mức độ ưu tiên của lớp được đánh giá cao hơn tính đặc trưng của phần tử.

Sắp xếp lớp

Bạn có thể sắp xếp các lớp ngay trên trang, như trình bày ở trên hoặc bạn có thể sắp xếp chúng ở đầu tệp.

Thứ tự lớp được thiết lập vào lần đầu tiên mỗi tên lớp xuất hiện trong mã của bạn.

Điều đó có nghĩa là nếu bạn thêm phần sau vào đầu tệp, thì tất cả các đường liên kết sẽ có màu đỏ và đường liên kết có lớp .link sẽ có màu xanh dương:

@layer utilities, typography, base;

Điều này là do thứ tự lớp hiện bị đảo ngược, đặt tiện ích lên trước và cơ sở cuối cùng. Do đó, các quy tắc kiểu trong lớp base sẽ luôn có tính cụ thể cao hơn các quy tắc kiểu trong lớp kiểu chữ. Chúng sẽ không còn là đường liên kết màu xanh lục nữa, mà thay vào đó là màu đỏ hoặc màu xanh dương.

Ảnh chụp màn hình dự án Codepen
Xem bản minh hoạ về Codepen.

Sắp xếp quá trình nhập

Một cách khác để sử dụng @layer là nhập tệp. Bạn có thể thực hiện việc này trực tiếp khi nhập kiểu bằng cách sử dụng hàm layer() như trong ví dụ sau:

/* Base */
@import '../styles/base/normalize.css' layer(base); /* normalize or rest file */
@import '../styles/base/base.css' layer(base); /* body and base styles */
@import '../styles/base/theme.css' layer(theme); /* theme variables */
@import '../styles/base/typography.css' layer(theme); /* theme typography */
@import '../styles/base/utilities.css' layer(utilities); /* base utilities */

/* Layouts */
@import '../styles/components/post.css' layer(layouts); /* post layout */

/* Components */
@import '../styles/components/cards.css' layer(components); /* imports card */
@import '../styles/components/footer.css' layer(components); /* footer component */

Đoạn mã trên có ba lớp: base,layoutscomponents. Các tệp chuẩn hoá, giao diện và kiểu chữ trong base, với một tệp post trong layouts, còn cardsfooter đều nằm trong components. Khi nhập tệp, các lớp sẽ được tạo thực thể bằng cách sử dụng hàm lớp. Một phương pháp thay thế là sắp xếp các lớp ở đầu tệp, khai báo các lớp đó trước khi nhập:

@layer base,
       theme,
       layouts,
       components,
       utilities;

Bây giờ, thứ tự @import các kiểu của bạn sẽ không quan trọng đối với thứ tự lớp, vì thứ tự này đã được thiết lập ở lần xuất hiện đầu tiên của tên lớp. Bạn không phải lo lắng về điều đó nữa. Bạn vẫn có thể đặt các tệp đã nhập vào các lớp cụ thể, nhưng thứ tự đã được thiết lập.

Ảnh chụp màn hình từ dự án Codepen
Khám phá dự án trên Codepen.

Lớp và thác

Hãy lùi lại một chút và xem nơi các lớp được sử dụng vì nó có liên quan đến tầng rộng hơn:

Hình minh hoạ Cascade

Thứ tự ưu tiên như sau:

  • Tác nhân người dùng thông thường (mức độ ưu tiên thấp nhất)
  • Người dùng cục bộ @layer
  • Người dùng cục bộ thông thường
  • Tác giả @layers
  • Tác giả bình thường
  • Tác giả !quan trọng
  • Tác giả @layer !important
  • Người dùng địa phương !important
  • Tác nhân người dùng !important** (mức độ ưu tiên cao nhất)

Bạn có thể nhận thấy ở đây các kiểu @layer !important bị đảo ngược. Thay vì ít cụ thể hơn so với kiểu không theo lớp (thông thường), chúng có mức độ ưu tiên cao hơn. Điều này là do cách !important hoạt động trong tầng: nó phá vỡ phân tầng thông thường trong biểu định kiểu của bạn và đảo ngược tính đặc hiệu cấp lớp thông thường (ưu tiên).

Lớp lồng ghép

Các lớp cũng có thể được lồng vào bên trong các lớp khác. Ví dụ sau đây được lấy từ giải thích về Lớp phủ cascade của Miriam Suzanne:

@layer default {
  p { max-width: 70ch; }
}

@layer framework {
  @layer default {
    p { margin-block: 0.75em; }
  }

  p { margin-bottom: 1em; }
}

Trong đoạn mã trên, bạn có thể truy cập framework.default, sử dụng . làm biểu tượng của lớp default được lồng trong framework. Bạn cũng có thể viết nội dung này ở định dạng viết tắt hơn:

@layer framework.default {
  p { margin-block: 0.75em }
}

Các lớp và thứ tự lớp kết quả là:

  • mặc định
  • framework.default
  • framework không có lớp
  • không phân lớp

Những điều cần lưu ý

Lớp phủ tầng có thể rất tuyệt vời nếu bạn sử dụng chúng đúng cách nhưng chúng cũng có thể tạo thêm sự nhầm lẫn và kết quả không mong muốn. Hãy chú ý những điều sau khi làm việc với các lớp phân tầng:

Quy tắc 1: Không sử dụng @layer để xác định phạm vi

Các lớp phân tầng không giải quyết phạm vi. Nếu bạn có tệp CSS có @layer, chẳng hạn như card.css và muốn tạo kiểu cho tất cả các đường liên kết trong thẻ, đừng viết các kiểu như:

a {
  …
}

Điều này sẽ dẫn đến việc tất cả các thẻ a trong tệp của bạn đều bị ghi đè. Điều quan trọng vẫn là phải xác định phạm vi cho kiểu của bạn đúng cách:

.card a {
  …
}

Quy tắc 2: các lớp theo tầng được sắp xếp sau CSS không theo lớp

Điều quan trọng bạn cần lưu ý là tệp CSS có lớp sẽ không ghi đè tệp CSS không theo lớp. Đây là một quyết định có chủ ý để giúp bạn dễ dàng giới thiệu các lớp theo cách hợp lý hơn để làm việc với cơ sở mã hiện có của mình. Ví dụ: việc sử dụng tệp reset.css là điểm xuất phát và trường hợp sử dụng phù hợp cho các lớp phân tầng.

Quy tắc 3: !important đảo ngược tính đặc hiệu của tầng

Mặc dù kiểu phân lớp nhìn chung ít cụ thể hơn so với kiểu không phân lớp, nhưng việc sử dụng !important sẽ đảo ngược điều này. Trong một lớp, các nội dung khai báo có quy tắc !important sẽ cụ thể hơn so với kiểu không có lớp.

Trong trường hợp đó, kiểu !important sẽ đảo ngược đặc điểm của chúng. Sơ đồ trên cho thấy điều này để bạn tham khảo: Author @layers có mức độ ưu tiên thấp hơn bình thường của tác giả và có mức ưu tiên thấp hơntác giả !important (có mức độ ưu tiên thấp hơn tác giả @layer !important).

Nếu bạn có nhiều lớp, thì lớp đầu tiên với !important sẽ được ưu tiên !important và là kiểu cụ thể nhất.

Quy tắc 4: Hiểu điểm chèn

Vì thứ tự lớp được thiết lập vào lần đầu tiên mỗi tên lớp xuất hiện trong mã của bạn, nên nếu bạn đặt phần khai báo @layer sau khi nhập và đặt giá trị của layer() hoặc sau một câu lệnh @layer khác, thì lớp này có thể bị bỏ qua. Không giống như trong CSS, nơi quy tắc kiểu ở dưới cùng trên trang được áp dụng cho các lớp tầng, thứ tự được thiết lập ngay từ lần xuất hiện đầu tiên.

Nó có thể ở trong một danh sách, trong một khối lớp hoặc trong một tệp nhập. Nếu bạn đặt @layer sau một danh sách nhập bằng layer(), thì thao tác này sẽ không có ảnh hưởng gì. Đặt lớp này lên đầu tệp sẽ thiết lập thứ tự lớp và giúp bạn nhìn thấy rõ các lớp trong cấu trúc.

Quy tắc số 5: Chú ý đến tính cụ thể

Với các lớp phân tầng, bộ chọn ít cụ thể hơn (như a) sẽ ghi đè bộ chọn cụ thể hơn (như .link) nếu bộ chọn ít cụ thể hơn nằm trên một lớp cụ thể hơn. Hãy cân nhắc thực hiện những bước sau:

a trong layer(components) sẽ ghi đè .pink trong layer(utilities) nếu: @layer utilities, components được chỉ định. Mặc dù đây là một phần có chủ ý của API, nhưng điều này có thể gây nhầm lẫn và khó chịu nếu bạn không mong đợi.

Vì vậy, nếu đang viết các lớp tiện ích, hãy luôn thêm chúng dưới dạng lớp bậc cao hơn so với các thành phần bạn định ghi đè. Bạn có thể nghĩ rằng "Tôi vừa thêm lớp .pink này để thay đổi màu sắc và lớp này chưa được áp dụng".

Tìm hiểu thêm về lớp thác nước

Bạn cũng có thể tham khảo các tài nguyên sau để tìm hiểu thêm về lớp phân tầng: