เลย์เอาต์ CSS และการออกแบบที่ปรับเปลี่ยนตามอุปกรณ์

  1. 1 พื้นฐาน
    1. ควรใช้โหมดเลย์เอาต์ใด
    2. หลักการทำงาน
  2. 2 Flexbox
  3. 3 ตารางกริดและตารางกริดย่อย
    1. ตัวอย่างโค้ด: ตารางกริดและตารางกริดย่อย
  4. 4 การค้นหาคอนเทนเนอร์
    1. ตัวอย่างโค้ด: การจัดตัวพิมพ์แบบไหลโดยใช้หน่วยการค้นหาคอนเทนเนอร์
  5. 5 การวางซ้อนแบบเนทีฟ การวางตำแหน่งโฆษณา Anchor และบริบทการซ้อน
  6. 6 การติดตามการล้นและความเสถียรของเลย์เอาต์
  7. 7 กลไกของวิวพอร์ตและการกระจายแทร็ก
  8. 8 เลนตารางกริด (หรือที่เรียกว่า Masonry)

1 Fundamentals

ใช้เครื่องมือเลย์เอาต์ของเบราว์เซอร์เมื่อเป็นไปได้เพื่อประสิทธิภาพที่ดีขึ้น ใช้การกำหนดขนาดภายใน พร็อพเพอร์ตี้เชิงตรรกะ และ aspect-ratio ก่อนที่จะใช้มิติข้อมูลที่ฮาร์ดโค้ดหรือการค้นหาสื่อที่ซับซ้อน

1.1 ควรใช้โหมดเลย์เอาต์ใด

โดยไล่ตามแผนผังการตัดสินใจจากบนลงล่างและหยุดที่รายการแรกที่ตรงกัน โปรดทราบว่าเลย์เอาต์สามารถฝังอยู่ภายในเลย์เอาต์อื่นๆ ได้ และการตัดสินใจแต่ละครั้งจะขึ้นอยู่กับกรณีการใช้งานของคอนเทนเนอร์นั้นๆ

  1. เป็นแถวหรือคอลัมน์ของรายการแบบง่ายๆ ใช่ไหม ใช้ Flexbox — 1 มิติ เน้นเนื้อหาเป็นหลัก เนื้อหาจะกระจายไปตามแกนเดียว
  2. องค์ประกอบที่ฝังจำเป็นต้องสอดคล้องกับแทร็กของตารางกริดระดับปู่ย่าตายายหรือไม่ ใช้ subgrid — 2 มิติ, เน้นความสัมพันธ์, รับค่าแทร็กหลักเพื่อให้หลานสามารถจัดแนวข้ามพี่น้องได้
  3. เป็นหน้าหรือโครงสร้างคอมโพเนนต์ที่ซับซ้อนซึ่งมีทั้งแถวและคอลัมน์ใช่ไหม ใช้ grid — 2 มิติ, เลย์เอาต์มาก่อน, คุณกำหนดโครงร่างและเนื้อหาจะเติมเต็ม
  4. เนื้อหาเป็นข้อความยาวๆ ที่ควรแบ่งออกเป็นคอลัมน์ที่สมดุลหรือไม่ ใช้หลายคอลัมน์ — โฟลว์ 1 มิติ สไตล์หนังสือพิมพ์
  5. สินค้ามีความสูงต่างกันและต้องบรรจุอย่างแน่นหนาใช่ไหม ใช้ grid กับ grid-auto-flow: dense ในปัจจุบัน และใช้ Masonry แบบเนทีฟ (หรือที่เรียกว่า "เลนตารางกริด") เฉพาะเมื่อจัดส่งในเป้าหมาย Baseline (ดู §8)
  6. องค์ประกอบจำเป็นต้องลอยอยู่เหนือหน้าเว็บและเชื่อมโยงเชิงพื้นที่กับทริกเกอร์หรือไม่ แม้จะข้ามขอบเขต DOM หรือบริบทการซ้อน ใช้การจัดตำแหน่ง Anchor - anchor-name ในทริกเกอร์, position-anchor ในการวางซ้อน (ดู §5)

