- CSS Subgrid คือฟีเจอร์ที่ก้าวข้ามข้อจำกัดของ Grid แบบเดิม ทำให้สามารถ ขยาย โครงสร้างกริดของพาเรนต์ลงไปถึงองค์ประกอบ DOM ชั้นล่างได้
- เดิมทีมีเพียง ลูกโดยตรงเท่านั้น ที่เข้าร่วมในกริดได้ แต่เมื่อใช้ subgrid แม้แต่โครงสร้างแบบซ้อนอย่าง
<ul> และ <li> ก็สามารถจัดวางอยู่ใน เซลล์กริดเดียวกัน ได้
- ช่วยแก้ปัญหา ความไม่สมดุลระหว่างองค์ประกอบพี่น้อง ที่เกิดจากความยาวคอนเทนต์ต่างกัน และรองรับการสร้างเลย์เอาต์ที่ให้การ์ดหรือเซกชันแต่ละส่วน ตอบสนองแบบไดนามิก
- อย่างไรก็ตาม ยังมีข้อควรระวัง เช่น ปัญหาการจองแถว, การรีเซ็ตเลขเส้นกริด, และ ความเข้ากันไม่ได้กับ fluid grid ที่อิง auto-fill
- เบราว์เซอร์หลักส่วนใหญ่รองรับแล้ว และสามารถ นำมาใช้แบบค่อยเป็นค่อยไป ได้ จึงเป็นเทคโนโลยีที่จะช่วยเพิ่ม ความยืดหยุ่นในการออกแบบ UI อย่างมากในอนาคต
แนวคิดพื้นฐานของ Subgrid
- CSS Grid ในช่วงแรกให้เฉพาะ องค์ประกอบลูกโดยตรงเท่านั้น ที่เข้าร่วมในเลย์เอาต์ได้
- ตัวอย่างเช่น ใน UI พอร์ตโฟลิโอ รูปภาพ
<li> ภายใน <ul> จะถูกรวมอยู่ในเซลล์เดียวโดยปริยาย
- หากใช้
grid-template-rows: subgrid และ grid-template-columns: subgrid จะเป็นการ สืบทอดนิยามแถวและคอลัมน์ ของกริดพาเรนต์
- ทำให้
<li> แต่ละตัวถูกจัดวางลงในเซลล์ของกริดพาเรนต์ได้โดยตรง พร้อมคงไว้ทั้ง โครงสร้าง HTML เชิงความหมายและการจัดแนวเชิงภาพ
- แม้จะทำผลลัพธ์แบบเดียวกันได้ด้วย Flexbox และ Grid แบบซ้อน แต่ subgrid ให้แนวทางที่กระชับกว่า เพราะ ใช้โครงสร้างกริดเดียวร่วมกัน
ความเป็นไปได้ใหม่ของเลย์เอาต์
- ในตัวอย่างการ์ดพอร์ตโฟลิโอ
<article> แต่ละใบมี กริด 2 คอลัมน์ สำหรับวางรูปภาพและข้อความ
- หน่วย
fr มีความยืดหยุ่น แต่เพราะแต่ละการ์ดคำนวณแยกกัน จึงเกิด ความกว้างคอลัมน์ไม่สมดุล
- เมื่อใช้
grid-template-columns: subgrid คอลัมน์ของกริดพาเรนต์จะถูก แชร์ร่วมกันโดยทุกการ์ด
- เมื่อคอนเทนต์เปลี่ยน เช่น ความยาวของชื่อเรื่อง กริดทั้งชุดจะปรับใหม่อัตโนมัติ
- วิธีนี้ทำให้สร้าง เลย์เอาต์ที่องค์ประกอบพี่น้องรับรู้กันและกัน ได้
- ตัวอย่าง: หากย่อชื่อ “Infinite Supercomputer” สัดส่วนรูปภาพและข้อความของการ์ดทั้งหมดจะปรับทันที
ข้อควรระวังเมื่อใช้ Subgrid (Gotchas)
การจองพื้นที่แถว
- การแชร์คอลัมน์นั้นเข้าใจง่าย แต่เมื่อ แชร์แถว จำเป็นต้องมี การจองแถวอย่างชัดเจน
- ตัวอย่าง: ในการ์ดตารางราคา หากต้องการให้รายการใน
<ul> แต่ละชุดเรียงตรงกันในแถวเดียวกัน ต้องกำหนดจำนวนแถวด้วย grid-row: span N
- โดยปกติ subgrid จะ กินพื้นที่เพียงเซลล์เดียว ดังนั้นหากต้องการใช้หลายแถว ต้อง ขยายพื้นที่กริดของพาเรนต์ ก่อน
หมายเลขกริดในกริดซ้อน
- subgrid จะ สืบทอดเทมเพลตแถวและคอลัมน์ จากพาเรนต์ แต่ หมายเลขเส้นกริดจะถูกรีเซ็ตใหม่
- ตัวอย่าง: แม้จะสืบทอดเส้นที่ 2 ถึง 5 ของพาเรนต์ ภายในก็จะถูกนับใหม่เป็น 1 ถึง 4
- แต่ละกริดมีดัชนีของตัวเอง ดังนั้น เลขเส้นกริดจึงทำงานเป็นดัชนีสัมพัทธ์ ไม่ใช่ ID แบบเฉพาะตัว
ใช้ร่วมกับ fluid grid ไม่ได้
- fluid grid ในรูปแบบ
repeat(auto-fill, minmax()) ไม่สามารถใช้ร่วมกับ subgrid ได้
- subgrid ต้องมี จำนวนคอลัมน์ที่กำหนดแน่นอน และไม่รองรับ
auto-fill หรือ auto-fit
- ผู้เขียนระบุอย่างชัดเจนว่ายังไม่พบวิธีแก้สำหรับการใช้คู่กันแบบนี้
การรองรับเบราว์เซอร์รุ่นเก่า
- แม้เบราว์เซอร์หลักจะรองรับตั้งแต่หลังปี 2023 แต่ อัตราการรองรับยังไม่ถึง 90%
- สามารถใช้เงื่อนไข
@supports not (grid-template-columns: subgrid) เพื่อ จัดเตรียมเลย์เอาต์ทางเลือก ได้
- ตัวอย่าง: เสนอ fallback ที่จัดรูปภาพและข้อความเป็น สแต็กแนวตั้ง
กรณีใช้งานจริงและบทสรุป
- เว็บไซต์นักพัฒนาของ Stripe (stripe.dev) จัดทั้งหน้าเป็นกริดขนาดใหญ่หนึ่งชุด และใช้ subgrid หลายชั้น เพื่อจัดวางรายละเอียดอย่างประณีต
- subgrid มีประโยชน์ไม่ใช่แค่กับเลย์เอาต์ขนาดใหญ่ แต่ยังใช้ได้ดีในการ ปรับ UI ขนาดเล็ก
- สามารถ ค่อย ๆ นำมาใช้ ได้โดยไม่จำเป็นต้องรื้อโครงสร้างทั้งโปรเจกต์ใหม่
- เป็นเครื่องมือที่ช่วยเพิ่ม ความยืดหยุ่นรูปแบบใหม่ให้กับเลย์เอาต์ CSS และน่าลองนำไปใช้งานเชิงทดลองอย่างมาก
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
ฟีเจอร์ Subgrid เจ๋งมาก แต่ในตัวอย่างง่าย ๆ แรกนั้นใช้
ul { display: contents }เพื่อให้ลูก ๆ เข้าไปมีส่วนร่วมกับ grid layout ได้เหมือนกันถ้าไม่ได้จำเป็นต้องใช้ฟีเจอร์ subgrid วิธีนี้จะ มีประสิทธิภาพ กว่า
display: contentsจะลบองค์ประกอบ UL ออกจากเลย์เอาต์ไปเลยเลยไม่สามารถใส่สไตล์หรือรับ UI events ที่องค์ประกอบนั้นได้
ถ้าอยากใช้ UL เป็นพื้นที่ไฮไลต์หรือส่วนที่เลื่อนได้ subgrid จะมีประโยชน์กว่ามาก
frและใช้frกับข้อความเพื่อให้เติมพื้นที่ที่เหลือตอนก่อนหน้านี้เคยทำ UI เปรียบเทียบราคา แล้วลำบากมากเพราะไม่มี subgrid
การวางสองตารางไว้ข้างกันแล้วทำให้แถวตรงกันนั้น เป็นไปไม่ได้เลย
จะแก้ด้วยความสูงคงที่หรือคำนวณด้วย JS ก็ได้ แต่ในโครงสร้างคอมโพเนนต์ React มันไม่มีประสิทธิภาพมาก
เคยคิดว่า container queries น่าจะเป็นทางออกที่ดีกว่าไหม
แต่ถ้าต้องการรักษาความสอดคล้องของทั้งกริด subgrid อาจเหมาะกว่า
อ้างอิง ตัวอย่างบน CodePen จะเข้าใจได้ง่าย
แถมพอใช้ container ก็จะเกิด stacking context ใหม่ขึ้นมา ซึ่งไม่สะดวก
น่าเสียดายที่ใช้ subgrid กับ container ร่วมกันไม่ได้ ถ้าลูกอ้างอิงขนาดของ subgrid ได้ก็น่าจะแรงมาก
ทำให้นึกว่า “สุดท้ายเราก็กลับไปใช้เลย์เอาต์แบบ <table> กันแล้วเหรอ?”
ระบบ Grid เป็นเครื่องมือสำหรับการจัดวางเชิงภาพ ไม่เหมือนตารางที่ใช้แสดงโครงสร้างข้อมูล
ตอนนี้กริดกลายเป็นมาตรฐานไปแล้ว ก็อยากให้เลิกเอาไปเทียบกับตารางเสียที
แต่ไม่ได้คำนึงถึง responsive หรือ accessibility เลย สุดท้ายก็เหมือนเราประดิษฐ์ตารางขึ้นมาใหม่
สุดท้าย CSS ก็แค่เอาความสามารถนั้นกลับมาทำใหม่ในรูปแบบที่ดีกว่า
หนึ่งใน grid bug ที่เคยเจอคือ ถ้ากำหนดขนาดของ
<img>เป็นเปอร์เซ็นต์ ขนาดเซลล์จะเพี้ยนไปตามขนาดต้นฉบับของรูปเกิดทั้งใน Firefox และ Chromium และบั๊กที่เกี่ยวข้องอยู่ที่ Mozilla Bug 1857365
น่าเสียดายที่บางครั้ง CSS ต้องเพิ่มข้อมูลโครงสร้างเอกสารเพื่อทำเลย์เอาต์
อย่างเช่นต้องระบุจำนวนแถวเอาไว้
หรือไม่ก็ให้ flex container หนึ่ง เลียนแบบการกระจายตัว ของอีกคอนเทนเนอร์ได้ก็คงดี
แต่ถ้าทำแบบนั้น DX อาจซับซ้อนขึ้นมาก
สงสัยว่าทำไมในตัวอย่างโค้ดถึงมีสไตล์ทั้งในไฟล์ HTML และ CSS
ดูแค่ CSS ของตัวอย่าง subgrid แรกแล้วใช้เวลาหาอยู่นานว่า UL ถูกใส่สไตล์อะไรไว้บ้าง
ทำให้นึกว่า “สุดท้ายก็กลับมาที่ grid อีกแล้วเหรอ?”
สมัย HTML ก่อนหน้านี้ก็เคยทำอะไรคล้าย ๆ กัน
บล็อกโพสต์ของ Josh น่าทึ่งเสมอ
ทั้งความชัดเจนของเนื้อหา เซนส์ด้านดีไซน์ และเว็บไซต์แบบอินเทอร์แอ็กทีฟ ล้วนยอดเยี่ยม
โดยส่วนตัวผมยังรู้สึกว่า grid ใช้งานยากอยู่ดี
ไวยากรณ์มันแปลก ๆ และความรู้สึกเรื่องเลย์เอาต์ก็ยังไม่ค่อยเข้ามือ Flexbox เข้าใจง่ายและยืดหยุ่นกว่ามาก
Flexbox ควบคุมขนาดโดยยึดตามเนื้อหา ส่วน Grid ควบคุมโดยยึด คอนเทนเนอร์เป็นศูนย์กลาง
เนื้อหาไม่จัดเข้าหากันอัตโนมัติในหลายแกน และต้องวางทุกอย่างด้วยมือทั้งหมด
อาจเป็นเพราะผมยังไม่เข้าใจแก่นของ grid ดีพอ หรือไม่ก็ไม่เหมาะกับลักษณะงานที่ทำ