Lồng ghép CSS

Một trong những tính năng của trình xử lý trước CSS mà chúng tôi yêu thích hiện đã được tích hợp vào ngôn ngữ: lồng ghép các quy tắc kiểu.

Adam Argyle
Adam Argyle

Trước khi lồng, bạn cần khai báo rõ ràng từng bộ chọn, tách biệt với nhau. Điều này dẫn đến việc lặp lại, tệp kiểu lớn và trải nghiệm soạn thư bị phân tán.

Trước
.nesting {
  color: hotpink;
}

.nesting > .is {
  color: rebeccapurple;
}

.nesting > .is > .awesome {
  color: deeppink;
}

Sau khi lồng nhau, bạn có thể tiếp tục sử dụng bộ chọn và nhóm các quy tắc kiểu liên quan vào bên trong.

Sau
.nesting {
  color: hotpink;

  > .is {
    color: rebeccapurple;

    > .awesome {
      color: deeppink;
    }
  }
}

Thử trong trình duyệt.

Việc lồng giúp nhà phát triển giảm nhu cầu lặp lại bộ chọn, đồng thời cùng định vị các quy tắc kiểu cho các phần tử có liên quan. Điều này cũng có thể giúp các kiểu khớp với HTML mà chúng nhắm đến. Nếu thành phần .nesting trong ví dụ trước đã bị xoá khỏi dự án, bạn có thể xoá toàn bộ nhóm thay vì tìm kiếm các tệp cho các thực thể bộ chọn có liên quan.

Việc lồng có thể giúp: - Sắp xếp - Giảm kích thước tệp - Tái cấu trúc

Tính năng lồng nhau có trong Chrome 112 và bạn cũng có thể thử trong Bản dùng thử kỹ thuật Safari 162.

Làm quen với tính năng lồng ghép CSS

Trong phần còn lại của bài đăng này,hộp cát minh hoạ sau đây sẽ được dùng để giúp bạn hình dung các lựa chọn. Ở trạng thái mặc định này, không có mục nào được chọn và mọi mục đều hiển thị. Bằng cách chọn nhiều hình dạng và kích thước, bạn có thể luyện tập cú pháp và xem cú pháp đó hoạt động như thế nào.

Một lưới nhiều màu sắc gồm các vòng tròn, tam giác và hình vuông lớn nhỏ.

Bên trong hộp cát là các hình tròn, tam giác và hình vuông. Một số có kích thước nhỏ, trung bình hoặc lớn. Các màu khác là xanh dương, hồng hoặc tím. Tất cả đều nằm bên trong phần tử chứa .demo. Sau đây là bản xem trước các phần tử HTML mà bạn sẽ nhắm đến.

<div class="demo">
  <div class="sm triangle pink"></div>
  <div class="sm triangle blue"></div>
  <div class="square blue"></div>
  <div class="sm square pink"></div>
  <div class="sm square blue"></div>
  <div class="circle pink"></div>
  …
</div>

Ví dụ về cách lồng

Tính năng lồng CSS cho phép bạn xác định kiểu cho một phần tử trong ngữ cảnh của một bộ chọn khác.

.parent {
  color: blue;

  .child {
    color: red;
  }
}

Trong ví dụ này, bộ chọn lớp .child được lồng trong bộ chọn lớp .parent. Điều này có nghĩa là bộ chọn .child lồng nhau sẽ chỉ áp dụng cho các phần tử là phần tử con của các phần tử có lớp .parent.

Bạn cũng có thể viết ví dụ này bằng ký hiệu & để biểu thị rõ ràng vị trí đặt lớp mẹ.

.parent {
  color: blue;

  & .child {
    color: red;
  }
}

Hai ví dụ này có chức năng tương đương và lý do bạn có các tuỳ chọn sẽ trở nên rõ ràng hơn khi các ví dụ nâng cao hơn được khám phá trong bài viết này.

Chọn các vòng tròn

Đối với ví dụ đầu tiên này, nhiệm vụ của bạn là thêm các kiểu để làm mờ và làm mờ chỉ các vòng tròn bên trong bản minh hoạ.

Không lồng nhau, CSS hiện nay:

.demo .circle {
  opacity: .25;
  filter: blur(25px);
}

Với cách lồng nhau, có hai cách hợp lệ:

/* & is explicitly placed in front of .circle */
.demo {
  & .circle {
    opacity: .25;
    filter: blur(25px);
  }
}

hoặc

/* & + " " space is added for you */
.demo {
  .circle {
    opacity: .25;
    filter: blur(25px);
  }
}