1.2 หลักการทำงาน

  • ใช้พร็อพเพอร์ตี้เชิงตรรกะ (inline-size, block-size, margin-inline, padding-block, inset-inline-start) สำหรับขนาดเลย์เอาต์และระยะห่าง — ดูรายละเอียดทั้งหมดได้ที่ CSS: Modern Architecture
  • ใช้โมเดลทางความคิดแบบเนื้อหามาก่อนเทียบกับเลย์เอาต์มาก่อน: flexbox เมื่อรายการกำหนดโฟลว์, grid เมื่อคุณกำหนดโครงร่างก่อน
  • ใช้ชวเลข place-* (place-content, place-items, place-self) เพื่อจัดแนวทั้ง 2 แกนในการประกาศเดียว
  • ใช้การกำหนดขนาดภายใน (min-content, max-content, fit-content()) และแทร็กที่ยืดหยุ่น (fr, minmax()) ก่อน width/height แบบคงที่ ซึ่งจะช่วยลดการค้นหาสื่อและทำให้เลย์เอาต์มีความยืดหยุ่นมากขึ้น
  • อย่าใช้ overflow: hidden เพื่อปกปิดปัญหาเกี่ยวกับเลย์เอาต์ แต่ให้แก้ไขที่ต้นทาง ตัวอย่างเช่น องค์ประกอบที่ถูกแทนที่บางอย่าง เช่น <canvas>, <img>, <svg>, <video>, <iframe> จะใช้ display: inline โดยค่าเริ่มต้น ซึ่งอาจเพิ่มช่องว่างในเลย์เอาต์บางอย่าง แทนที่จะตัดช่องว่างด้วย overflow ให้นำออกโดยใช้ค่า display-outside แบบบล็อก (เช่น display: block, grid, flex เป็นต้น)
  • ใช้ aspect-ratio เพื่อจองพื้นที่สำหรับสื่อและป้องกันไม่ให้เลย์เอาต์เปลี่ยนแปลงก่อนที่ชิ้นงานจะโหลด
.sidebar       { inline-size: max-content; }    /* Size to longest unbreakable token. */
.main-content  { inline-size: fit-content; }    /* Grow to available space, no further. */
.media         { aspect-ratio: 16 / 9; inline-size: 100%; block-size: auto; }
body.centered  { display: grid; place-content: center; min-block-size: 100dvb; }

ดูข้อมูลเกี่ยวกับ calc-size() และการปรับขนาดภายในที่คำนึงถึงข้อจำกัดได้ที่คำนวณด้วยขนาดภายใน

2 Flexbox

เลย์เอาต์แบบ 1 มิติ - รายการจะไหลไปตามแกนหลักแกนเดียวโดยมีการจัดแนวบนแกนไขว้ ใช้สำหรับแถบนำทาง แถบเครื่องมือ แถวรายการ และการกระจายแถวหรือคอลัมน์เดียว

