Lồng ghép CSS

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

Adam Argyle
Adam Argyle

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

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

.nesting > .is {
  color: rebeccapurple;
}

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

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

Sau
.nesting {
  color: hotpink;

  > .is {
    color: rebeccapurple;

    > .awesome {
      color: deeppink;
    }
  }
}

Hãy thử cách này trong trình duyệt.

Tính năng lồng ghép giúp các nhà phát triển giảm nhu cầu lặp lại bộ chọn, đồng thời xác định vị trí của các quy tắc định 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 mục tiêu. Nếu thành phần .nesting trong ví dụ trước đã bị xoá khỏi dự án, thì bạn có thể xoá toàn bộ nhóm thay vì tìm các tệp cho các thực thể bộ chọn có liên quan.

Tính năng lồng ghép có thể giúp: – Tổ chức – Giảm kích thước tệp – Tái cấu trúc

Bạn có thể sử dụng tính năng lồng ghép trong Chrome 112 và dùng thử trong Safari Engineer Preview 162.

Bắt đầu sử dụng CSS Nesting

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 trực quan hoá 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 thứ đề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ể thực hành cú pháp và xem chúng trong thực tế.

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

Bên trong hộp cát là các hình tròn, hình tam giác và hình vuông. Một số kích thước nhỏ, trung bình hoặc lớn. Các màu khác có màu xanh dương, hồng hoặc tím. Tất cả đều nằm trong phần tử chứa .demo. Dưới đâ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 ghép

Lồng ghép 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.

Ngoài ra, ví dụ này có thể được viết bằng ký hiệu & để biểu thị rõ vị trí đặt lớp cha.

.parent {
  color: blue;

  & .child {
    color: red;
  }
}

Hai ví dụ này có chức năng tương đương nhau và lý do bạn có các lựa chọn sẽ rõ ràng hơn khi chúng ta sẽ khám phá các ví dụ nâng cao hơn trong bài viết này.

Chọn vòng kết nối

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

Không lồng ghép, CSS hiện nay:

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

Khi lồng ghép, 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ả phần tử bên trong .demo có lớp .circle đều bị làm mờ và gần như không xuất hiện:

Các hình dạng lưới đầy màu sắc không còn là các vòng tròn nữa, chúng rất mờ trên nền.
Dùng thử bản minh hoạ

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

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

Khi không lồng ghép, 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);
}

Khi lồng ghép, dưới đâ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ó 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 các hình tròn, tất cả các hình dạng khác gần như không xuất hiện.
Dùng thử bản minh hoạ

Chọn các 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 ghép, 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);
}

Khi lồng ghép, dưới đâ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ả các tam giác và hình tròn lớn đều bị ẩn bên trong .demo:

Lưới nhiều 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 nâng cao với bộ chọn phức hợp và lồng ghép

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ù là cách lồng ghép hợp lệ, nhưng kết quả sẽ không khớp với các yếu 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 được kết hợp với nhau, kết quả thực tế sẽ là .lg .triangle, .lg .circle; bộ chọn con.

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

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

Không lồng ghép, CSS hiện nay:

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

Khi lồng ghép, dưới đâ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 không phải màu hồng sẽ bị ẩn bên trong .demo:

Lưới nhiều màu sắc nay trở thành đơn sắc, chỉ hiển thị các hình màu hồng.
Dùng thử bản minh hoạ
Độ chính xác và linh hoạt với &

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

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

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

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

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

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

Thông số kỹ thuật của CSS để lồng ghép 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ụ, thì bài viết này có nhiều ví dụ hợp lệ và không hợp lệ.

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

@media lồng ghép

Việc di chuyển sang khu vực khác của biểu định kiểu để tìm các đ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 đó đã biến mất nhờ khả năng lồng các điều kiện ngay bên trong ngữ cảnh.

Để thuận tiện về cú pháp, nếu truy vấn đa phương tiện lồng nhau chỉ sửa đổi 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 mục tiêu thẻ .large để minh hoạ các tính năng lồng nhau khác vẫn tiếp tục hoạt động.

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

Lồng ghép vào vị trí bất kỳ

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

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

Ký hiệu & biểu thị sự tham chiếu đến một đối tượng bộ chọn (không phải là một chuỗi) và có thể được đặt ở bất kỳ vị trí nào trong một bộ chọn lồng nhau. Thậm chí, bạn có thể đặt đoạn mã này 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 trong một số trường hợp, việc có thể lặp lại ngữ cảnh của bộ chọn sẽ rất hữu ích.

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

Có một vài trường hợp cú pháp lồng không hợp lệ và có thể khiến bạn ngạc nhiên nếu đang lồng trong các bộ tiền xử lý.

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 nối các bộ chọn như thể đó là các chuỗi. Điều này không hiệu quả trong cách lồng CSS 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 nội dung giải thích chi tiết hơn trong phần thông số kỹ thuật.

Ví dụ về cách làm lồng ghép

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

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

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

Đây là ví dụ đầu tiên bắt đầu bằng danh sách bộ chọn, sau đó tiếp tục lồng ghép thêm nữa. Các ví dụ trước đó chỉ kết thúc bằng 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ó thể có chi tiết triển khai phức tạp về việc lồng bên trong danh sách bộ chọn, đặc biệt là những chi tiết bao gồm bộ chọn ID.

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

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

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

Hãy theo dõi hoặc hướng dẫn các trình tìm lỗi mã nguồn của bạn cảnh báo khi lồng ghép bên trong một danh sách bộ chọn đang sử dụng bộ chọn mã, mức độ cụ thể của tất cả việc lồng ghép trong danh sách bộ chọn đó sẽ cao.

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

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

.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 kết hợp đều được chuyển lên trên cùng, như thể các nội dung này được tạo trước khi xảy ra việc lồng nhau. Bạn có thể xem thêm thông tin chi tiết trong phần thông số kỹ thuật.

Có nhiều cách để giải quyết vấn đề này. Phần sau đây cho biết 3 kiểu màu trong &, giúp duy trì thứ tự phân tầng 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 sau lồng ghép với &.

.card {
  color: green;

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

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

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

Có 2 cách hiệu quả để làm nổi bật tính năng phát hiện việc lồng ghép CSS: sử dụng phương pháp lồng hoặc sử dụng @supports để kiểm tra khả năng phân tích cú pháp bộ chọn lồng.

Ảnh chụp màn hình bản minh hoạ Codepen của Bramus, hỏi xem trình duyệt của bạn có hỗ trợ lồng ghép CSS hay không. Bên dưới câu hỏi đó là một ô màu xanh lục, báo hiệu sự hỗ trợ.

Sử dụng cách lồng ghép:

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

  .no-nesting {
    display: none;
  }
}

Đang dùng @supports:

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

Đồng nghiệp của tôi, Bramus, có một Codepen tuyệt vời để hướng dẫn chiến lược này.

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

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

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

Chrome 113 dự định sẽ hỗ trợ thêm cho việc lồng ghép CSS. Hãy chú ý theo dõi.

Tương lai

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

Tính năng lồng ghép là một điểm cải tiến lớn cho ngôn ngữ CSS. Nó có tác động đến hầu hết mọi khía cạnh kiến trúc của CSS. Tác động lớn này cần được khám phá và hiểu sâu sắc trước khi có thể chỉ định hiệu quả phiên bản 2.

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

Một thẻ sáng trên nền xám. Thẻ 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 Cyber punk.