Kết quả, tất cả các phần tử bên trong .demo có lớp .circle đều bị làm mờ và gần như không nhìn thấy:

Lưới hình nhiều màu không còn hình tròn nữa, mà chỉ có các hình rất mờ ở nền.
Dùng thử bản minh hoạ

Chọn bất kỳ hình tam giác và hình vuông nào

Nhiệm vụ này yêu cầu bạn chọn nhiều phần tử lồng nhau, còn gọi là bộ chọn nhóm.

Không lồng nhau, CSS hiện có hai cách:

.demo .triangle,
.demo .square {
  opacity: .25;
  filter: blur(25px);
}

hoặc sử dụng :is()

/* grouped with :is() */
.demo :is(.triangle, .square) {
  opacity: .25;
  filter: blur(25px);
}

Với cách lồng nhau, sau đây là hai cách hợp lệ:

.demo {
  & .triangle,
  & .square {
    opacity: .25;
    filter: blur(25px);
  }
}

hoặc

.demo {
  .triangle, .square {
    opacity: .25;
    filter: blur(25px);
  }
}

Kết quả, chỉ các phần tử .circle còn lại bên trong .demo:

Lưới hình dạng đầy màu sắc chỉ còn lại hình tròn, tất cả hình dạng khác gần như không nhìn thấy.
Dùng thử bản minh hoạ

Chọn tam giác và hình tròn lớn

Nhiệm vụ này yêu cầu một bộ chọn phức hợp, trong đó các phần tử phải có cả hai lớp để được chọn.

Không lồng nhau, CSS hiện nay:

.demo .lg.triangle,
.demo .lg.square {
  opacity: .25;
  filter: blur(25px);
}

hoặc

.demo .lg:is(.triangle, .circle) {
  opacity: .25;
  filter: blur(25px);
}

Với cách lồng nhau, sau đây là hai cách hợp lệ:

.demo {
  .lg.triangle,
  .lg.circle {
    opacity: .25;
    filter: blur(25px);
  }
}

hoặc

.demo {
  .lg {
    &.triangle,
    &.circle {
      opacity: .25;
      filter: blur(25px);
    }
  }
}

Kết quả, tất cả hình tam giác và hình tròn lớn đều được ẩn bên trong .demo:

Lưới đầy màu sắc chỉ hiển thị các hình dạng nhỏ và trung bình.
Dùng thử bản minh hoạ
Mẹo chuyên nghiệp về bộ chọn phức hợp và lồng nhau

Biểu tượng & là bạn của bạn ở đây vì biểu tượng này cho thấy rõ cách nối các bộ chọn lồng nhau. Hãy xem ví dụ sau đây:

.demo {
  .lg {
    .triangle,
    .circle {
      opacity: .25;
      filter: blur(25px);
    }
  }
}

Mặc dù đây là cách lồng hợp lệ, nhưng kết quả sẽ không khớp với các phần tử mà bạn có thể mong đợi. Lý do là nếu không có & để chỉ định kết quả mong muốn của .lg.triangle, .lg.circle kết hợp với nhau, thì kết quả thực tế sẽ là .lg .triangle, .lg .circle; bộ chọn con cháu.

Chọn tất cả các hình dạng trừ hình màu hồng

Tác vụ này yêu cầu một lớp giả lập chức năng phủ định, trong đó các phần tử không được có bộ chọn được chỉ định.

Không lồng nhau, CSS hiện nay:

.demo :not(.pink) {
  opacity: .25;
  filter: blur(25px);
}

Với cách lồng nhau, sau đây là hai cách hợp lệ:

.demo {
  :not(.pink) {
    opacity: .25;
    filter: blur(25px);
  }
}

hoặc

.demo {
  & :not(.pink) {
    opacity: .25;
    filter: blur(25px);
  }
}

Kết quả, tất cả các hình dạng không phải màu hồng đều bị ẩn bên trong .demo:

Lưới đầy màu sắc hiện đã chuyển sang màu đơn sắc, chỉ hiển thị các hình dạng màu hồng.
Thử bản minh hoạ
Độ chính xác và tính linh hoạt với &

Giả sử bạn muốn nhắm đến .demo bằng bộ chọn :not(). & là bắt buộc đối với:

.demo {
  &:not() {
    ...
  }
}

Thao tác này kết hợp .demo:not() thành .demo:not(), trái ngược với ví dụ trước cần .demo :not(). Lời nhắc này rất quan trọng khi bạn muốn lồng một lượt tương tác :hover.

.demo {
  &:hover {
    /* .demo:hover */
  }

  :hover {
    /* .demo :hover */
  }
}