สิ่งที่ควรทำ

  • สร้างบริบทด้วย display: flex และตั้งค่าแกนหลักด้วย flex-direction (ค่าเริ่มต้น row)
  • ใช้ flex-wrap: wrap เมื่อใดก็ตามที่อาจเกิดการล้น — nowrap ที่ไม่มี overflow: auto/hidden จะล้นในวิวพอร์ตแคบๆ
  • เลเยอร์ flex-wrap: balance หลัง flex-wrap: wrap ในแถวที่ตัดขึ้นบรรทัดใหม่ของรายการขนาดกะทัดรัด (เช่น กลุ่มแท็ก, เม็ดยาตัวกรอง, กลุ่มปุ่ม หรือลิงก์การนำทาง) เพื่อกระจายรายการอย่างสม่ำเสมอในบรรทัดต่างๆ และป้องกันไม่ให้มีรายการเดียวที่ไม่มีคู่ในแถวสุดท้าย เบราว์เซอร์ที่ไม่รองรับจะเปลี่ยนไปใช้ wrap
  • ใช้ชวเลข flex <grow> <shrink> <basis> (เช่น flex: 1 1 250px) กับรายการแทนการตั้งค่า flex-grow/flex-shrink/flex-basis ทีละรายการ
  • ใช้ gap (หรือ row-gap/column-gap แบบเต็ม) เพื่อเว้นวรรคระหว่างรายการแทนระยะขอบย่อย
  • ใส่ safe (เช่น align-items: safe center) ไว้หน้าการจัดตำแหน่งตามตำแหน่งเพื่อให้เนื้อหาที่โฟกัสได้ไม่ถูกตัดเมื่อคอนเทนเนอร์แคบกว่าเนื้อหา
  • ดันรายการเดียวไปที่ปลายสุดของแกนหลักด้วย margin-inline-start: auto (หรือ margin-block-start: auto) ซึ่งเป็นวิธีมาตรฐานในการหลีกเลี่ยงปัญหา
  • ลบล้างการปรับแนวแกนไขว้ต่อรายการด้วย align-self
  • ใช้ align-items เพื่อจัดกึ่งกลางรายการทั้งหมดบนแกนขวาง ใช้ margin: auto กับรายการเดียวเพื่อจัดกึ่งกลางทั้ง 2 แกนอย่างอิสระ ใช้ align-content เฉพาะเมื่อคอนเทนเนอร์ตัดขึ้นบรรทัดใหม่และมีพื้นที่ว่างเพิ่มเติมในแถว
  • ตั้งค่า min-inline-size: 0 (หรือ min-width: 0) ในรายการ Flex ที่มีเนื้อหาแบบไม่ขึ้นบรรทัดใหม่ที่ยาว (URL, โค้ด, สตริงยาว) โดยค่าเริ่มต้น รายการ Flex จะไม่หดตัวลงต่ำกว่าขนาดเนื้อหา ซึ่งทำให้เกิดการล้น

สิ่งที่ไม่ควรทำ

  • อย่าใช้ justify-self กับรายการ Flex เนื่องจากใช้ได้กับเลย์เอาต์แบบตารางกริด บล็อก และตำแหน่งสัมบูรณ์เท่านั้น โปรดใช้ระยะขอบอัตโนมัติแทน
  • อย่าใช้ order หรือ flex-direction: *-reverse เพื่อจัดเรียงเนื้อหาแบบอินเทอร์แอกทีฟใหม่ โดยจะเปลี่ยนเฉพาะลำดับภาพเท่านั้น ลำดับ DOM ยังคงขับเคลื่อนโฟกัสตามลำดับ ดังนั้นขั้นตอนการใช้แท็บแป้นพิมพ์จะไม่ตรงกับสิ่งที่ผู้ใช้เห็น
  • อย่าสับสนระหว่าง space-around (ช่องว่างครึ่งหนึ่งที่ส่วนท้าย) กับ space-evenly (ช่องว่างเท่ากันก่อน ระหว่าง และหลัง)
  • อย่าลืมการพลิกแกน: เมื่อ flex-direction: column, justify-content จะจัดแนวบนแกนบล็อกและ align-items จะจัดแนวบนแกนอินไลน์ ซึ่งตรงข้ามกับค่าเริ่มต้น
  • อย่าปรับขนาดทั้งคอนเทนเนอร์และองค์ประกอบย่อยให้เต็มซึ่งกันและกัน เนื่องจากเป็นสาเหตุที่พบบ่อยของการล้นและผลลัพธ์ที่ไม่คาดคิด กำหนดขนาดที่แน่นอนให้ด้านใดด้านหนึ่ง
  • อย่าตั้งค่าทั้ง flex-basis และ width/inline-size ในรายการเดียวกัน เนื่องจาก flex-basis จะมีความสำคัญเหนือกว่าในบริบท Flex และระบบจะไม่สนใจ width ใช้ flex-basis (หรือชวเลข flex) เป็นแหล่งข้อมูลที่เชื่อถือได้เพียงแหล่งเดียว (SSOT) สำหรับการปรับขนาดรายการ Flex
