ฟีเจอร์หนึ่งของ CSS Preprocessor ที่เราชื่นชอบได้ถูกสร้างขึ้นในภาษาแล้ว นั่นคือการซ้อนกฎของสไตล์
ก่อนการซ้อน คุณต้องประกาศตัวเลือกทุกรายการอย่างชัดเจนแยกกัน ซึ่งนำไปสู่การทำซ้ำ ขนาดใหญ่ของสไตล์ชีต และประสบการณ์การเขียนที่กระจัดกระจาย
.nesting { color: hotpink; } .nesting > .is { color: rebeccapurple; } .nesting > .is > .awesome { color: deeppink; }
หลังการซ้อน คุณสามารถดำเนินการต่อกับตัวเลือก และจัดกลุ่มกฎของสไตล์ที่เกี่ยวข้องไว้ภายในได้
.nesting { color: hotpink; > .is { color: rebeccapurple; > .awesome { color: deeppink; } } }
การซ้อนช่วยนักพัฒนาซอฟต์แวร์โดยลดความจำเป็นในการทำซ้ำตัวเลือก ขณะเดียวกันก็จัดกฎของสไตล์สำหรับองค์ประกอบที่เกี่ยวข้องไว้ด้วยกัน นอกจากนี้ยังช่วยให้สไตล์ตรงกับ HTML ที่กำหนดเป้าหมายได้ด้วย หากนำคอมโพเนนต์ .nesting ในตัวอย่างก่อนหน้าออกจากโปรเจ็กต์ คุณสามารถลบทั้งกลุ่มแทนการค้นหาไฟล์สำหรับอินสแตนซ์ตัวเลือกที่เกี่ยวข้องได้
การซ้อนช่วยในเรื่องต่อไปนี้ได้: - การจัดระเบียบ - การลดขนาดไฟล์ - การปรับโครงสร้าง
การซ้อนพร้อมใช้งานใน Chrome 112 และยังลองใช้ได้ใน Safari Technical Preview 162
เริ่มต้นใช้งานการซ้อน CSS
ในส่วนที่เหลือของโพสต์นี้ เราจะใช้แซนด์บ็อกซ์สาธิตต่อไปนี้เพื่อช่วยให้คุณเห็นภาพการเลือก ในสถานะเริ่มต้นนี้ จะไม่มีการเลือกอะไรเลยและทุกอย่างจะมองเห็นได้ การเลือกรูปร่างและขนาดต่างๆ จะช่วยให้คุณได้ฝึกไวยากรณ์และดูการทำงานจริง