Ví dụ khác về cách lồng

Quy cách CSS để lồng nhau có nhiều ví dụ hơn. Nếu bạn muốn tìm hiểu thêm về cú pháp thông qua các ví dụ, tài liệu này sẽ trình bày nhiều ví dụ hợp lệ và không hợp lệ.

Vài ví dụ tiếp theo sẽ giới thiệu ngắn gọn về tính năng lồng CSS để giúp bạn hiểu được phạm vi chức năng mà tính năng này mang lại.

Lồng ghép @media

Việc di chuyển đến một khu vực khác của tệp kiểu để tìm điều kiện truy vấn nội dung đa phương tiện sửa đổi bộ chọn và kiểu của bộ chọn đó có thể gây mất tập trung. Sự phân tâm đó sẽ biến mất khi bạn có thể lồng các điều kiện ngay bên trong ngữ cảnh.

Để thuận tiện cho cú pháp, nếu truy vấn nội dung đa phương tiện lồng nhau chỉ sửa đổi các kiểu cho ngữ cảnh bộ chọn hiện tại, thì bạn có thể sử dụng cú pháp tối thiểu.

.card {
  font-size: 1rem;

  @media (width >= 1024px) {
    font-size: 1.25rem;
  }
}

Bạn cũng có thể sử dụng & một cách rõ ràng:

.card {
  font-size: 1rem;

  @media (width >= 1024px) {
    &.large {
      font-size: 1.25rem;
    }
  }
}

Ví dụ này cho thấy cú pháp mở rộng với &, đồng thời nhắm đến các thẻ .large để minh hoạ các tính năng lồng ghép bổ sung tiếp tục hoạt động.

Tìm hiểu thêm về cách lồng @rules.

Lồng ghép ở bất cứ đâu

Tất cả các ví dụ cho đến thời điểm này đều tiếp tục hoặc thêm vào ngữ cảnh trước đó. Bạn có thể thay đổi hoặc sắp xếp lại hoàn toàn ngữ cảnh nếu cần.

.card {
  .featured & {
    /* .featured .card */
  }
}

Ký hiệu & biểu thị tham chiếu đến đối tượng bộ chọn (không phải chuỗi) và có thể được đặt ở vị trí bất kỳ trong bộ chọn lồng nhau. Bạn thậm chí có thể đặt nhiều lần:

.card {
  .featured & & & {
    /* .featured .card .card .card */
  }
}

Mặc dù ví dụ này có vẻ hơi vô dụng, nhưng chắc chắn có những trường hợp mà việc lặp lại ngữ cảnh bộ chọn sẽ rất hữu ích.

Ví dụ về cách lồng không hợp lệ

Có một số tình huống cú pháp lồng nhau không hợp lệ và có thể khiến bạn ngạc nhiên nếu bạn đã lồng trong trình xử lý trước.

Lồng ghép và nối

Nhiều quy ước đặt tên lớp CSS dựa vào việc lồng ghép có thể nối hoặc thêm các bộ chọn như thể chúng là chuỗi. Cách này không hoạt động trong CSS lồng nhau vì các bộ chọn không phải là chuỗi mà là tham chiếu đối tượng.

.card {
  &--header {
    /* is not equal to ".card--header" */
  }
}

Bạn có thể xem thêm nội dung giải thích chi tiết trong quy cách.

Ví dụ về cách lồng ghép khó khăn

Lồng trong danh sách bộ chọn và :is()

Hãy xem xét khối CSS lồng nhau sau:

.one, #two {
  .three {
    /* some styles */
  }
}

Đây là ví dụ đầu tiên bắt đầu bằng một danh sách bộ chọn, sau đó tiếp tục lồng ghép. Các ví dụ trước chỉ kết thúc bằng một danh sách bộ chọn. Không có gì không hợp lệ trong ví dụ lồng nhau này, nhưng có một chi tiết triển khai có thể khó khăn về việc lồng nhau bên trong danh sách bộ chọn, đặc biệt là những danh sách có bộ chọn mã nhận dạng.