.card-grid        { display: flex; flex-flow: row wrap; gap: 1rem; }
.card-item        { flex: 1 1 250px; }                  /* grow, shrink, basis */
.card-item-action { margin-inline-start: auto; }        /* Push to main-axis end. */
.toolbar          { display: flex; align-items: safe center; }
.tag-list         { display: flex; flex-wrap: wrap; flex-wrap: balance; gap: 0.5rem; }

3 ตารางกริดและตารางกริดย่อย

เลย์เอาต์ 2 มิติ — กำหนดแถวและคอลัมน์อย่างชัดเจน หรือให้เครื่องมือดึงข้อมูลมาให้ Subgrid ช่วยให้ตารางกริดที่ฝังรับแทร็กของตารางกริดระดับบนมาใช้ เพื่อให้ตารางกริดระดับหลานจัดแนวตามตารางกริดระดับพี่น้องได้

การเลือกฟีเจอร์กริด

  • คุณทราบจำนวนคอลัมน์ที่ต้องการอย่างแน่ชัดหรือไม่
    • ใช่ - ใช้แทร็กที่มีเนื้อหาโจ่งแจ้ง (grid-template-columns: 200px 1fr, repeat(3, 1fr) ฯลฯ)
    • คอลัมน์ต่างๆ ต้องมีขนาดต่างกันหรือไม่ (แถบด้านข้าง + หลัก, ส่วนหัวครอบคลุมทั้งหมด) → ใช้ grid-template-areas สำหรับภูมิภาคที่ตั้งชื่อและอ่านได้
    • คอลัมน์ทั้งหมดมีรูปแบบเดียวกันหรือจัดตำแหน่งตามหมายเลขบรรทัดเท่านั้น → ใช้ repeat(N, ...) หรือเส้นที่มีชื่อ
    • No (ปรับเปลี่ยนตามอุปกรณ์ ไม่ทราบจำนวนรายการ) - ใช้ repeat(auto-fit, minmax(min, 1fr))
    • รายการในแถวสุดท้ายควรยืดจนเต็มพื้นที่ที่เหลือหรือไม่ → auto-fit
    • แทร็กแถวสุดท้ายที่ว่างเปล่าควรคงขนาดต่ำสุดไว้ (คงช่องว่างของคอลัมน์ไว้) หรือไม่ → auto-fill
  • คุณต้องการวางรายการในตำแหน่งที่เฉพาะเจาะจงไหม
    • ใช่ - ใช้ grid-column: <start> / <end> หรือ grid-area: <name>
    • ไม่ (เพียงแค่ครอบคลุมหลายแทร็ก ตำแหน่งการไหลไม่สำคัญ) - ใช้ grid-column: span <n>
  • องค์ประกอบย่อยจำเป็นต้องรับค่าขนาดแทร็กของตารางกริดหลัก (การจัดแนวขอบไม่สม่ำเสมอในองค์ประกอบระดับเดียวกัน) หรือไม่
    • ใช่ - ใช้ตารางกริดย่อยในแกนที่ได้รับผลกระทบ
    • จำนวนเด็กต่อเซลล์เป็นตัวแปรหรือไม่ → subgrid แกนเดียวเท่านั้น ใช้ grid-auto-rows/grid-auto-columns สำหรับอีกแกนหนึ่ง
    • จำนวนบุตรหลานคงที่หรือไม่ → subgrid บนทั้ง 2 แกนก็ใช้ได้
    • ไม่ — ตารางกริดมาตรฐาน ไม่จำเป็นต้องใช้ตารางกริดย่อย

