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 xếp chồng (quy tắc CSS @layer) sẽ có trong Chromium 99, Firefox 97 và Safari 15.4 Beta. Các lớp này cho phép bạn kiểm soát rõ ràng hơn các tệp CSS để ngăn chặn xung đột về tính chất cụ thể của kiểu. Điều này đặc biệt hữu ích cho các cơ sở mã lớn, hệ thống thiết kế và khi quản lý các 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 chặn việc ghi đè kiểu không mong muốn và thúc đẩy cấu trúc CSS tốt hơn.

Tính cụ thể của CSS và hiệu ứng lũ lụt

Độ cụ thể của CSS là cách CSS quyết định áp dụng kiểu nào 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 mức độ cụ thể của bất kỳ quy tắc kiểu nào. Ví dụ: phần tử ít cụ thể hơn lớp hoặc thuộc tính, và lớp hoặc thuộc tính lại ít cụ thể hơn mã nhận dạng. Đây là một phần cơ bản trong quá trình học CSS.

Mọi người chuyển sang các quy ước đặt tên CSS như BEM để tránh vô tình ghi đè tính cụ thể. Bằng cách đặt mọi thứ thành một tên lớp duy nhất, mọi thứ sẽ được đặt trên cùng một mặt phẳ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 sắp xếp 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ẻ có các lớp
Ví dụ minh hoạ về cách đặt tên BEM từ keepinguptodate.com.

Lớp xếp chồng nhằm giải quyết vấn đề này. Các lớp này giới thiệu một lớp mới vào loạt CSS. Với các kiểu phân lớp, mức độ ưu tiên của lớp luôn cao hơn mức độ cụ thể của bộ chọn.

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

Bằng cách sử dụng @layer, bạn có thể nêu rõ hơn về mức độ cụ thể của từng kiểu và đảm bảo rằng kiểu của đường liên kết thẻ sẽ 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 thứ tự ưu tiên của thác nước. Kiểu phân lớp tạo ra các "mặt phẳng" dạng thác nước mới.

Hình minh hoạ trong bản minh hoạ dự án về việc tách giao diện người dùng

@layer trong thực tế

Bản minh hoạ cho thấy màu của đường liên kết với các lệnh nhập
Xem bản minh hoạ trên Codepen.

Ví dụ này cho thấy sức mạnh của các lớp xếp chồng, sử dụng @layer. Có một số đường liên kết hiển thị: một số không áp dụng tên lớp bổ sung, một số có lớp .link và một số có lớp .pink. Sau đó, CSS sẽ thêm 3 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 hồng. Lý do là: mặc dù .link có mức độ cụ thể ở cấp bộ chọn cao hơn a, nhưng có một kiểu màu trên a trong @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 sẽ vượt qua mức độ cụ thể của phần tử.

Sắp xếp các lớp

Bạn có thể sắp xếp các lớp ngay trên trang, như minh hoạ ở trên, hoặc sắp xếp các lớp ở đầu tệp.

Thứ tự lớp được thiết lập theo 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 nội dung sau vào đầu tệp, 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 đã đảo ngược, đặt các tiện ích trước và cơ sở sau cùng. Do đó, các quy tắc kiểu trong lớp base sẽ luôn có mức độ cụ thể cao hơn so với các quy tắc kiểu trong lớp kiểu chữ. Các đường liên kết này sẽ không còn là đường liên kết màu xanh lục nữa mà sẽ 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ạ trên Codepen.

Sắp xếp các lệnh nhập

Một cách khác để sử dụng @layer là với các tệp nhập. Bạn có thể thực hiện việc này ngay 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ó 3 lớp: base,layoutscomponents. Các tệp chuẩn hoá, giao diện và kiểu chữ trong base, với tệp post trong layouts, và cardsfooter đều trong components. Khi nhập tệp, các lớp được tạo bản sao bằ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;

Giờ đây, thứ tự bạn @import các kiểu sẽ không quan trọng đối với thứ tự lớp, vì thứ tự này đã được thiết lập ở thực thể đầu tiên của tên lớp. Vậy là bạn đã bớt lo lắng về một vấn đề. Bạn vẫn có thể đặt các tệp đã nhập thành 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 nước

Hãy lùi lại một bước và xem các lớp được sử dụng ở đâu khi liên quan đến thác nước rộng hơn:

Hình minh hoạ về hiệu ứng xếp chồng

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ả thông thường
  • Author !important
  • Tác giả @layer !important
  • Người dùng cục bộ !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 rằng các kiểu @layer !important được đảo ngược. Thay vì ít cụ thể hơn các kiểu không phân lớp (bình thường), các kiểu này có mức độ ưu tiên cao hơn. Điều này là do cách !important hoạt động trong trình tự: lớp này phá vỡ trình tự thông thường trong các tệp kiểu và đảo ngược mức độ cụ thể (tính ưu tiên) thông thường ở cấp lớp.

Lớp lồng nhau

Các lớp cũng có thể được lồng trong các lớp khác. Ví dụ sau đây được lấy từ phần Giải thích về Lớp xếp chồng 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 vào framework.default bằng cách sử dụng . làm chỉ báo của lớp default được lồng trong framework. Bạn cũng có thể viết mã 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 thu được là:

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

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

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

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

Lớp xếp chồng không giải quyết được vấn đề về phạm vi. Nếu bạn có một tệp CSS có @layer, giả sử là 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 kiểu như:

a {
  
}

Thao tác này sẽ khiến tất cả thẻ a trong tệp của bạn được ghi đè. Bạn vẫn cần xác định phạm vi cho các kiểu một cách chính xác:

.card a {
  
}

Quy tắc 2: các lớp xếp chồng được sắp xếp phía sau CSS không phân lớp

Điều quan trọng cần lưu ý là tệp CSS phân lớp sẽ không ghi đè CSS không phân 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ó. Ví dụ: việc sử dụng tệp reset.css là một điểm xuất phát và trường hợp sử dụng phù hợp cho các lớp xếp chồng.

Quy tắc 3: !important đảo ngược mức độ cụ thể của thác nước

Mặc dù các kiểu phân lớp nói chung ít cụ thể hơn các 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 phần khai báo có quy tắc !important cụ thể hơn so với các kiểu không phân lớp.

Trong trường hợp đó, các kiểu !important sẽ đảo ngược tính cụ thể 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 author normal, author normal có mức độ ưu tiên thấp hơn author !important và author !important có mức độ ưu tiên thấp hơn author @layer !important.

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

Quy tắc 4: Tìm hiểu các điểm chèn

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

Đây có thể là trong danh sách, trong khối lớp hoặc trong tệp nhập. Nếu bạn đặt @layer sau danh sách nhập bằng layer(), thì thao tác này sẽ không làm gì cả. Việc đặt tệp này ở đầu tệp sẽ giúp thiết lập thứ tự lớp và giúp bạ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 xếp 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ể đó 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 lường trước được.

Vì vậy, nếu bạn đang viết các lớp tiện ích, hãy luôn đưa các lớp đó vào dưới dạng một lớp thứ tự cao hơn các thành phần mà bạn dự đị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 nhưng lớp này không được áp dụng".

Tìm hiểu thêm về lớp xếp chồng

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 xếp chồng: