คุณสมบัติที่กำหนดเอง CSS ใน Cascade
เผยแพร่แล้ว: 2022-03-10เมื่อเดือนที่แล้ว ฉันได้สนทนาบน Twitter เกี่ยวกับความแตกต่างระหว่างสไตล์ "กำหนดขอบเขต" (สร้างขึ้นในกระบวนการสร้าง) และสไตล์ "ซ้อน" ดั้งเดิมของ CSS ฉันถามว่าทำไมโดยปกติแล้วนักพัฒนาจึงหลีกเลี่ยงความเฉพาะเจาะจงของตัวเลือก ID ในขณะที่ใช้ "รูปแบบที่กำหนดขอบเขต" ที่สร้างโดย JavaScript Keith Grant เสนอว่าความแตกต่างอยู่ที่การสร้างสมดุลระหว่างน้ำตก* และการสืบทอด กล่าวคือ ให้ความสำคัญกับความใกล้ชิดมากกว่าความจำเพาะ ลองมาดูกัน
น้ำตก
CSS cascade ขึ้นอยู่กับปัจจัยสามประการ:
- ความสำคัญที่ กำหนดโดยแฟล็ก
!important
และรูปแบบต้นทาง (ผู้ใช้ > ผู้แต่ง > เบราว์เซอร์) - ความ จำเพาะ ของตัวเลือกที่ใช้ (inline > ID > class > element)
- ลำดับต้นทาง ของรหัสเอง (ล่าสุดมีความสำคัญกว่า)
ไม่มีการกล่าวถึง ความใกล้เคียง — ความสัมพันธ์ DOM-tree ระหว่างส่วนต่างๆ ของตัวเลือก ย่อหน้าด้านล่างจะเป็นสีแดงทั้งคู่ แม้ว่า #inner p
จะอธิบายความสัมพันธ์ที่ใกล้ชิดกว่า #outer p
สำหรับย่อหน้าที่สอง:
See the Pen [Cascade: Specificity vs Proximity](https://codepen.io/smashingmag/pen/OexweJ/) โดย Miriam Suzanne
<section> <p>This text is red</p> <div> <p>This text is also red!</p> </div> </section>
#inner p { color: green; } #outer p { color: red; }
ตัวเลือกทั้งสองมีความเฉพาะเจาะจงเหมือนกัน พวกเขาทั้งสองอธิบายองค์ประกอบ p
เดียวกัน และไม่ถูกตั้งค่าสถานะเป็น !important
ดังนั้นผลลัพธ์จะขึ้นอยู่กับลำดับแหล่งที่มาเพียงอย่างเดียว
BEM และรูปแบบที่กำหนดขอบเขต
หลักการตั้งชื่ออย่าง BEM ("Block__Element—Modifier") ถูกใช้เพื่อให้แน่ใจว่าแต่ละย่อหน้า "กำหนดขอบเขต" ไว้สำหรับผู้ปกครองเพียงคนเดียวเท่านั้น เพื่อหลีกเลี่ยงน้ำตกทั้งหมด ย่อหน้า "องค์ประกอบ" ได้รับคลาสเฉพาะสำหรับบริบท "บล็อก" ของพวกเขา:
ดูปากกา [BEM Selectors & Proximity](https://codepen.io/smashingmag/pen/qzPyeM/) โดย Miriam Suzanne
<section class="outer"> <p class="outer__p">This text is red</p> <div class="inner"> <p class="inner__p">This text is green!</p> </div> </section>
.inner__p { color: green; } .outer__p { color: red; }
ตัวเลือกเหล่านี้ยังคงมีความสำคัญ ความเฉพาะเจาะจง และลำดับแหล่งที่มาเหมือนกัน — แต่ผลลัพธ์ต่างกัน เครื่องมือ CSS "กำหนดขอบเขต" หรือ "โมดูลาร์" ทำให้กระบวนการนั้นเป็นไปโดยอัตโนมัติ โดยเขียน CSS ใหม่ให้เราโดยอิงตาม HTML ในโค้ดด้านล่าง แต่ละย่อหน้ามีขอบเขตไปยังพาเรนต์โดยตรง:
ดูปากกา [Scoped Style Proximity](https://codepen.io/smashingmag/pen/NZaLWN/) โดย Miriam Suzanne
<section outer-scope> <p outer-scope>This text is red</p> <div outer-scope inner-scope> <p inner-scope>This text is green!</p> </div> </section>
p[inner-scope] { color: green } p[outer-scope] { color: red; }
มรดก
ความใกล้ชิดไม่ได้เป็นส่วนหนึ่งของน้ำตก แต่เป็นส่วนหนึ่งของ CSS นั่นคือสิ่งที่ มรดก กลายเป็นสิ่งสำคัญ ถ้าเราปล่อย p
ออกจากตัวเลือกของเรา แต่ละย่อหน้าจะได้รับสีจากบรรพบุรุษที่ใกล้ที่สุด:
See the Pen [มรดก: ความจำเพาะ vs ความใกล้ชิด](https://codepen.io/smashingmag/pen/mZBGyN/) โดย Miriam Suzanne
#inner { color: green; } #outer { color: red; }
เนื่องจาก #inner
และ #outer
อธิบายองค์ประกอบที่แตกต่างกัน div
และ section
ของเราตามลำดับ คุณสมบัติของสีทั้งสองจึงถูกนำมาใช้โดยไม่มีข้อขัดแย้ง องค์ประกอบ p
ที่ซ้อนกันไม่มีการระบุสี ดังนั้นผลลัพธ์จะถูกกำหนดโดยการ สืบทอด (สีของพาเรนต์โดยตรง) แทนที่จะเป็น cascade ความใกล้ชิดมีความสำคัญเหนือกว่า และค่า #inner
จะแทนที่ #outer
แต่มีปัญหาอยู่: ในการใช้การสืบทอด เรากำลังกำหนดสไตล์ ทุกอย่าง ภายใน section
และ div
ของเรา เราต้องการกำหนดเป้าหมายสีของย่อหน้าโดยเฉพาะ
(Re-) แนะนำคุณสมบัติที่กำหนดเอง
คุณสมบัติที่กำหนดเองเป็นโซลูชันใหม่ที่ใช้กับเบราว์เซอร์ พวกเขาสืบทอดเหมือนคุณสมบัติอื่น ๆ แต่ ไม่จำเป็นต้องใช้ในที่ที่กำหนดไว้ การใช้ CSS แบบธรรมดา โดยไม่ต้องมีหลักการตั้งชื่อหรือเครื่องมือสร้างใดๆ เราสามารถสร้างสไตล์ที่มีทั้งแบบกำหนดเป้าหมายและตามบริบท โดยมีความใกล้เคียงมีความสำคัญเหนือกว่าน้ำตก:
ดูปากกา [อุปกรณ์ประกอบฉากที่กำหนดเอง: ความจำเพาะเทียบกับความใกล้เคียง](https://codepen.io/smashingmag/pen/gNGdaO/) โดย Miriam Suzanne
p { color: var(--paragraph); } #inner { --paragraph: green; } #outer { --paragraph: red; }
คุณสมบัติ --paragraph
ที่กำหนดเองนั้นสืบทอดมาเหมือนกับคุณสมบัติ color
แต่ตอนนี้เราควบคุมได้แล้วว่าค่านั้นจะถูกนำไปใช้อย่างไรและที่ไหน คุณสมบัติ --paragraph
ทำหน้าที่คล้ายกับพารามิเตอร์ที่สามารถส่งผ่านไปยังองค์ประกอบ p
ไม่ว่าจะผ่านการเลือกโดยตรง (specificity-rules) หรือบริบท (proximity-rules)
ฉันคิดว่าสิ่งนี้เผยให้เห็นศักยภาพของคุณสมบัติแบบกำหนดเองที่เรามักเชื่อมโยงกับฟังก์ชัน มิกซ์อิน หรือส่วนประกอบ
กำหนดเอง "ฟังก์ชัน" และพารามิเตอร์
ฟังก์ชัน มิกซ์อิน และส่วนประกอบทั้งหมดมีพื้นฐานมาจากแนวคิดเดียวกัน นั่นคือ โค้ดที่ใช้ซ้ำได้ ซึ่งสามารถเรียกใช้ด้วยพารามิเตอร์อินพุตต่างๆ เพื่อให้ได้ผลลัพธ์ที่สม่ำเสมอแต่สามารถกำหนดค่าได้ ความแตกต่างอยู่ในสิ่งที่พวกเขาทำกับผลลัพธ์ เราจะเริ่มต้นด้วยตัวแปรการไล่ระดับสีแบบสไทรพ์ จากนั้นเราสามารถขยายไปยังรูปแบบอื่นๆ ได้:
html { --stripes: linear-gradient( to right, powderblue 20%, pink 20% 40%, white 40% 60%, pink 60% 80%, powderblue 80% ); }
ตัวแปรนั้นถูกกำหนดไว้ในองค์ประกอบ root html
(สามารถใช้ :root
ได้ แต่จะเพิ่มความจำเพาะที่ไม่จำเป็น) ดังนั้นตัวแปรแบบสไทรพ์ของเราจะสามารถใช้ได้ทุกที่ในเอกสาร เราสามารถใช้ได้ทุกที่ที่รองรับการไล่ระดับสี:
ดูปากกา [อุปกรณ์ประกอบฉากที่กำหนดเอง: ตัวแปร](https://codepen.io/smashingmag/pen/NZwrrm/) โดย Miriam Suzanne
body { background-image: var(--stripes); }
การเพิ่มพารามิเตอร์
มีการใช้ฟังก์ชันเหมือนตัวแปร แต่กำหนดพารามิเตอร์สำหรับเปลี่ยนเอาต์พุต เราสามารถอัปเดตตัวแปร --stripes
ให้มีลักษณะเหมือนฟังก์ชันมากขึ้นโดยกำหนดตัวแปรที่เหมือนพารามิเตอร์บางตัวอยู่ข้างใน ฉันจะเริ่มต้นด้วยการแทนที่ to right
ด้วย var(--stripes-angle)
เพื่อสร้างพารามิเตอร์เปลี่ยนมุม:
html { --stripes: linear-gradient( var(--stripes-angle), powderblue 20%, pink 20% 40%, white 40% 60%, pink 60% 80%, powderblue 80% ); }
มีพารามิเตอร์อื่น ๆ ที่เราสามารถสร้างได้ ขึ้นอยู่กับวัตถุประสงค์ของฟังก์ชันที่จะให้บริการ เราควรอนุญาตให้ผู้ใช้เลือกสีลายทางของตัวเองหรือไม่? ถ้าเป็นเช่นนั้น ฟังก์ชันของเรายอมรับพารามิเตอร์สีที่แตกต่างกัน 5 แบบหรือเพียง 3 สีที่จะเข้าออกเหมือนตอนนี้หรือไม่ เราต้องการสร้างพารามิเตอร์สำหรับการหยุดสีด้วยหรือไม่ ทุกพารามิเตอร์ที่เราเพิ่มให้การปรับแต่งเพิ่มเติมด้วยต้นทุนของความเรียบง่ายและความสม่ำเสมอ
ไม่มีคำตอบที่ถูกต้องที่เป็นสากลสำหรับเครื่องชั่งนั้น — ฟังก์ชันบางอย่างจำเป็นต้องยืดหยุ่นมากขึ้น และบางฟังก์ชันต้องมีความเห็นมากกว่า มีนามธรรมเพื่อให้มีความสอดคล้องและอ่านง่ายในโค้ดของคุณ ดังนั้นให้ถอยออกมาแล้วถามว่าเป้าหมายของคุณคืออะไร สิ่งใดจำเป็นต้องปรับแต่งได้จริง และควรบังคับใช้ความสอดคล้องที่ใด ในบางกรณี อาจมีประโยชน์มากกว่าที่จะมีฟังก์ชันที่ได้รับความเห็นชอบ 2 ฟังก์ชัน แทนที่จะเป็นฟังก์ชันที่ปรับแต่งได้ทั้งหมดเพียงฟังก์ชันเดียว
ในการใช้ฟังก์ชันข้างต้น เราจำเป็นต้องส่งค่าสำหรับพารามิเตอร์ --stripes-angle
และใช้เอาต์พุตกับคุณสมบัติเอาต์พุต CSS เช่น background-image
:
/* in addition to the code above… */ html { --stripes-angle: 75deg; background-image: var(--stripes); }
ดูปากกา [อุปกรณ์ประกอบฉากที่กำหนดเอง: ฟังก์ชั่น](https://codepen.io/smashingmag/pen/BgwOjj/) โดย Miriam Suzanne
สืบทอดกับสากล
ฉันกำหนด --stripes
ฟังก์ชั่นบนองค์ประกอบ html
ออกจากนิสัย คุณสมบัติที่กำหนดเองสืบทอดมา และฉันต้องการให้ฟังก์ชันของฉันพร้อมใช้งานทุกที่ ดังนั้นจึงควรใส่ไว้ในองค์ประกอบรูท ทำงานได้ดีสำหรับการสืบทอดตัวแปรเช่น --brand-color: blue
ดังนั้นเราอาจคาดหวังให้ตัวแปรนี้ทำงานได้ดีสำหรับ "ฟังก์ชัน" ของเราเช่นกัน แต่ถ้าเราพยายามใช้ฟังก์ชันนี้อีกครั้งในตัวเลือกที่ซ้อนกัน มันจะไม่ทำงาน:
ดูปากกา [อุปกรณ์ประกอบฉากที่กำหนดเอง: การสืบทอดฟังก์ชันล้มเหลว](https://codepen.io/smashingmag/pen/RzjRrM/) โดย Miriam Suzanne
div { --stripes-angle: 90deg; background-image: var(--stripes); }
ใหม่ --stripes-angle
จะถูกละเว้นทั้งหมด ปรากฎว่าเราไม่สามารถพึ่งพาการสืบทอดสำหรับฟังก์ชันที่ต้องคำนวณใหม่ได้ นั่นเป็นเพราะว่าค่าคุณสมบัติแต่ละค่าจะถูกคำนวณครั้งเดียวต่อองค์ประกอบ (ในกรณีของเราคือองค์ประกอบรูท html
) จากนั้น ค่าที่คำนวณได้นั้นจะถูกสืบทอด โดยการกำหนดฟังก์ชันของเราที่รูทเอกสาร เราไม่ได้ทำให้ฟังก์ชันทั้งหมดพร้อมใช้งานสำหรับผู้สืบทอด — เฉพาะผลลัพธ์ที่คำนวณจากฟังก์ชันของเราเท่านั้น
นั่นสมเหตุสมผลถ้าคุณจัดเฟรมในแง่ของพารามิเตอร์ cascading --stripes-angle
เช่นเดียวกับพร็อพเพอร์ตี้ CSS ที่สืบทอดมา พร็อพเพอร์ตี้นี้มีให้สำหรับทายาทแต่ไม่มีบรรพบุรุษ ค่าที่เราตั้งค่าใน div
ที่ซ้อนกันไม่สามารถใช้ได้กับฟังก์ชันที่เรากำหนดไว้ในบรรพบุรุษรูท html
ในการสร้างฟังก์ชันที่ใช้งานได้ในระดับสากลซึ่งจะคำนวณใหม่บนองค์ประกอบใดๆ เราต้องกำหนดมันในทุกองค์ประกอบ:
ดูปากกา [อุปกรณ์ประกอบฉากที่กำหนดเอง: ฟังก์ชันสากล](https://codepen.io/smashingmag/pen/agLaNj/) โดย Miriam Suzanne
* { --stripes: linear-gradient( var(--stripes-angle), powderblue 20%, pink 20% 40%, white 40% 60%, pink 60% 80%, powderblue 80% ); }
ตัวเลือกสากลทำให้ฟังก์ชันของเราใช้งานได้ทุกที่ แต่เราสามารถกำหนดให้แคบลงได้หากต้องการ สิ่งสำคัญคือสามารถคำนวณใหม่ได้เฉพาะเมื่อมีการกำหนดไว้อย่างชัดเจนเท่านั้น นี่คือทางเลือกบางส่วน:
/* make the function available to elements with a given selector */ .stripes { --stripes: /* etc… */; } /* make the function available to elements nested inside a given selector */ .stripes * { --stripes: /* etc… */; } /* make the function available to siblings following a given selector */ .stripes ~ * { --stripes: /* etc… */; }
ดูปากกา [อุปกรณ์ประกอบฉากที่กำหนดเอง: ฟังก์ชั่นกำหนดขอบเขต](https://codepen.io/smashingmag/pen/JQMvGM/) โดย Miriam Suzanne
สามารถขยายได้ด้วยตรรกะของตัวเลือกใดๆ ที่ไม่ต้องอาศัยการสืบทอด
พารามิเตอร์ฟรีและค่าทางเลือก
ในตัวอย่างด้านบน var(--stripes-angle)
ไม่มีค่าและไม่มีทางเลือก แตกต่างจากตัวแปร Sass หรือ JS ที่ต้องกำหนดหรือสร้างอินสแตนซ์ก่อนที่จะเรียก คุณสมบัติที่กำหนดเองของ CSS สามารถเรียกได้โดยไม่ต้องกำหนด สิ่งนี้จะสร้างตัวแปร "อิสระ" คล้ายกับพารามิเตอร์ของฟังก์ชันที่สืบทอดมาจากบริบทได้
ในที่สุด เราสามารถกำหนดตัวแปรบน html
หรือ :root
(หรือบรรพบุรุษอื่น ๆ ) เพื่อตั้งค่าที่สืบทอดมา แต่ก่อนอื่น เราต้องพิจารณาทางเลือกอื่นหากไม่มีการกำหนดค่า มีหลายทางเลือก ขึ้นอยู่กับพฤติกรรมที่เราต้องการ
- สำหรับพารามิเตอร์ "จำเป็น" เราไม่ต้องการทางเลือกอื่น ตามที่เป็นอยู่ ฟังก์ชันนี้จะไม่ทำอะไรเลยจนกว่าจะกำหนด
--stripes-angle
- สำหรับพารามิเตอร์ "ทางเลือก" เราสามารถระบุค่าทางเลือกในฟังก์ชัน
var()
หลังจากชื่อตัวแปร เราเพิ่มเครื่องหมายจุลภาค ตามด้วยค่าเริ่มต้น:
var(--stripes-angle, 90deg)
ฟังก์ชัน var()
แต่ละฟังก์ชันสามารถมีทางเลือกได้เพียงรายการเดียว ดังนั้นเครื่องหมายจุลภาคเพิ่มเติมจะเป็นส่วนหนึ่งของค่านั้น ซึ่งทำให้สามารถระบุค่าเริ่มต้นที่ซับซ้อนด้วยเครื่องหมายจุลภาคภายในได้:

html { /* Computed: Hevetica, Ariel, sans-serif */ font-family: var(--sans-family, Hevetica, Ariel, sans-serif); /* Computed: 0 -1px 0 white, 0 1px 0 black */ test-shadow: var(--shadow, 0 -1px 0 white, 0 1px 0 black); }
นอกจากนี้เรายังสามารถใช้ตัวแปรที่ซ้อนกันเพื่อสร้างกฎคาสเคดของเราเอง โดยให้ลำดับความสำคัญที่แตกต่างกันกับค่าต่างๆ:
var(--stripes-angle, var(--global-default-angle, 90deg))
- ขั้นแรก ลองใช้พารามิเตอร์ที่ชัดเจนของเรา (
--stripes-angle
); - ย้อนกลับไปสู่ "ค่าเริ่มต้นของผู้ใช้" ทั่วโลก (
--user-default-angle
) หากมีให้บริการ - สุดท้าย ใช้ทางเลือกแทน "ค่าเริ่มต้นจากโรงงาน"
(90deg
)
See the Pen [อุปกรณ์ประกอบฉากที่กำหนดเอง: ค่าทางเลือก](https://codepen.io/smashingmag/pen/jjGvVm/) โดย Miriam Suzanne
โดยการตั้งค่าทางเลือกใน var()
แทนที่จะกำหนดคุณสมบัติที่กำหนดเองอย่างชัดแจ้ง เรามั่นใจว่าไม่มีข้อกำหนดเฉพาะหรือข้อจำกัดการเรียงซ้อนในพารามิเตอร์ พารามิเตอร์ *-angle
ทั้งหมดเป็น "อิสระ" ที่จะสืบทอดจากบริบทใดๆ
ทางเลือกของเบราว์เซอร์เทียบกับทางเลือกที่เปลี่ยนแปลงได้
เมื่อเราใช้ตัวแปร มีสองเส้นทางสำรองที่เราต้องจำไว้:
- เบราว์เซอร์ที่ไม่มีการรองรับตัวแปรควรใช้ค่าใด
- เบราว์เซอร์ที่รองรับตัวแปรควรใช้ค่าใด เมื่อตัวแปรบางตัวขาดหายไปหรือไม่ถูกต้อง
p { color: blue; color: var(--paragraph); }
แม้ว่าเบราว์เซอร์รุ่นเก่าจะไม่สนใจคุณสมบัติการประกาศตัวแปร และตัวเลือกสำรองเป็น blue
— เบราว์เซอร์รุ่นใหม่จะอ่านทั้งสองอย่างและใช้อันหลัง var(--paragraph)
ของเราอาจไม่ได้กำหนดไว้ แต่ถูกต้องและจะแทนที่คุณสมบัติก่อนหน้า ดังนั้นเบราว์เซอร์ที่มีการสนับสนุนตัวแปรจะเป็นทางเลือกแทนค่าที่สืบทอดหรือค่าเริ่มต้น ราวกับว่าใช้คีย์เวิร์ดที่ไม่ได้ unset
อาจดูสับสนในตอนแรก แต่มีเหตุผลที่ดี ประการแรกคือด้านเทคนิค: กลไกของเบราว์เซอร์จัดการ ไวยากรณ์ ที่ไม่ถูกต้องหรือไม่รู้จักที่ "เวลาแยกวิเคราะห์" (ซึ่งเกิดขึ้นก่อน) แต่ตัวแปรจะไม่ได้รับการแก้ไขจนกว่าจะถึง "เวลาที่คำนวณได้" (ซึ่งจะเกิดขึ้นในภายหลัง)
- ในเวลาแยกวิเคราะห์ การประกาศที่มีไวยากรณ์ที่ไม่ถูกต้องจะถูกละเว้นโดยสมบูรณ์ — กลับไปใช้การประกาศก่อนหน้านี้ นี่คือเส้นทางที่เบราว์เซอร์เก่าจะปฏิบัติตาม เบราว์เซอร์สมัยใหม่รองรับไวยากรณ์ของตัวแปร ดังนั้นการประกาศครั้งก่อนจะถูกยกเลิกแทน
- ณ เวลาที่คำนวณค่า ตัวแปรจะถูกคอมไพล์ว่าไม่ถูกต้อง แต่สายเกินไป — การประกาศครั้งก่อนถูกยกเลิกไปแล้ว ตามข้อมูลจำเพาะ ค่า ตัวแปรที่ไม่ถูกต้องจะถือว่าเหมือนกับ
unset
:
ดูปากกา [อุปกรณ์ประกอบฉากที่กำหนดเอง: ไม่ถูกต้อง/ไม่ได้รับการสนับสนุน vs ไม่ได้กำหนด](https://codepen.io/smashingmag/pen/VJMGbJ/) โดย Miriam Suzanne
html { color: red; /* ignored as *invalid syntax* by all browsers */ /* - old browsers: red */ /* - new browsers: red */ color: not a valid color; color: var(not a valid variable name); /* ignored as *invalid syntax* by browsers without var support */ /* valid syntax, but invalid *values* in modern browsers */ /* - old browsers: red */ /* - new browsers: unset (black) */ --invalid-value: not a valid color value; color: var(--undefined-variable); color: var(--invalid-value); }
สิ่งนี้ยังดีสำหรับเราในฐานะผู้เขียน เนื่องจากช่วยให้เราสามารถเล่นกับทางเลือกที่ซับซ้อนมากขึ้นสำหรับเบราว์เซอร์ที่รองรับตัวแปร และให้ทางเลือกที่ง่ายสำหรับเบราว์เซอร์รุ่นเก่า ยิ่งไปกว่านั้น ซึ่งช่วยให้เราใช้สถานะ null
/ undefined
เพื่อตั้งค่าพารามิเตอร์ที่จำเป็น สิ่งนี้มีความสำคัญอย่างยิ่งหากเราต้องการเปลี่ยนฟังก์ชันให้เป็นมิกซ์อินหรือส่วนประกอบ
คุณสมบัติที่กำหนดเอง "มิกซ์"
ใน Sass ฟังก์ชันจะคืนค่าดิบ ในขณะที่มิกซ์อินโดยทั่วไปจะคืนค่าเอาต์พุต CSS จริงด้วยคู่ของคุณสมบัติ-ค่า เมื่อเรากำหนดคุณสมบัติ universal --stripes
โดยไม่นำไปใช้กับการแสดงผลใดๆ ผลลัพธ์จะเป็นแบบฟังก์ชัน เราสามารถทำให้พฤติกรรมนั้นเหมือนมิกซ์อินมากขึ้น โดยกำหนดผลลัพธ์ในระดับสากลเช่นกัน:
* { --stripes: linear-gradient( var(--stripes-angle), powderblue 20%, pink 20% 40%, white 40% 60%, pink 60% 80%, powderblue 80% ); background-image: var(--stripes); }
ตราบใดที่ --stripes-angle
ยังคงไม่ถูกต้องหรือไม่ถูกกำหนด มิกซ์อินจะไม่สามารถคอมไพล์ได้ และจะไม่มี background-image
หากเราตั้งค่ามุมที่ถูกต้องบนองค์ประกอบใดๆ ฟังก์ชันจะคำนวณและให้พื้นหลังแก่เรา:
div { --stripes-angle: 30deg; /* generates the background */ }
ขออภัย ค่าพารามิเตอร์ นั้นจะ สืบทอดมา ดังนั้นคำจำกัดความปัจจุบันจะสร้างพื้นหลังบน div
และรายการย่อยทั้งหมด ในการแก้ไขปัญหานั้น เราต้องตรวจสอบให้แน่ใจว่าค่า --stripes-angle
ไม่ได้รับการสืบทอด โดยวางให้เป็น initial
(หรือค่าที่ไม่ถูกต้อง) ในทุกองค์ประกอบ เราสามารถทำได้โดยใช้ตัวเลือกสากลเดียวกัน:
See the Pen [อุปกรณ์ประกอบฉากที่กำหนดเอง: Mixin](https://codepen.io/smashingmag/pen/ZdXMJx/) โดย Miriam Suzanne
* { --stripes-angle: initial; --stripes: /* etc… */; background-image: var(--stripes); }
รูปแบบอินไลน์ที่ปลอดภัย
ในบางกรณี เราจำเป็นต้องตั้งค่าพารามิเตอร์แบบไดนามิกจากภายนอก CSS โดยอิงตามข้อมูลจากเซิร์ฟเวอร์ส่วนหลังหรือเฟรมเวิร์กส่วนหน้า ด้วยคุณสมบัติที่กำหนดเอง เราสามารถกำหนดตัวแปรใน HTML ได้อย่างปลอดภัยโดยไม่ต้องกังวลเกี่ยวกับปัญหาความจำเพาะทั่วไป:
See the Pen [อุปกรณ์ประกอบฉากที่กำหนดเอง: Mixin + Inline Style](https://codepen.io/smashingmag/pen/qzPMPv/) โดย Miriam Suzanne
<div>...</div>
สไตล์อินไลน์มีความเฉพาะเจาะจงสูงและยากต่อการแทนที่ — แต่ด้วยคุณสมบัติแบบกำหนดเอง เรามีตัวเลือกอื่น: ไม่ต้องสนใจ หากเราตั้งค่า div เป็น background-image: none
(ตัวอย่าง) ตัวแปรอินไลน์นั้นจะไม่ได้รับผลกระทบ เพื่อให้ห่างไกลออกไปอีก เราสามารถสร้างตัวแปรระดับกลางได้:
* { --stripes-angle: var(--stripes-angle-dynamic, initial); }
ตอนนี้เรามีตัวเลือกในการกำหนด --stripes-angle-dynamic
ใน HTML หรือละเว้น และตั้งค่า --stripes-angle
โดยตรงในสไตล์ชีตของเรา
See the Pen [อุปกรณ์ประกอบฉากที่กำหนดเอง: Mixin + Inline / Override](https://codepen.io/smashingmag/pen/ZdXMao/) โดย Miriam Suzanne
ค่าที่ตั้งไว้ล่วงหน้า
สำหรับค่าที่ซับซ้อนมากขึ้น หรือรูปแบบทั่วไปที่เราต้องการใช้ซ้ำ เราสามารถจัดเตรียมตัวแปรที่กำหนดไว้ล่วงหน้าสองสามตัวให้เลือก:
* { --tilt-down: 6deg; --tilt-up: -6deg; }
และใช้ค่าที่ตั้งไว้ล่วงหน้าเหล่านั้น แทนที่จะตั้งค่าโดยตรง:
<div>...</div>
See the Pen [อุปกรณ์ประกอบฉากที่กำหนดเอง: Mixin + Presets](https://codepen.io/smashingmag/pen/LKemZm/) โดย Miriam Suzanne
เหมาะอย่างยิ่งสำหรับการสร้างแผนภูมิและกราฟตามข้อมูลไดนามิก หรือแม้แต่จัดวางโปรแกรมวางแผนวัน
ดู Pen [แผนภูมิแท่งในตาราง CSS + ตัวแปร](https://codepen.io/smashingmag/pen/wLrEyg/) โดย Miriam Suzanne
องค์ประกอบตามบริบท
นอกจากนี้เรายังสามารถจัดวาง "มิกซ์อิน" ของเราใหม่เป็น "ส่วนประกอบ" ได้ด้วยการใช้กับตัวเลือกที่ชัดเจน และทำให้พารามิเตอร์เป็นทางเลือก แทนที่จะพึ่งพาการมีอยู่หรือไม่มีของ --stripes-angle
เพื่อสลับเอาต์พุตของเรา เราสามารถพึ่งพาการมีอยู่หรือไม่มีของตัวเลือกส่วนประกอบได้ ที่ช่วยให้เราสามารถตั้งค่าทางเลือกได้อย่างปลอดภัย:
ดูปากกา [อุปกรณ์ประกอบฉากที่กำหนดเอง: ส่วนประกอบ](https://codepen.io/smashingmag/pen/QXqVmM/) โดย Miriam Suzanne
[data-stripes] { --stripes: linear-gradient( var(--stripes-angle, to right), powderblue 20%, pink 20% 40%, white 40% 60%, pink 60% 80%, powderblue 80% ); background-image: var(--stripes); }
โดยการวางทางเลือกในฟังก์ชัน var()
เราสามารถปล่อยให้ --stripes-angle
ไม่ได้กำหนดและ "ว่าง" เพื่อสืบทอดค่าจากภายนอกส่วนประกอบ นี่เป็นวิธีที่ยอดเยี่ยมในการเปิดเผยลักษณะบางอย่างของรูปแบบองค์ประกอบต่อการป้อนข้อมูลตามบริบท แม้แต่รูปแบบ "กำหนดขอบเขต" ที่สร้างโดยเฟรมเวิร์ก JS (หรือกำหนดขอบเขตภายใน shadow-DOM เช่นไอคอน SVG) ก็สามารถใช้วิธีนี้เพื่อแสดงพารามิเตอร์เฉพาะสำหรับอิทธิพลภายนอก
ส่วนประกอบที่แยกออกมา
หากเราไม่ต้องการเปิดเผยพารามิเตอร์สำหรับการสืบทอด เราสามารถกำหนดตัวแปรด้วยค่าเริ่มต้นได้:
[data-stripes] { --stripes-angle: to right; --stripes: linear-gradient( var(--stripes-angle, to right), powderblue 20%, pink 20% 40%, white 40% 60%, pink 60% 80%, powderblue 80% ); background-image: var(--stripes); }
ส่วนประกอบเหล่านี้จะทำงานกับคลาสหรือตัวเลือกที่ถูกต้องอื่นๆ ได้เช่นกัน แต่ฉันเลือกแอตทริบิวต์ data-
เพื่อสร้างเนมสเปซสำหรับตัวดัดแปลงที่เราต้องการ:
[data-stripes='vertical'] { --stripes-angle: to bottom; } [data-stripes='horizontal'] { --stripes-angle: to right; } [data-stripes='corners'] { --stripes-angle: to bottom right; }
ดูปากกา [อุปกรณ์ประกอบฉากที่กำหนดเอง: ส่วนประกอบที่แยกได้](https://codepen.io/smashingmag/pen/agLaGX/) โดย Miriam Suzanne
ตัวเลือกและพารามิเตอร์
ฉันมักจะหวังว่าฉันจะใช้ data-attributes เพื่อตั้งค่าตัวแปร — คุณลักษณะที่รองรับโดยข้อกำหนด CSS3 attr()
แต่ยังไม่ได้ใช้งานในเบราว์เซอร์ใด ๆ (ดูแท็บทรัพยากรสำหรับปัญหาที่เชื่อมโยงในแต่ละเบราว์เซอร์) ซึ่งจะทำให้เราสามารถเชื่อมโยงตัวเลือกกับพารามิเตอร์เฉพาะได้อย่างใกล้ชิดยิ่งขึ้น:
<div data-stripes="30deg">...</div> /* Part of the CSS3 spec, but not yet supported */ /* attr( , ) */ [data-stripes] { --stripes-angle: attr(data-stripes angle, to right); }
<div data-stripes="30deg">...</div> /* Part of the CSS3 spec, but not yet supported */ /* attr( , ) */ [data-stripes] { --stripes-angle: attr(data-stripes angle, to right); }
<div data-stripes="30deg">...</div> /* Part of the CSS3 spec, but not yet supported */ /* attr( , ) */ [data-stripes] { --stripes-angle: attr(data-stripes angle, to right); }
ในระหว่างนี้ เราสามารถบรรลุสิ่งที่คล้ายกันได้โดยใช้แอตทริบิวต์ style
:
ดูปากกา [อุปกรณ์ประกอบฉากที่กำหนดเอง: ตัวเลือกรูปแบบ](https://codepen.io/smashingmag/pen/PrJdBG/) โดย Miriam Suzanne
<div>...</div> /* The `*=` atttribute selector will match a string anywhere in the attribute */ [style*='--stripes-angle'] { /* Only define the function where we want to call it */ --stripes: linear-gradient(…); }
วิธีนี้มีประโยชน์มากที่สุดเมื่อเราต้องการรวมคุณสมบัติอื่นๆ นอกเหนือจากพารามิเตอร์ที่ตั้งค่าไว้ ตัวอย่างเช่น การตั้งค่าพื้นที่กริดสามารถเพิ่มช่องว่างภายในและพื้นหลังได้:
[style*='--grid-area'] { background-color: white; grid-area: var(--grid-area, auto / 1 / auto / -1); padding: 1em; }
บทสรุป
เมื่อเราเริ่มประกอบชิ้นส่วนเหล่านี้เข้าด้วยกัน จะเห็นได้ชัดว่าคุณสมบัติที่กำหนดเองมีมากกว่ากรณีการใช้งานตัวแปรทั่วไปที่เราคุ้นเคย เราไม่เพียงแต่สามารถจัดเก็บค่าและกำหนดขอบเขตให้กับการเรียงซ้อน — แต่เราสามารถใช้ค่าเหล่านี้เพื่อจัดการการเรียงซ้อนในรูปแบบใหม่ และสร้างส่วนประกอบที่ชาญฉลาดขึ้นได้โดยตรงใน CSS
สิ่งนี้เรียกร้องให้เราคิดใหม่เกี่ยวกับเครื่องมือต่างๆ ที่เราเคยใช้ในอดีต — ตั้งแต่การตั้งชื่อแบบแผนการตั้งชื่ออย่าง SMACSS และ BEM ไปจนถึงรูปแบบ "กำหนดขอบเขต" และ CSS-in-JS เครื่องมือเหล่านี้จำนวนมากช่วยหลีกเลี่ยงความเฉพาะเจาะจง หรือจัดการรูปแบบไดนามิกในภาษาอื่น ซึ่งเป็นกรณีใช้งานที่เราสามารถแก้ไขได้โดยตรงด้วยคุณสมบัติที่กำหนดเอง รูปแบบไดนามิกที่เราคำนวณบ่อยครั้งใน JS ตอนนี้สามารถจัดการได้โดยส่งข้อมูลดิบไปยัง CSS
ในตอนแรก การเปลี่ยนแปลงเหล่านี้อาจถูกมองว่าเป็น "ความซับซ้อนที่เพิ่มขึ้น" เนื่องจากเราไม่คุ้นเคยกับการเห็นตรรกะใน CSS และเช่นเดียวกับโค้ดทั้งหมด วิศวกรรมมากเกินไปอาจเป็นอันตรายได้ แต่ฉันขอเถียงว่าในหลายกรณี เราสามารถใช้พลังนี้ไม่ เพิ่ม ความซับซ้อน แต่เพื่อ ย้าย ความซับซ้อนออกจากเครื่องมือและข้อตกลงของบริษัทอื่น กลับเข้าไปในภาษาหลักของการออกแบบเว็บ และ (ที่สำคัญกว่านั้น) กลับเข้าไปใน เบราว์เซอร์ หากสไตล์ของเราต้องการการคำนวณ การคำนวณนั้นควรอยู่ภายใน CSS ของเรา
แนวคิดทั้งหมดนี้สามารถนำไปเพิ่มเติมได้อีกมาก พร็อพเพอร์ตี้แบบกำหนดเองเพิ่งเริ่มเห็นการนำไปใช้ในวงกว้าง และเราเพิ่งเริ่มที่จะขีดข่วนพื้นผิวของสิ่งที่เป็นไปได้ ฉันตื่นเต้นที่จะได้เห็นสิ่งที่เกิดขึ้น และคนอื่นๆ คิดเห็นอย่างไร มีความสุข!
อ่านเพิ่มเติม
- “ได้เวลาเริ่มใช้ CSS Custom Properties แล้ว” Serg Hospodarets
- “คู่มือกลยุทธ์สำหรับคุณสมบัติที่กำหนดเองของ CSS” Michael Riethmuller