สิ่งที่ควรทำ

  • สร้างบริบทด้วย display: grid
  • ใช้ grid-template-areas สำหรับเลย์เอาต์ระดับหน้าเว็บที่ซับซ้อน ชื่อพื้นที่จะเป็นเอกสารในตัว และการประกาศสามารถจัดแนวเป็นแถวและคอลัมน์เพื่อให้ดูได้ง่าย
  • ใช้ repeat(auto-fit, minmax(200px, 1fr)) สำหรับตารางกริดการ์ดที่ปรับเปลี่ยนตามอุปกรณ์ซึ่งขยายแทร็กที่เติมแล้วให้เต็มแถว หรือใช้ auto-fill เพื่อคงแทร็กที่ซ้ำกันซึ่งว่างเปล่าไว้ที่ขนาดต่ำสุด
  • ใช้ fr สำหรับการกระจายแทร็กตามสัดส่วน และใช้ minmax(min, max) สำหรับแทร็กที่ยืดหยุ่นแต่มีขอบเขต
  • จัดตำแหน่งรายการด้วย grid-column: span <n> เพื่อปรับขนาดตามแทร็ก grid-column: <start> / <end> เพื่อวางที่บรรทัดที่เฉพาะเจาะจง หรือ grid-area: <name> สำหรับขอบเขตที่มีชื่อ
  • ใช้ Subgrid (grid-template-columns: subgrid หรือ grid-template-rows: subgrid) เพื่อแก้ปัญหา "ขอบไม่เท่ากัน" ในรายการการ์ด โดยองค์ประกอบภายใน เช่น ชื่อ ข้อมูลเมตา และ CTA จะเรียงกันในระดับเดียวกัน
  • จับคู่การประกาศ subgrid กับการประกาศ grid-template-rows/-columns ที่ชัดเจนก่อนหน้าเป็นทางเลือกสำรองแบบเรียงซ้อนเดียวกันสำหรับเบราว์เซอร์รุ่นเก่า

สิ่งที่ไม่ควรทำ

  • อย่าคาดหวังว่าขนาดแทร็ก auto-fit/auto-fill จะมาจากเนื้อหาของรายการ แต่จะมาจากอาร์กิวเมนต์ขนาด repeat()
  • อย่าใช้ grid-auto-flow: dense กับเนื้อหาแบบอินเทอร์แอกทีฟ ซึ่งจะจัดเรียงรายการอย่างมีประสิทธิภาพ แต่จะจัดลำดับใหม่ในเชิงภาพ ซึ่งจะทำให้ขั้นตอนการใช้แท็บแป้นพิมพ์ตามลำดับ DOM เสียไป
  • อย่าใช้ Subgrid กับทั้ง 2 แกนเมื่อจำนวนรายการย่อยเป็นตัวแปร ส่วนเสริมจะอยู่ในแทร็กสุดท้าย ให้ใช้ grid-auto-rows/grid-auto-columns สำหรับแกนโดยนัยแทน
  • อย่าสับสนระหว่าง justify-items/align-items (จัดแนวเนื้อหาของรายการภายในแทร็ก) กับ justify-content/align-content (จัดแนวแทร็กตารางกริดภายในคอนเทนเนอร์) การใช้ค่าที่ไม่ถูกต้องจะไม่มีผลใดๆ
  • อย่าใช้ repeat(auto-fit/auto-fill, ...) โดยไม่มี inline-size ที่แน่นอนในคอนเทนเนอร์ เนื่องจากภายใน display: inline-grid หรือรายการ Flex ที่ไม่มีขนาด คอนเทนเนอร์จะไม่มีความกว้างให้แบ่ง ซึ่งทำให้คาดเดาจำนวนแทร็กไม่ได้

3.1 ตัวอย่างโค้ด: grid และ subgrid

Page shell: <main class="page-layout"> มี <header>, <aside>, <section class="card-grid"> ที่มี <div class="card"> รายการย่อย และ <footer>

/* Align grid-template-areas in rows and columns for readability. */
.page-layout {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-areas:
    "header  header  header"
    "sidebar main    main"
    "footer  footer  footer";
  gap: 1.5rem;
}

header  { grid-area: header; }
aside   { grid-area: sidebar; }
footer  { grid-area: footer; }

.card-grid {
  grid-area: main;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  grid-template-rows: auto 1fr; /* title block, body block */
  gap: 1rem;
}

.card {
  grid-row: span 2;
  display: grid;
  /* Same-cascade fallback: ignored when subgrid is supported. */
  grid-template-rows: auto 1fr;
  grid-template-rows: subgrid;
}

4 การค้นหาคอนเทนเนอร์