Để ý định lồng nhau hoạt động, trình duyệt sẽ gói bất kỳ danh sách bộ chọn nào không phải là lồng nhau nhất bên trong bằng :is(). Thao tác gói này duy trì việc nhóm danh sách bộ chọn trong bất kỳ ngữ cảnh nào do tác giả tạo. Hiệu ứng phụ của hoạt động nhóm này, :is(.one, #two), là việc áp dụng tính cụ thể của điểm số cao nhất trong các bộ chọn trong dấu ngoặc đơn. Đây là cách :is() luôn hoạt động, nhưng có thể gây ngạc nhiên khi sử dụng cú pháp lồng nhau vì đó không phải là nội dung chính xác đã được tạo. Tóm tắt mẹo: việc lồng ghép với mã nhận dạng và danh sách bộ chọn có thể dẫn đến bộ chọn có độ cụ thể rất cao.

Để tóm tắt rõ ràng ví dụ khó hiểu này, khối lồng trước đó sẽ được áp dụng cho tài liệu như sau:

:is(.one, #two) .three {
  /* some styles */
}

Hãy chú ý hoặc hướng dẫn trình tìm lỗi mã nguồn cảnh báo khi lồng bên trong danh sách bộ chọn đang sử dụng bộ chọn mã nhận dạng, độ cụ thể của tất cả các phần lồng trong danh sách bộ chọn đó sẽ rất cao.

Kết hợp việc lồng và khai báo

Hãy xem xét khối CSS lồng nhau sau:

.card {
  color: green;
  & { color: blue; }
  color: red;
}

Màu của các phần tử .card sẽ là blue.

Mọi nội dung khai báo kiểu được kết hợp sẽ được chuyển lên trên cùng, như thể chúng được tạo trước khi có bất kỳ hoạt động lồng ghép nào. Bạn có thể xem thêm thông tin chi tiết trong quy cách.

Có một số cách để giải quyết vấn đề này. Phần sau đây gói ba kiểu màu trong &, giúp duy trì thứ tự lũy tiến như tác giả có thể dự định. Màu của các phần tử .card sẽ là màu đỏ.

.card {
  color: green;
  & { color: blue; }
  & { color: red; }
}

Trên thực tế, bạn nên gói mọi kiểu theo cách lồng nhau bằng &.

.card {
  color: green;

  @media (prefers-color-scheme: dark) {
    color: lightgreen;
  }

  & {
    aspect-ratio: 4/3;
  }
}

Phát hiện tính năng

Có hai cách hay để phát hiện tính năng lồng ghép CSS: sử dụng tính năng lồng ghép hoặc sử dụng @supports để kiểm tra khả năng phân tích cú pháp bộ chọn lồng ghép.

Ảnh chụp màn hình minh hoạ Codepen của Bramus, hỏi liệu trình duyệt của bạn có hỗ trợ tính năng lồng CSS hay không. Dưới câu hỏi đó là một hộp màu xanh lục, cho biết có hỗ trợ.

Sử dụng tính năng lồng ghép:

html {
  .has-nesting {
    display: block;
  }

  .no-nesting {
    display: none;
  }
}

Sử dụng @supports:

@supports (selector(&)) {
  /* nesting parsing available */
}

Đồng nghiệp của tôi Bramus có một Codepen tuyệt vời cho thấy chiến lược này.

Gỡ lỗi bằng Công cụ của Chrome cho nhà phát triển

Hiện tại, DevTools hỗ trợ rất ít cho việc lồng nhau. Hiện tại, bạn sẽ thấy các kiểu được thể hiện trong ngăn Kiểu như dự kiến, nhưng tính năng theo dõi quá trình lồng ghép và ngữ cảnh bộ chọn đầy đủ của kiểu đó chưa được hỗ trợ. Chúng tôi có thiết kế và kế hoạch để làm rõ vấn đề này.

Ảnh chụp màn hình cú pháp lồng nhau trong Công cụ của Chrome cho nhà phát triển.

Chrome 113 dự định hỗ trợ thêm tính năng lồng CSS. Hãy chú ý theo dõi nhé.

Tương lai

Lồng CSS chỉ ở phiên bản 1. Phiên bản 2 sẽ giới thiệu nhiều cú pháp đơn giản hơn và có thể ít quy tắc hơn để ghi nhớ. Có rất nhiều yêu cầu về việc phân tích cú pháp lồng nhau để không bị giới hạn hoặc gặp phải những thời điểm khó khăn.

Lồng nhau là một điểm cải tiến lớn đối với ngôn ngữ CSS. Phương thức này có tác động đến việc tạo tác giả đối với hầu hết mọi khía cạnh cấu trúc của CSS. Bạn cần tìm hiểu và hiểu rõ tác động lớn này trước khi có thể chỉ định phiên bản 2 một cách hiệu quả.

Cuối cùng, dưới đây là một bản minh hoạ sử dụng @scope, lồng nhau và @layer cùng nhau. Mọi thứ đều rất thú vị!

Thẻ sáng trên nền xám. Thẻ này có tiêu đề và văn bản, một vài nút hành động và hình ảnh theo phong cách cyberpunk.