ภายในแซนด์บ็อกซ์มีวงกลม สามเหลี่ยม และสี่เหลี่ยมจัตุรัส บางรายการมีขนาดเล็ก กลาง หรือใหญ่ บางรายการมีสีน้ำเงิน ชมพู หรือม่วง ทั้งหมดอยู่ในองค์ประกอบที่มี .demo ต่อไปนี้คือตัวอย่างองค์ประกอบ HTML ที่คุณจะกำหนดเป้าหมาย
<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>
ตัวอย่างการซ้อน
การซ้อน CSS ช่วยให้คุณกำหนดสไตล์สำหรับองค์ประกอบภายในบริบทของตัวเลือกอื่นได้
.parent {
color: blue;
.child {
color: red;
}
}
ในตัวอย่างนี้ ตัวเลือกคลาส .child จะซ้อนอยู่ภายใน
ตัวเลือกคลาส .parent ซึ่งหมายความว่าตัวเลือก .child ที่ซ้อนอยู่จะใช้ได้กับองค์ประกอบที่เป็นองค์ประกอบย่อยขององค์ประกอบที่มีคลาส .parent เท่านั้น
คุณสามารถเขียนตัวอย่างนี้โดยใช้สัญลักษณ์ & เพื่อระบุตำแหน่งที่จะวางคลาสระดับบนอย่างชัดเจน
.parent {
color: blue;
& .child {
color: red;
}
}
ตัวอย่างทั้ง 2 นี้ทำงานได้เหมือนกัน และเหตุผลที่คุณมีตัวเลือกจะชัดเจนขึ้นเมื่อเราสำรวจตัวอย่างขั้นสูงเพิ่มเติมในบทความนี้
การเลือกวงกลม
สำหรับตัวอย่างแรกนี้ งานคือการเพิ่มสไตล์เพื่อจางและเบลอเฉพาะวงกลมภายในเดโม
หากไม่มีการซ้อน, CSS ในปัจจุบันจะเป็นดังนี้
.demo .circle {
opacity: .25;
filter: blur(25px);
}
หากมีการซ้อน, จะมี 2 วิธีที่ถูกต้องดังนี้
/* & is explicitly placed in front of .circle */
.demo {
& .circle {
opacity: .25;
filter: blur(25px);
}
}
หรือ
/* & + " " space is added for you */
.demo {
.circle {
opacity: .25;
filter: blur(25px);
}
}
ผลลัพธ์ คือองค์ประกอบทั้งหมดภายใน .demo ที่มีคลาส .circle จะ
เบลอและมองเห็นได้แทบไม่ชัด
การเลือกสามเหลี่ยมและสี่เหลี่ยมจัตุรัส
งานนี้ต้องเลือกองค์ประกอบที่ซ้อนกันหลายรายการ ซึ่งเรียกว่าตัวเลือกกลุ่ม
หากไม่มีการซ้อน, CSS ในปัจจุบันจะมี 2 วิธีดังนี้
.demo .triangle,
.demo .square {
opacity: .25;
filter: blur(25px);
}
หรือใช้ :is()
/* grouped with :is() */
.demo :is(.triangle, .square) {
opacity: .25;
filter: blur(25px);
}
หากมีการซ้อน, จะมี 2 วิธีที่ถูกต้องดังนี้
.demo {
& .triangle,
& .square {
opacity: .25;
filter: blur(25px);
}
}
หรือ
.demo {
.triangle, .square {
opacity: .25;
filter: blur(25px);
}
}
ผลลัพธ์ คือมีเพียงองค์ประกอบ .circle เท่านั้นที่เหลืออยู่ภายใน .demo
การเลือกสามเหลี่ยมและวงกลมขนาดใหญ่
งานนี้ต้องใช้ตัวเลือกแบบผสม ซึ่ง องค์ประกอบต้องมีทั้ง 2 คลาสจึงจะเลือกได้
หากไม่มีการซ้อน, CSS ในปัจจุบันจะเป็นดังนี้
.demo .lg.triangle,
.demo .lg.square {
opacity: .25;
filter: blur(25px);
}
หรือ
.demo .lg:is(.triangle, .circle) {
opacity: .25;
filter: blur(25px);
}
หากมีการซ้อน, จะมี 2 วิธีที่ถูกต้องดังนี้
.demo {
.lg.triangle,
.lg.circle {
opacity: .25;
filter: blur(25px);
}
}
หรือ
.demo {
.lg {
&.triangle,
&.circle {
opacity: .25;
filter: blur(25px);
}
}
}
ผลลัพธ์ คือสามเหลี่ยมและวงกลมขนาดใหญ่ทั้งหมดจะซ่อนอยู่ภายใน .demo
เคล็ดลับมือโปรเกี่ยวกับการใช้ตัวเลือกแบบผสมและการซ้อน
สัญลักษณ์ & จะเป็นตัวช่วยที่ดีในที่นี้ เนื่องจากจะแสดงวิธีรวมตัวเลือกที่ซ้อนกันอย่างชัดเจน ลองดูตัวอย่างต่อไปนี้
.demo {
.lg {
.triangle,
.circle {
opacity: .25;
filter: blur(25px);
}
}
}
แม้ว่าจะเป็นวิธีซ้อนที่ถูกต้อง แต่ผลลัพธ์จะไม่ตรงกับองค์ประกอบที่คุณคาดหวัง
เนื่องจากหากไม่มี & เพื่อระบุผลลัพธ์ที่ต้องการของ .lg.triangle,
.lg.circle ที่รวมกัน ผลลัพธ์ที่ได้จะเป็น .lg .triangle, .lg
.circle ซึ่งเป็นตัวเลือกองค์ประกอบย่อย
การเลือกรูปร่างทั้งหมด ยกเว้นรูปร่างสีชมพู
งานนี้ต้องใช้คลาสเทียมเชิงฟังก์ชันแบบปฏิเสธ ซึ่งองค์ประกอบต้องไม่มีตัวเลือกที่ระบุ
หากไม่มีการซ้อน, CSS ในปัจจุบันจะเป็นดังนี้
.demo :not(.pink) {
opacity: .25;
filter: blur(25px);
}
หากมีการซ้อน, จะมี 2 วิธีที่ถูกต้องดังนี้
.demo {
:not(.pink) {
opacity: .25;
filter: blur(25px);
}
}
หรือ
.demo {
& :not(.pink) {
opacity: .25;
filter: blur(25px);
}
}
ผลลัพธ์ คือรูปร่างทั้งหมดที่ไม่ใช่สีชมพูจะซ่อนอยู่ภายใน .demo
ความแม่นยำและความยืดหยุ่นด้วย &
สมมติว่าคุณต้องการกำหนดเป้าหมาย .demo ด้วยตัวเลือก :not() คุณต้อง ใช้ & สำหรับการดำเนินการดังกล่าว
.demo {
&:not() {
...
}
}
ซึ่งจะรวม .demo และ :not() เป็น .demo:not() ซึ่งต่างจากตัวอย่างก่อนหน้า
ที่ต้องใช้ .demo :not() การแจ้งเตือนนี้มีความสำคัญมากเมื่อต้องการซ้อนการโต้ตอบ :hover
.demo {
&:hover {
/* .demo:hover */
}
:hover {
/* .demo :hover */
}
}
ตัวอย่างการซ้อนเพิ่มเติม
ข้อกำหนด CSS สำหรับการซ้อน มี ตัวอย่างเพิ่มเติมมากมาย หากคุณต้องการเรียนรู้เพิ่มเติมเกี่ยวกับไวยากรณ์ผ่านตัวอย่าง ข้อกำหนดนี้ครอบคลุมตัวอย่างที่ถูกต้องและไม่ถูกต้องหลากหลายรายการ
ตัวอย่าง 2-3 ตัวอย่างถัดไปจะแนะนำฟีเจอร์การซ้อน CSS สั้นๆ เพื่อช่วยให้คุณเข้าใจความสามารถที่หลากหลายของฟีเจอร์นี้
การซ้อน @media
การย้ายไปยังส่วนอื่นของสไตล์ชีตเพื่อค้นหาเงื่อนไข Media Query ที่แก้ไขตัวเลือกและสไตล์อาจทำให้เสียสมาธิได้มาก แต่ความเสียสมาธินี้จะหมดไปเมื่อคุณสามารถซ้อนเงื่อนไขไว้ในบริบทได้โดยตรง
เพื่อความสะดวกในการใช้ไวยากรณ์ หาก Media Query ที่ซ้อนอยู่แก้ไขเฉพาะสไตล์สำหรับบริบทตัวเลือกปัจจุบัน คุณจะใช้ไวยากรณ์แบบย่อได้
.card {
font-size: 1rem;
@media (width >= 1024px) {
font-size: 1.25rem;
}
}
นอกจากนี้ คุณยังใช้ & อย่างชัดเจนได้ด้วย
.card {
font-size: 1rem;
@media (width >= 1024px) {
&.large {
font-size: 1.25rem;
}
}
}
ตัวอย่างนี้แสดงไวยากรณ์แบบขยายด้วย & ขณะเดียวกันก็กำหนดเป้าหมายการ์ด .large เพื่อแสดงให้เห็นว่าฟีเจอร์การซ้อนเพิ่มเติมยังคงทำงานได้
ดูข้อมูลเพิ่มเติมเกี่ยวกับการซ้อน @กฎ
การซ้อนทุกที่
ตัวอย่างทั้งหมดจนถึงจุดนี้ได้ดำเนินการต่อหรือเพิ่มบริบทก่อนหน้า คุณสามารถเปลี่ยนหรือจัดเรียงบริบทใหม่ทั้งหมดได้หากต้องการ
.card {
.featured & {
/* .featured .card */
}
}
สัญลักษณ์ & แสดงถึงการอ้างอิงไปยังออบเจ็กต์ตัวเลือก (ไม่ใช่สตริง) และสามารถวางไว้ที่ใดก็ได้ในตัวเลือกที่ซ้อนกัน หรือจะวางหลายครั้งก็ได้
.card {
.featured & & & {
/* .featured .card .card .card */
}
}
แม้ว่าตัวอย่างนี้จะดูไม่มีประโยชน์ แต่ก็มีสถานการณ์ที่การทำซ้ำบริบทตัวเลือกได้นั้นมีประโยชน์อย่างแน่นอน
ตัวอย่างการซ้อนที่ไม่ถูกต้อง
มีสถานการณ์ไวยากรณ์การซ้อนบางอย่างที่ไม่ถูกต้องและอาจทำให้คุณประหลาดใจหากคุณเคยซ้อนใน Preprocessor
การซ้อนและการเชื่อม
อนุสัญญาการตั้งชื่อคลาส CSS หลายรายการอาศัยการซ้อนเพื่อเชื่อมหรือเพิ่มตัวเลือกราวกับว่าเป็นสตริง แต่การดำเนินการนี้ใช้ไม่ได้ในการซ้อน CSS เนื่องจากตัวเลือกไม่ใช่สตริง แต่เป็นการอ้างอิงออบเจ็กต์
.card {
&--header {
/* is not equal to ".card--header" */
}
}
ดูคำอธิบายโดยละเอียดเพิ่มเติมได้ใน ข้อกำหนด
ตัวอย่างการซ้อนที่ซับซ้อน
การซ้อนภายในรายการตัวเลือกและ :is()
ลองดูบล็อก CSS การซ้อนต่อไปนี้
.one, #two {
.three {
/* some styles */
}
}
นี่เป็นตัวอย่างแรกที่เริ่มต้นด้วยรายการตัวเลือก แล้วซ้อนต่อ ตัวอย่างก่อนหน้าจบลงด้วยรายการตัวเลือกเท่านั้น ตัวอย่างการซ้อนนี้ไม่มีอะไรที่ไม่ถูกต้อง แต่มีรายละเอียดการติดตั้งใช้งานที่อาจซับซ้อนเกี่ยวกับการซ้อนภายในรายการตัวเลือก โดยเฉพาะรายการที่มีตัวเลือก ID
เพื่อให้การซ้อนทำงานได้ตามที่ต้องการ เบราว์เซอร์จะห่อหุ้มรายการตัวเลือกที่ไม่ใช่การซ้อนด้านในสุดด้วย :is() การห่อหุ้มนี้จะรักษากลุ่มของรายการตัวเลือกไว้ภายในบริบทที่เขียน ผลข้างเคียงของการจัดกลุ่มนี้ :is(.one, #two) คือการนำความเฉพาะเจาะจงของคะแนนสูงสุดภายในตัวเลือกในวงเล็บมาใช้ ซึ่งเป็นวิธีที่ :is() ทำงานเสมอ แต่คุณอาจประหลาดใจเมื่อใช้ไวยากรณ์การซ้อนเนื่องจากไม่ใช่สิ่งที่เขียนไว้เป๊ะๆ สรุปเคล็ดลับคือการซ้อนด้วย ID และรายการตัวเลือกอาจทำให้เกิดตัวเลือกที่มีความเฉพาะเจาะจงสูงมาก
เพื่อสรุปตัวอย่างที่ซับซ้อนอย่างชัดเจน บล็อกการซ้อนก่อนหน้าจะใช้กับเอกสารดังนี้
:is(.one, #two) .three {
/* some styles */
}
โปรดสังเกตหรือสอน Linters ให้แจ้งเตือนเมื่อมีการซ้อนภายในรายการตัวเลือกที่ใช้ตัวเลือก ID ความเฉพาะเจาะจงของการซ้อนทั้งหมดภายในรายการตัวเลือกนั้นจะสูง
การผสมการซ้อนและการประกาศ
ลองดูบล็อก CSS การซ้อนต่อไปนี้
.card {
color: green;
& { color: blue; }
color: red;
}
สีขององค์ประกอบ .card จะเป็น blue
ระบบจะยกการประกาศสไตล์ที่ผสมกันขึ้นไปด้านบนสุด ราวกับว่ามีการเขียนก่อนที่จะมีการซ้อน ดูรายละเอียดเพิ่มเติมได้ในข้อกำหนด
มีวิธีแก้ไขปัญหานี้ โดยการห่อหุ้มสไตล์สี 3 รายการต่อไปนี้ด้วย & ซึ่งจะรักษลําดับการเรียงซ้อนตามที่ผู้เขียนอาจตั้งใจไว้ สีขององค์ประกอบ .card จะเป็นสีแดง
.card {
color: green;
& { color: blue; }
& { color: red; }
}
ในความเป็นจริง การห่อหุ้มสไตล์ใดๆ ที่ตามมาด้วยการซ้อนด้วย & ถือเป็นแนวทางปฏิบัติแนะนำ
.card {
color: green;
@media (prefers-color-scheme: dark) {
color: lightgreen;
}
& {
aspect-ratio: 4/3;
}
}
การตรวจหาฟีเจอร์
มี 2 วิธีที่ยอดเยี่ยมในการตรวจหาฟีเจอร์การซ้อน CSS ได้แก่ การใช้การซ้อนหรือใช้ @supports เพื่อตรวจสอบความสามารถในการแยกวิเคราะห์ตัวเลือกการซ้อน

การใช้การซ้อน
html {
.has-nesting {
display: block;
}
.no-nesting {
display: none;
}
}
การใช้ @supports
@supports (selector(&)) {
/* nesting parsing available */
}
Bramus เพื่อนร่วมงานของฉันมี Codepen ที่ยอดเยี่ยม ซึ่งแสดงกลยุทธ์นี้
การดีบั๊กด้วยเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome
การรองรับการซ้อนในเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome ปัจจุบันมีน้อย ปัจจุบันคุณจะเห็นว่าสไตล์แสดงอยู่ในแผงสไตล์ตามที่คาดไว้ แต่ระบบยังไม่รองรับการติดตามการซ้อนและบริบทตัวเลือกแบบเต็ม เรามีดีไซน์และแผนที่จะทำให้การดำเนินการนี้โปร่งใสและชัดเจน

Chrome 113 วางแผนที่จะรองรับการซ้อน CSS เพิ่มเติม โปรดอดใจรอ
อนาคต
การซ้อน CSS อยู่ในเวอร์ชัน 1 เท่านั้น เวอร์ชัน 2 จะแนะนำไวยากรณ์ที่ง่ายขึ้นและอาจมีกฎที่ต้องจดจำน้อยลง มีการเรียกร้องให้การแยกวิเคราะห์การซ้อนไม่ถูกจำกัดหรือมีช่วงเวลาที่ซับซ้อน
การซ้อนเป็นการปรับปรุงที่สำคัญของภาษา CSS ซึ่งส่งผลต่อการเขียนในเกือบทุกแง่มุมทางสถาปัตยกรรมของ CSS เราต้องสำรวจและทำความเข้าใจผลกระทบที่สำคัญนี้อย่างละเอียดก่อนที่จะระบุเวอร์ชัน 2 ได้อย่างมีประสิทธิภาพ
สุดท้ายนี้ ลองดูเดโม
ที่ใช้ @scope การซ้อน และ @layer ร่วมกัน ซึ่งเป็นเรื่องที่น่าตื่นเต้นมาก