ค้นหาขนาด (หรือสไตล์ที่คำนวณแล้ว) ของคอนเทนเนอร์ระดับบนสุดแทนวิวพอร์ต โมเดลทางความคิด: การค้นหาคอนเทนเนอร์ = บริบทของคอมโพเนนต์, การค้นหาสื่อ = เลย์เอาต์หน้าเว็บส่วนกลางและค่ากำหนดของผู้ใช้ (prefers-color-scheme, prefers-reduced-motion)

สิ่งที่ควรทำ

  • สร้างบริบทการจำกัดด้วย container-type: inline-size (การค้นหาความกว้างเท่านั้น) หรือ container-type: size (ทั้ง 2 แกน) ใน Wrapper ก่อนที่จะค้นหาองค์ประกอบสืบทอดได้
  • ตั้งชื่อคอนเทนเนอร์ด้วย container-name (หรือชื่อย่อ container: container: card / inline-size) เมื่อบริบทที่ฝังอยู่ซ้อนทับกัน
  • รวมหน่วยการค้นหาคอนเทนเนอร์ในการคำนวณประเภทและระยะห่างแบบไหล: cqi/cqb (อินไลน์/บล็อกเชิงตรรกะ), cqw/cqh (กายภาพ), cqmin/cqmax
  • ระบุ block-size ที่แน่นอนให้กับคอนเทนเนอร์เมื่อใดก็ตามที่ใช้ container-type: size หากไม่มี องค์ประกอบสืบทอดจะยุบเนื่องจากการจำกัดขนาดบังคับให้คอนเทนเนอร์ละเว้นเนื้อหา

สิ่งที่ไม่ควรทำ

  • อย่าใช้ block-size เป็นค่า container-type เนื่องจากไม่ถูกต้อง ใช้ size สำหรับทั้ง 2 แกน
  • อย่าคาดหวังว่าขนาดที่แท้จริงขององค์ประกอบย่อยจะส่งผลต่อคอนเทนเนอร์หลังจากประกาศ container-type ระบบจะคำนวณคอนเทนเนอร์เสมือนว่าไม่มีองค์ประกอบย่อยเมื่อการกักกันทำงานอยู่
  • อย่าใช้หน่วยการค้นหาคอนเทนเนอร์ภายในองค์ประกอบสืบทอดขององค์ประกอบระดับบนสุดที่ไม่เข้าเกณฑ์ เนื่องจากจะเปลี่ยนกลับไปใช้วิวพอร์ตขนาดเล็ก (svw/svh)

4.1 ตัวอย่างโค้ด: ตัวพิมพ์แบบไหลโดยใช้หน่วยการค้นหาคอนเทนเนอร์

.card-wrapper {
  container: card / inline-size; /* shorthand for container-name / container-type */
}

@container card (inline-size > 400px) {
  .content {
    display: flex;
    gap: 2rem;
  }
}

.title {
  /* Fluid type bound to the container width, not the viewport. */
  font-size: clamp(1rem, 4cqi, 2rem);
}

ดูรูปแบบการจัดสไตล์ที่ปรับเปลี่ยนตามอุปกรณ์ซึ่งขับเคลื่อนด้วยคอมโพเนนต์ได้ที่การจัดสไตล์ที่คำนึงถึงขนาดและการปรับขนาดแบบยืดหยุ่น

5 การวางซ้อนแบบเนทีฟ การวางตำแหน่ง Anchor และบริบทการซ้อน

กรณีที่ควรใช้แต่ละองค์ประกอบพื้นฐานของภาพซ้อนทับ

  • ใช้ popover สำหรับ UI ที่ไม่เป็นโมดัลและเกิดขึ้นชั่วคราว (เมนูแบบลอย, ข้อความโทสต์, เคล็ดลับเครื่องมือ) ซึ่งจะอยู่ในเลเยอร์บนสุด โดยไม่จำเป็นต้องจัดการ z-index
  • ใช้ <dialog> กับ .showModal() สำหรับการโต้ตอบโมดัลที่ต้องมีการดักจับโฟกัสและฉากหลังที่ไม่มีการโต้ตอบ
  • อย่ารวม popover และ .showModal() ไว้ในองค์ประกอบเดียวกัน เนื่องจากเป็นสถานะรันไทม์ที่แยกกัน

