Lồng ghép CSS

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

Adam Argyle
Adam Argyle

Trước khi lồng nhau, bạn cần khai báo rõ ràng cho mọi bộ chọn, 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 viết bài 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à các quy tắc kiểu có liên quan đến bộ chọn đó có thể được nhóm trong đó.

Sau
.nesting {
  color: hotpink;

  > .is {
    color: rebeccapurple;

    > .awesome {
      color: deeppink;
    }
  }
}

Thử cách này trong trình duyệt.

Tính năng lồng nhau giúp nhà phát triển giảm bớt nhu cầu lặp lại bộ chọn, đồng thời xác định vị trí đồng thời của các quy tắc kiểu cho các phần tử có liên quan. Mã 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 kiếm các tệp cho các thực thể bộ chọn có liên quan.

Việc lồng nhau 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 nhau trên Chrome 112 cũng như thử trong Bản dùng thử kỹ thuật Safari 162.

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

Trong suốt phần còn lại của bài đăng này,hộp cát minh hoạ sau đây đượ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, bạn sẽ không chọn mục nào 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 cú pháp đó hoạt động như thế nào.

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

Bên trong hộp cát là hình tròn, hình tam giác và hình vuông. Có kích thước nhỏ, trung bình hoặc lớn. Một số 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. Sau đây là bản xem trước các phần tử HTML mà bạn sẽ nhắm mục tiêu.

<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. Tức là bộ chọn .child lồng nhau sẽ chỉ áp dụng cho các phần tử là con của các phần tử có lớp .parent.

Ngoài ra, bạn có thể viết ví dụ này bằng cách sử dụng ký hiệu & để cho biết rõ vị trí nên đặ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 lựa chọn sẽ rõ ràng hơn khi bạn tìm hiểu thêm nhiều ví dụ nâng cao trong bài viết này.

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

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

Nếu 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ả là, 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 được:

Lưới hình dạng nhiều màu sắc không còn có vòng tròn,
    chúng sẽ rất mờ trong nền.
Dùng thử bản minh hoạ

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

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

Nếu không lồng ghép, CSS hiện nay 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, 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ả là chỉ còn phần tử .circle bên trong .demo:

Lưới nhiều màu sắc của các hình dạng 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 nhìn thấy được.
Dùng thử bản minh hoạ

Chọn hình 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.

Nếu 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, 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ả là tất cả hình tam giác và hình tròn lớn 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à cách lồng

Biểu tượng & là bạn bè của bạn vì nó cho biết 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 phần tử 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; các bộ chọn con cháu.

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

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

Nếu không lồng ghép, CSS hiện nay:

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

Khi lồng ghép, 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 nhiều màu sắc hiện là đơ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 nhờ &

Giả sử bạn muốn nhắm mục tiêu .demo bằng bộ chọn :not(). Bạn bắt buộc phải & để:

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

Hàm 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 hoạt động tương tác :hover.

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

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

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

Thông số kỹ thuật CSS để lồng 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ì trong đó có rất nhiều ví dụ hợp lệ và không hợp lệ.

Một vài ví dụ tiếp theo 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 những chức năng mà tính năng này giới thiệu.

Lồng @media

Bạn có thể rất mất tập trung khi di chuyển đến một 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 nghe nhìn sửa đổi bộ chọn và kiểu của bộ chọn đó. 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 bằng &, đồng thời nhắm mục tiêu thẻ .large để minh hoạ các tính năng lồng nhau khác 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ả ví dụ cho đến thời điểm này đều tiếp tục hoặc đượ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 */
  }
}

Biểu tượng & đại diện cho tham chiếu đến đối tượng bộ chọn (không phải chuỗi) và có thể được đặt ở bất kỳ vị trí nào trong bộ chọn lồng nhau. Bạn thậm chí có thể đặt lệnh 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 sẽ có những trường hợp bạn có thể sử dụng lại ngữ cảnh của bộ chọn để tiện cho việc sử dụng.

Ví dụ lồng ghép không hợp lệ

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

Cách lồng và nối

Nhiều quy ước đặt tên lớp CSS tính đến việc lồng ghép để có thể nối hoặc nối các bộ chọn như thể chúng là chuỗi. Điều này không có tác dụng trong việc lồng CSS vì bộ chọn không phải là chuỗi mà là các 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 thông số kỹ thuật.

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

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 với một danh sách bộ chọn và sau đó tiếp tục lồng nhau. Các ví dụ trước chỉ kết thúc với 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ể phức tạp về việc lồng bên trong danh sách bộ chọn, đặc biệt là những danh sách chứa bộ chọn mã nhận dạng.

Để ý định lồng nhau hoạt động, mọi danh sách bộ chọn không phải là danh sách lồng trong nhất, sẽ được trình duyệt gói bằng :is(). Cách gói này duy trì việc nhóm danh sách bộ chọn trong mọi ngữ cảnh đã tạo. Tác dụng phụ của nhóm này, :is(.one, #two), là áp dụng tính cụ thể của điểm số cao nhất trong các 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ì đây không phải là nội dung được tạo ra. Tóm tắt thủ thuật; việc lồng bằng mã nhận dạng và danh sách bộ chọn có thể dẫn đến các bộ chọn có độ đặc hiệu rất cao.

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

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

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

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

Có nhiều cách để giải quyết vấn đề này. Sau đây là 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ẽ có 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 khi lồng 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 tuyệt vời để phát hiện việc lồng CSS: sử dụng tính năng lồng nhau 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ợ tính năng lồng ghép CSS hay không. Bên dưới câu hỏi đó là một ô màu xanh lục, thể hiện sự ủng hộ.

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 là 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, chúng tôi chỉ hỗ trợ việc lồng ghép trong Công cụ cho nhà phát triển ở mức 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 việc theo dõi cách lồng 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 để làm cho điều này rõ ràng và minh bạch.

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

Chrome 113 dự định bổ sung tính năng hỗ trợ cho việc lồng ghép CSS. Hãy chú ý theo dõi nhé.

Tương lai

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

Tính năng lồng ghép là một điểm cải tiến lớn cho ngôn ngữ CSS. Công cụ này có tác động lớn đến việc viết ra 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 tìm hiểu và hiểu rõ trước khi có thể chỉ định phiên bản 2 một cách hiệu quả.

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

Một 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à một hình ảnh theo phong cách Cyber punk.