การจัดตำแหน่ง Anchor (เลย์เอาต์เชิงพื้นที่ของภาพซ้อนทับ):

  • ใช้ position-area (หรือ anchor() ในส่วนแทรก) และ anchor-size() เพื่อจัดตำแหน่งและขนาดของภาพซ้อนทับให้สัมพันธ์กับทริกเกอร์
  • ใช้ position-try-fallbacks: flip-block (หรือ flip-inline) เพื่อให้เบราว์เซอร์เปลี่ยนตำแหน่งเมื่อการวางซ้อนล้นวิวพอร์ต
  • อย่าผสมคีย์เวิร์ดทางกายภาพและเชิงตรรกะในค่า position-area เดียว ให้เลือกระบบพิกัดเดียว
  • ตรวจหาฟีเจอร์ด้วย @supports (anchor-name: --x) และระบุตำแหน่งสำรองแบบสัมบูรณ์

ดูรายละเอียดการติดตั้งใช้งานทั้งหมด กลยุทธ์ Polyfill และข้อมูลอ้างอิงค่า popover ได้ที่การควบคุมกล่องโต้ตอบและป๊อปโอเวอร์แบบประกาศ และเคล็ดลับเครื่องมือที่รับรู้ตำแหน่ง ดูข้อมูลเกี่ยวกับการกำหนดตำแหน่ง Anchor ที่ใช้กับเมนูแบบเลื่อนลง เมนูแบบลอย และตัวบ่งชี้แท็บได้ที่เมนูแบบเลื่อนลงที่รับรู้วิวพอร์ตและขีดเส้นใต้แท็บการกำหนดตำแหน่ง Anchor

6 การติดตามรายการที่เกินมาและความเสถียรของเลย์เอาต์

จัดการการเปลี่ยนแปลงเลย์เอาต์ แถบเลื่อน และการคลิปได้อย่างคาดการณ์ได้

สิ่งที่ควรทำ

  • ใช้ overflow: auto เพื่อให้แถบเลื่อนปรากฏเฉพาะเมื่อเนื้อหาล้นจริงๆ เท่านั้น
  • ใช้ overflow: clip เพื่อตัดเนื้อหาโดยไม่สร้างคอนเทนเนอร์การเลื่อน เลือกใช้การล้นด้วย overflow-clip-margin
  • ใช้ scrollbar-gutter: stable เพื่อสำรองพื้นที่สำหรับแถบเลื่อนและป้องกันไม่ให้เลย์เอาต์เปลี่ยนแปลงเมื่อเนื้อหาเพิ่มขึ้น
  • ใช้ overscroll-behavior: contain (หรือ none) ในขอบเขตการเลื่อนที่ซ้อนกัน (เช่น แถบด้านข้าง, โมดัล และรายการ) เพื่อหยุดไม่ให้เชนการเลื่อนแสดงขึ้นในหน้า หรือใช้ overscroll-behavior: chain ใน Wrapper การเลื่อนระดับกลาง (เช่น แทร็กปัดเพื่อแสดง) เมื่อการเลื่อนที่เกินมาควรส่งต่อไปยังตัวเลื่อนระดับบนโดยไม่ทริกเกอร์การเด้งแบบแถบยางในเครื่อง
  • ใช้ -webkit-line-clamp + display: -webkit-box + -webkit-box-orient: vertical ไตรแอดสำหรับการตัดทอนหลายบรรทัด แม้จะมีคำนำหน้า แต่รูปแบบนี้ได้รับการระบุอย่างสมบูรณ์และไม่ได้เลิกใช้งาน ประกาศชวเลข line-clamp ที่ไม่มีคำนำหน้าควบคู่กันไป เบราว์เซอร์ที่ยังไม่รองรับจะละเว้นพร็อพเพอร์ตี้ดังกล่าวโดยไม่เป็นอันตราย สิ่งที่ไม่ควรทำ

  • อย่าใช้ overflow: scroll เมื่อ auto ทำได้ — scroll บังคับให้มีแถบเลื่อนแม้ว่าจะไม่มีอะไรให้เลื่อนก็ตาม

  • อย่าใช้ overflow: hidden เมื่อคุณต้องการเพียงแค่ตัดคลิปเท่านั้น hidden จะสร้างคอนเทนเนอร์การเลื่อนที่สามารถเลื่อนได้โดยใช้โปรแกรม

.scrollable-list {
  max-block-size: 400px;
  overflow-y: auto;
  scrollbar-gutter: stable;       /* Reserve scrollbar space. */
  overscroll-behavior: contain;   /* No scroll chaining into the page. */
}

.snippet {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  line-clamp: 3;                  /* Ignored where unsupported. */
  overflow: clip;
}

ดูข้อมูลเชิงลึกเกี่ยวกับ overflow: clip และ overflow-clip-margin ได้ที่การควบคุมการตัดส่วนที่เกิน สำหรับการส่งต่อการเลื่อนที่ซ้อนกันด้วย overscroll-behavior: chain ใน UI แบบปัดเพื่อแสดง ให้ดูที่ปัดเพื่อแสดง ดูข้อมูลเกี่ยวกับสี ขนาด และธีมของแถบเลื่อนได้ที่หัวข้อปรับแต่งสีหรือความหนาของแถบเลื่อน โหมดมืด และปรับแถบเลื่อนให้เหมาะกับค่ากำหนดคอนทราสต์สูง

7 กลไกของวิวพอร์ตและการกระจายแทร็ก

  • ใช้ dvh/dvw สำหรับคอนเทนเนอร์เลย์เอาต์บนอุปกรณ์เคลื่อนที่ซึ่งต้องพิจารณาการเลื่อน UI ของเบราว์เซอร์ (การยุบ/ขยายแถบ URL)
  • อย่าใช้ 100vw สำหรับเลย์เอาต์แบบเต็มความกว้าง เนื่องจากจะละเว้นความกว้างของแถบเลื่อนและทำให้เกิดการล้นในแนวนอน โปรดใช้ 100%, 100dvw หรือ 100svw แทน

ดูข้อมูลเกี่ยวกับจุดแรกเข้าของการออกแบบที่ปรับเปลี่ยนตามอุปกรณ์ (หน่วยวิวพอร์ตแบบไดนามิก การจองพื้นที่สำหรับสื่อ การจัดพิมพ์แบบไหล) ได้ที่การออกแบบที่ปรับเปลี่ยนตามอุปกรณ์

8 เลนตารางกริด (หรือที่เรียกว่าแบบเรียงต่อกัน)

ข้อกำหนดนี้อยู่ระหว่างการพัฒนาภายใต้ชื่อ "grid lanes" (เช่น display: grid-lanes) ขณะที่ต้นแบบทดลองก่อนหน้านี้ใช้ grid-template-rows: masonry

สิ่งที่ควรทำ

  • ใช้ตารางกริดที่มี grid-auto-flow: dense เพื่อจัดวางองค์ประกอบให้ชิดกันในวันนี้ โดยยอมรับว่าลำดับ DOM อาจไม่ตรงกับลำดับภาพ
  • ใช้หลายคอลัมน์ (columns: 3; column-gap: 1rem) สำหรับขั้นตอนที่คล้ายกับ Masonry ซึ่งมีเนื้อหาจำนวนมากเมื่อรายการเป็นส่วนย่อยของเอกสารแทนที่จะเป็นการ์ดที่มีน้ำหนักเท่ากัน
  • ใช้ grid-template-rows: masonry เป็นการเพิ่มประสิทธิภาพแบบต่อเนื่องเท่านั้น โดยตรวจหาฟีเจอร์ด้วย @supports

สิ่งที่ไม่ควรทำ

  • อย่าจัดส่ง grid-template-rows: masonry เป็นข้อกำหนดที่เข้มงวดจนกว่าเป้าหมาย Baseline จะตามทัน
.gallery       { columns: 3 200px; column-gap: 1rem; }
.gallery > *   { break-inside: avoid; margin-block-end: 1rem; }

@supports (grid-template-rows: masonry) {
  .gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    grid-template-rows: masonry;
    gap: 1rem;
    columns: unset;
  }
}