Article · programming
HTML & CSS EP11: CSS Grid ขั้นสูง — จัดตำแหน่งองค์ประกอบและสร้าง Responsive Layout
HTML & CSS
บทเรียน HTML และ CSS ตอนที่ 11 เจาะลึก CSS Grid ขั้นสูง การกำหนดตำแหน่งด้วยเส้น Grid การใช้ named lines และ grid-template-areas การจัดวางองค์ประกอบแบบซ้อนกัน การควบคุมขนาดด้วย minmax() และการสร้าง Responsive Dashboard พร้อมตัวอย่างโค้ด HTML เต็มหน้าที่นำไปทดลองได้ทันที
HTML & CSS EP11: ยกระดับ CSS Grid สำหรับเว็บไซต์จริง
จากบทเรียนก่อนหน้าที่เราได้รู้จัก `display: grid`, `grid-template-columns`, `gap`, `minmax()` และ `auto-fit` แล้ว ตอนนี้เราจะนำความรู้เหล่านั้นมาต่อยอดสู่การออกแบบเลย์เอาต์ที่ซับซ้อนขึ้น เช่น Dashboard ระบบจัดการสินค้า หน้ารายงานธุรกิจ และหน้าเว็บไซต์ที่มีส่วนเนื้อหาหลายขนาด เป้าหมายคือเขียน CSS ที่อ่านง่าย ปรับขนาดได้ และดูแลรักษาได้สะดวก โดยไม่ต้องกำหนดตำแหน่งทุกองค์ประกอบด้วยค่าคงที่
💡 เป้าหมายของบทเรียนเมื่อจบบทนี้ คุณจะสามารถกำหนดตำแหน่งรายการด้วย Grid Lines ใช้ Named Areas จัดโครงสร้างหน้าเว็บ ควบคุมการยืดและหดของคอลัมน์ และออกแบบเลย์เอาต์ที่ปรับตามหน้าจอโดยใช้ CSS Grid ร่วมกับ Media Queries ได้
1. ทำความเข้าใจ Grid Lines และ Grid Tracks
CSS Grid ประกอบด้วยเส้นแบ่งพื้นที่ที่เรียกว่า **Grid Lines** และพื้นที่ระหว่างเส้นที่เรียกว่า **Grid Tracks** โดย Grid Track อาจเป็นแถวหรือคอลัมน์ก็ได้ เส้น Grid จะมีหมายเลขเริ่มจาก 1 ในทิศทางปกติของเลย์เอาต์ และสามารถนำหมายเลขเส้นมาใช้ระบุตำแหน่งเริ่มต้นและสิ้นสุดของรายการได้
<div class="layout">
<div class="item item-a">A</div>
<div class="item item-b">B</div>
<div class="item item-c">C</div>
</div>
<style>
.layout {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
grid-template-rows: 100px 100px;
gap: 12px;
}
.item-a {
grid-column: 1 / 3;
grid-row: 1;
}
.item-b {
grid-column: 3;
grid-row: 1 / 3;
}
.item-c {
grid-column: 1 / 3;
grid-row: 2;
}
</style>
ในตัวอย่างนี้ A ครอบคลุมคอลัมน์ที่หนึ่งถึงก่อนเส้นที่สาม ส่วน B อยู่ในคอลัมน์ที่สามและครอบคลุมสองแถว ขณะที่ C ใช้สองคอลัมน์แรกในแถวที่สอง การระบุจุดเริ่มต้นและจุดสิ้นสุดด้วยเส้นช่วยให้ควบคุมรูปแบบเลย์เอาต์ได้ละเอียดกว่าการจัดวางอัตโนมัติเพียงอย่างเดียว
2. ใช้ span เพื่อกำหนดขนาดพื้นที่ที่รายการครอบครอง
หากไม่ต้องการอ้างอิงหมายเลขเส้นโดยตรง สามารถใช้ `span` เพื่อกำหนดว่ารายการควรกินพื้นที่กี่แทร็ก ตัวอย่างเช่น `grid-column: span 2` หมายถึงให้รายการครอบคลุมสองคอลัมน์ ส่วน `grid-row: span 2` ให้ครอบคลุมสองแถว ตำแหน่งเริ่มต้นยังขึ้นอยู่กับอัลกอริทึมการจัดวางของ Grid และพื้นที่ที่เหลืออยู่
.dashboard-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 16px;
}
.dashboard-card--featured {
grid-column: span 2;
grid-row: span 2;
}
.dashboard-card--wide {
grid-column: span 2;
}
.dashboard-card--full {
grid-column: 1 / -1;
}
⚠️ ระวังการกำหนดตำแหน่งซ้ำซ้อนหากกำหนดให้หลายรายการครอบครองพื้นที่เดียวกัน อาจเกิดการซ้อนทับได้โดยตั้งใจหรือไม่ตั้งใจก็ได้ ควรตรวจสอบ `grid-column`, `grid-row` และลำดับการวางรายการให้สอดคล้องกับโครงสร้างเนื้อหา
3. ตั้งชื่อ Grid Lines เพื่อให้ CSS อ่านง่าย
นอกจากการใช้หมายเลขเส้นแล้ว CSS Grid ยังอนุญาตให้ตั้งชื่อเส้นใน `grid-template-columns` หรือ `grid-template-rows` ได้ การตั้งชื่อช่วยให้โค้ดสื่อความหมาย โดยเฉพาะเลย์เอาต์ที่มีคอลัมน์สำหรับเมนูหลัก เนื้อหา และแผงด้านข้าง
.page {
display: grid;
grid-template-columns:
[sidebar-start] 240px
[sidebar-end content-start] minmax(0, 1fr)
[content-end];
gap: 20px;
}
.sidebar {
grid-column: sidebar-start / sidebar-end;
}
.main-content {
grid-column: content-start / content-end;
}
ชื่อเส้นอย่าง `sidebar-start` และ `content-end` ทำให้เจตนาของเลย์เอาต์ชัดเจนกว่าหมายเลขเส้นเพียงอย่างเดียว หากโครงสร้างมีการเปลี่ยนจำนวนคอลัมน์ในภายหลัง ควรตรวจสอบชื่อเส้นและกฎที่อ้างอิงชื่อเหล่านั้นให้ตรงกับโครงสร้างใหม่
4. วางแผนผังหน้าด้วย grid-template-areas
สำหรับหน้าที่มีโครงสร้างชัดเจน การใช้ `grid-template-areas` ช่วยให้มองเห็นผังหน้าเว็บจาก CSS ได้ทันที โดยกำหนดชื่อพื้นที่ให้กับองค์ประกอบผ่าน `grid-area` แต่ละแถวในแผนผังต้องมีจำนวนเซลล์เท่ากัน และชื่อพื้นที่เดียวกันควรสร้างเป็นรูปทรงสี่เหลี่ยมผืนผ้าต่อเนื่องกัน
.app-layout {
display: grid;
grid-template-columns: 220px minmax(0, 1fr) 280px;
grid-template-areas:
"header header header"
"sidebar main aside"
"footer footer footer";
gap: 16px;
}
.app-header { grid-area: header; }
.app-sidebar { grid-area: sidebar; }
.app-main { grid-area: main; }
.app-aside { grid-area: aside; }
.app-footer { grid-area: footer; }
ในตัวอย่างนี้ Header และ Footer กินพื้นที่ครบทั้งสามคอลัมน์ ส่วน Sidebar, Main และ Aside แบ่งพื้นที่ในแถวกลาง วิธีนี้เหมาะกับ Dashboard ที่มีเมนูนำทาง เนื้อหาหลัก และแผงข้อมูลประกอบ
5. ปรับผังหน้าเว็บด้วย Media Queries
เลย์เอาต์สามคอลัมน์อาจเหมาะกับจอคอมพิวเตอร์ แต่แคบเกินไปบนมือถือ เราจึงสามารถเปลี่ยน `grid-template-areas` ภายใน Media Query เพื่อเรียงส่วนต่าง ๆ ใหม่ตามลำดับที่เหมาะสม การเปลี่ยนผังด้วย CSS ช่วยให้ใช้ HTML ชุดเดิมได้โดยไม่จำเป็นต้องสร้างเนื้อหาซ้ำ
.app-layout {
display: grid;
grid-template-columns: 220px minmax(0, 1fr) 260px;
grid-template-areas:
"header header header"
"sidebar main aside"
"footer footer footer";
gap: 16px;
}
@media (max-width: 900px) {
.app-layout {
grid-template-columns: 200px minmax(0, 1fr);
grid-template-areas:
"header header"
"sidebar main"
"aside aside"
"footer footer";
}
}
@media (max-width: 620px) {
.app-layout {
grid-template-columns: minmax(0, 1fr);
grid-template-areas:
"header"
"main"
"sidebar"
"aside"
"footer";
}
}
✅ หลักการ Responsive ที่ควรจำกำหนดผังหลักสำหรับหน้าจอใหญ่ก่อน แล้วใช้ Media Queries ปรับคอลัมน์และพื้นที่ตามความกว้างที่เหมาะสม อย่าลืมทดสอบทั้งหน้าจอขนาดกลางและมือถือจริง เพราะความยาวของข้อความและขนาดภาพอาจส่งผลต่อเลย์เอาต์
6. ควบคุมขนาดด้วย minmax() และ min-width: 0
ปัญหาที่พบบ่อยใน Grid คือคอลัมน์ขยายกว้างเกินพื้นที่ เพราะเนื้อหาภายในมีขนาดขั้นต่ำโดยปริยาย เช่น URL ยาว ตารางกว้าง หรือข้อความที่ตัดบรรทัดไม่ได้ การใช้ `minmax(0, 1fr)` ช่วยอนุญาตให้แทร็กหดได้ถึงศูนย์ในส่วนขั้นต่ำ และการกำหนด `min-width: 0` ให้รายการช่วยลดการขยายเกินคาด อย่างไรก็ตาม เนื้อหาที่ยาวมากอาจยังต้องจัดการด้วย `overflow-wrap` หรือกฎอื่นตามชนิดข้อมูล
.content-grid {
display: grid;
grid-template-columns: 240px minmax(0, 1fr);
gap: 20px;
}
.content-grid > * {
min-width: 0;
}
.long-text {
overflow-wrap: anywhere;
}
.content-grid img {
max-width: 100%;
height: auto;
}
7. สร้างการ์ดที่มีขนาดไม่เท่ากันอย่างเป็นระบบ
ในหน้าแสดงบทความหรือสินค้า เราอาจต้องการการ์ดเด่นที่ใหญ่กว่ารายการอื่น สามารถใช้ `grid-column: span 2` เพื่อขยายการ์ดให้กินสองคอลัมน์ และใช้ `grid-row: span 2` เพื่อเพิ่มความสูงตามจำนวนแถวที่ครอบครอง แต่ควรตรวจสอบขนาดหน้าจอและจำนวนคอลัมน์ที่เหลือ เพื่อป้องกันเลย์เอาต์ดูไม่สมดุล
.article-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
grid-auto-rows: minmax(160px, auto);
gap: 18px;
}
.article-card--featured {
grid-column: span 2;
grid-row: span 2;
}
@media (max-width: 700px) {
.article-grid {
grid-template-columns: minmax(0, 1fr);
}
.article-card--featured {
grid-column: auto;
grid-row: auto;
}
}
เมื่อหน้าจอเล็กลง ตัวอย่างนี้จะเปลี่ยนเป็นคอลัมน์เดียวและยกเลิกการขยายการ์ดเด่น เพื่อให้แต่ละรายการใช้พื้นที่เต็มแถวตามลำดับ การออกแบบลักษณะนี้ช่วยลดความจำเป็นในการเลื่อนแนวนอนบนอุปกรณ์ขนาดเล็ก
8. ใช้ auto-placement และ grid-auto-flow
หากไม่ได้กำหนดตำแหน่งให้ทุก Grid Item เบราว์เซอร์จะจัดวางรายการที่เหลือตามกฎ Auto-placement โดยอัตโนมัติ ค่าเริ่มต้นของ `grid-auto-flow` คือ `row` ซึ่งวางรายการตามแถว หากใช้ `column` จะจัดวางตามคอลัมน์ ส่วน `dense` อนุญาตให้เบราว์เซอร์ย้อนกลับไปเติมช่องว่างที่เหลืออยู่ได้ แต่ลำดับที่มองเห็นอาจไม่ตรงกับลำดับการอ่านตาม DOM จึงควรใช้ด้วยความระมัดระวัง
.gallery {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
grid-auto-rows: minmax(120px, auto);
grid-auto-flow: row;
gap: 14px;
}
/* ใช้ dense เมื่อยอมรับการเติมช่องว่างได้ */
.gallery--dense {
grid-auto-flow: row dense;
}
⚠️ อย่าพึ่งพาตำแหน่งที่มองเห็นเพียงอย่างเดียวการจัดวางด้วย Grid ไม่ควรทำให้ลำดับภาพที่ผู้ใช้เห็นขัดแย้งกับลำดับการอ่านของเอกสาร โดยเฉพาะเมื่อใช้ `dense` หรือกำหนดตำแหน่งรายการด้วยตนเอง ควรจัดลำดับ HTML ให้มีความหมายและใช้งานด้วยคีย์บอร์ดได้อย่างสมเหตุสมผล
9. CSS Grid ทำงานร่วมกับ Flexbox อย่างไร
ไม่จำเป็นต้องเลือกระหว่าง Grid กับ Flexbox เพียงอย่างเดียว แนวทางที่ใช้ได้ดีคือใช้ Grid สำหรับเลย์เอาต์หลักของหน้า เช่น การแบ่ง Sidebar และ Main Content จากนั้นใช้ Flexbox ภายในส่วนย่อย เช่น การจัดปุ่มในแถบเครื่องมือ การวางไอคอนข้างข้อความ หรือการจัดเนื้อหาในส่วนหัวของการ์ด
| สถานการณ์ | แนวทางที่เหมาะสม | เหตุผล |
|---|---|---|
| แบ่งหน้าเป็น Sidebar และ Main | CSS Grid | ควบคุมแถวและคอลัมน์ของหน้าได้พร้อมกัน |
| จัดปุ่มในแถบเครื่องมือ | Flexbox | จัดองค์ประกอบตามแกนหลักได้สะดวก |
| สร้างตารางการ์ดสินค้า | CSS Grid | ควบคุมจำนวนคอลัมน์และช่องว่างได้ |
| จัดไอคอนกับข้อความในปุ่ม | Flexbox | เหมาะกับองค์ประกอบย่อยที่เรียงตามแนวเดียว |
| สร้าง Dashboard พร้อมการ์ดข้อมูล | Grid ร่วมกับ Flexbox | Grid จัดหน้า ส่วน Flexbox จัดเนื้อหาภายใน |
10. ข้อผิดพลาดที่พบบ่อยในการใช้ CSS Grid
กำหนด grid-template-columns แล้วแต่ยังมีแถบเลื่อนแนวนอน
ตรวจสอบเนื้อหาภายในคอลัมน์ โดยเฉพาะข้อความยาว ตาราง รูปภาพ และองค์ประกอบที่มีความกว้างคงที่ ลองใช้ `minmax(0, 1fr)` กับคอลัมน์ที่ยืดหยุ่น กำหนด `min-width: 0` ให้รายการที่จำเป็น และจัดการข้อความด้วย `overflow-wrap: anywhere` หากเหมาะกับเนื้อหา
พื้นที่ใน grid-template-areas ไม่ตรงกัน
ตรวจสอบว่าแต่ละแถวมีจำนวนเซลล์เท่ากัน และชื่อพื้นที่เดียวกันเชื่อมต่อกันเป็นรูปสี่เหลี่ยมผืนผ้า หลีกเลี่ยงการกำหนดชื่อพื้นที่เดียวกันเป็นส่วนที่แยกจากกันหรือมีรูปทรงไม่ถูกต้อง
การ์ดเด่นล้นออกจากเลย์เอาต์บนมือถือ
ภายใน Media Query สำหรับหน้าจอเล็ก ให้เปลี่ยนจำนวนคอลัมน์และยกเลิกการขยายรายการ เช่น กำหนด `grid-column: auto` และ `grid-row: auto` สำหรับการ์ดเด่น เพื่อให้รายการกลับมาอยู่ในแถวตามปกติ
ทำไมใช้ justify-content แล้วไม่เห็นความเปลี่ยนแปลง?
`justify-content` และ `align-content` ใช้จัดตำแหน่งชุดแทร็กเมื่อมีพื้นที่ว่างในคอนเทนเนอร์ หาก Grid เต็มพื้นที่พอดีหรือแทร็กขยายเต็มแล้ว อาจไม่มีพื้นที่เหลือให้เห็นผลชัดเจน หากต้องการจัดรายการภายในแต่ละเซลล์ ให้ตรวจสอบ `justify-items`, `align-items` หรือ `place-items` แทน
11. แนวทางออกแบบ Grid ให้ดูแลรักษาง่าย
ควรตั้งชื่อคลาสตามหน้าที่ขององค์ประกอบ ใช้ `minmax(0, 1fr)` ในคอลัมน์เนื้อหาที่ยืดหยุ่น และเลือกใช้ `grid-template-areas` เมื่อผังหน้าเป็นส่วนสำคัญที่ต้องอ่านเข้าใจได้รวดเร็ว หลีกเลี่ยงการกำหนดตำแหน่งทุกรายการโดยไม่มีความจำเป็น เพราะจะทำให้เพิ่มหรือลดเนื้อหาในอนาคตได้ยาก ควรให้รายการทั่วไปไหลอัตโนมัติ และกำหนดตำแหน่งเฉพาะรายการที่ต้องการความโดดเด่น
✅ Checklist ก่อนนำขึ้นใช้งานตรวจสอบจำนวนคอลัมน์ในแต่ละขนาดหน้าจอ ตรวจสอบเนื้อหาที่ยาวและรูปภาพ ทดสอบกรณีมีรายการจำนวนต่างกัน ตรวจสอบลำดับการอ่านและการใช้งานด้วยคีย์บอร์ด รวมถึงเปิดเครื่องมือ Developer Tools เพื่อตรวจดูเส้น Grid เมื่อเลย์เอาต์ไม่เป็นไปตามที่คาด
12. แบบฝึกหัดท้ายบท
สร้างหน้า Dashboard ที่มี Header, Sidebar, Main Content, Summary Panel และ Footer โดยใช้ `grid-template-areas` จากนั้นเพิ่มการ์ดข้อมูลอย่างน้อยหกใบ กำหนดให้การ์ดหนึ่งใบกินพื้นที่สองคอลัมน์ แล้วปรับเลย์เอาต์ให้เหลือคอลัมน์เดียวเมื่อหน้าจอมีความกว้างไม่เกิน 620 พิกเซล สุดท้ายทดลองใส่ข้อความยาวและตรวจสอบว่าไม่มีแถบเลื่อนแนวนอนโดยไม่จำเป็น
13. ตัวอย่างฉบับเต็ม: Responsive Business Dashboard
ตัวอย่างต่อไปนี้เป็นหน้า HTML แบบเต็ม สามารถบันทึกเป็น `index.html` แล้วเปิดด้วยเบราว์เซอร์ได้ทันที ตัวอย่างใช้ Grid Areas สำหรับโครงสร้างหน้า ใช้ Grid ซ้อนกันสำหรับการ์ดสรุปข้อมูล และใช้ Flexbox ในแถบหัวเรื่องและส่วนแสดงสถานะ มี Media Queries สำหรับหน้าจอขนาดกลางและมือถือ
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Responsive Business Dashboard</title>
<style>
* { box-sizing: border-box; }
:root {
--page: #f1f5f9;
--surface: #ffffff;
--ink: #172033;
--muted: #64748b;
--border: #dbe3ef;
--primary: #2563eb;
--success: #15803d;
}
body {
margin: 0;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
background: var(--page);
color: var(--ink);
line-height: 1.5;
}
.dashboard {
min-height: 100vh;
display: grid;
grid-template-columns: 230px minmax(0, 1fr);
grid-template-rows: auto minmax(0, 1fr) auto;
grid-template-areas:
"sidebar header"
"sidebar main"
"sidebar footer";
gap: 18px;
padding: 18px;
max-width: 1500px;
margin: 0 auto;
}
.sidebar {
grid-area: sidebar;
align-self: stretch;
padding: 20px;
background: #172554;
color: #fff;
border-radius: 18px;
}
.brand { margin: 0 0 22px; font-size: 1.2rem; }
.nav-list {
display: flex;
flex-direction: column;
gap: 8px;
padding: 0;
margin: 0;
list-style: none;
}
.nav-list a {
display: block;
padding: 10px 12px;
color: #dbeafe;
text-decoration: none;
border-radius: 9px;
}
.nav-list a:hover,
.nav-list a:focus-visible {
background: #1e40af;
color: #fff;
outline-offset: 3px;
}
.header {
grid-area: header;
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 12px;
padding: 20px 24px;
border: 1px solid var(--border);
border-radius: 16px;
background: var(--surface);
}
.header h1 { margin: 0; font-size: clamp(1.35rem, 3vw, 2rem); }
.header p { margin: 4px 0 0; color: var(--muted); }
.date-label {
padding: 8px 12px;
border-radius: 999px;
background: #dbeafe;
color: #1d4ed8;
font-size: .9rem;
font-weight: 700;
}
.main { grid-area: main; min-width: 0; }
.section-title { margin: 0 0 14px; font-size: 1.2rem; }
.stats-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 14px;
}
.stat-card {
min-width: 0;
padding: 18px;
border: 1px solid var(--border);
border-radius: 15px;
background: var(--surface);
}
.stat-label { margin: 0; color: var(--muted); font-size: .92rem; }
.stat-value { margin: 8px 0; font-size: clamp(1.25rem, 2.5vw, 1.8rem); font-weight: 800; overflow-wrap: anywhere; }
.stat-change { margin: 0; color: var(--success); font-size: .88rem; }
.content-grid {
display: grid;
grid-template-columns: minmax(0, 1.5fr) minmax(240px, 1fr);
gap: 16px;
margin-top: 18px;
}
.panel {
min-width: 0;
padding: 20px;
border: 1px solid var(--border);
border-radius: 15px;
background: var(--surface);
}
.panel h2 { margin: 0 0 14px; font-size: 1.1rem; }
.activity-list { display: grid; gap: 12px; margin: 0; padding: 0; list-style: none; }
.activity-item {
display: flex;
align-items: flex-start;
gap: 12px;
padding-bottom: 12px;
border-bottom: 1px solid #e2e8f0;
}
.activity-item:last-child { padding-bottom: 0; border-bottom: 0; }
.activity-icon {
display: grid;
place-items: center;
flex: 0 0 40px;
width: 40px;
height: 40px;
border-radius: 11px;
background: #dbeafe;
}
.activity-item p { margin: 0; }
.activity-item small { color: var(--muted); }
.progress-row { margin: 14px 0; }
.progress-label { display: flex; justify-content: space-between; gap: 12px; margin-bottom: 7px; font-size: .92rem; }
.progress-track { height: 9px; border-radius: 999px; background: #e2e8f0; overflow: hidden; }
.progress-fill { height: 100%; border-radius: inherit; background: var(--primary); }
.footer {
grid-area: footer;
padding: 12px 4px;
color: var(--muted);
font-size: .9rem;
}
@media (max-width: 1050px) {
.stats-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.content-grid { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 700px) {
.dashboard {
grid-template-columns: minmax(0, 1fr);
grid-template-rows: auto auto minmax(0, 1fr) auto;
grid-template-areas:
"header"
"sidebar"
"main"
"footer";
padding: 12px;
gap: 12px;
}
.sidebar { padding: 14px; }
.brand { margin-bottom: 12px; }
.nav-list { flex-direction: row; flex-wrap: wrap; }
.nav-list a { padding: 8px 10px; }
.header { padding: 16px; }
.stats-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 420px) {
.stats-grid { grid-template-columns: minmax(0, 1fr); }
.date-label { width: 100%; text-align: center; }
}
</style>
</head>
<body>
<div class="dashboard">
<aside class="sidebar">
<h2 class="brand">LevelLance</h2>
<nav aria-label="เมนูหลัก">
<ul class="nav-list">
<li><a href="#overview">ภาพรวม</a></li>
<li><a href="#activity">กิจกรรม</a></li>
<li><a href="#performance">ประสิทธิภาพ</a></li>
</ul>
</nav>
</aside>
<header class="header">
<div>
<h1>Business Dashboard</h1>
<p>ภาพรวมตัวอย่างข้อมูลธุรกิจ</p>
</div>
<span class="date-label">ข้อมูลตัวอย่างเพื่อการเรียนรู้</span>
</header>
<main class="main" id="overview">
<h2 class="section-title">สรุปผลการดำเนินงาน</h2>
<section class="stats-grid" aria-label="ตัวชี้วัดสำคัญ">
<article class="stat-card">
<p class="stat-label">ยอดขายตัวอย่าง</p>
<p class="stat-value">฿128,450</p>
<p class="stat-change">เพิ่มขึ้น 8.2%</p>
</article>
<article class="stat-card">
<p class="stat-label">คำสั่งซื้อ</p>
<p class="stat-value">324</p>
<p class="stat-change">เพิ่มขึ้น 4.5%</p>
</article>
<article class="stat-card">
<p class="stat-label">ลูกค้าตัวอย่าง</p>
<p class="stat-value">1,280</p>
<p class="stat-change">เพิ่มขึ้น 6.1%</p>
</article>
<article class="stat-card">
<p class="stat-label">อัตราสำเร็จ</p>
<p class="stat-value">96.8%</p>
<p class="stat-change">อยู่ในเกณฑ์เป้าหมาย</p>
</article>
</section>
<section class="content-grid">
<article class="panel" id="activity">
<h2>กิจกรรมล่าสุด</h2>
<ul class="activity-list">
<li class="activity-item">
<span class="activity-icon" aria-hidden="true">🛒</span>
<div><p><strong>มีคำสั่งซื้อใหม่</strong></p><small>ตัวอย่างกิจกรรมสำหรับแสดงรายการล่าสุด</small></div>
</li>
<li class="activity-item">
<span class="activity-icon" aria-hidden="true">👤</span>
<div><p><strong>มีลูกค้าใหม่</strong></p><small>ตัวอย่างการแจ้งเตือนภายในแดชบอร์ด</small></div>
</li>
<li class="activity-item">
<span class="activity-icon" aria-hidden="true">📦</span>
<div><p><strong>อัปเดตข้อมูลสินค้า</strong></p><small>ตัวอย่างการเปลี่ยนแปลงข้อมูลระบบ</small></div>
</li>
</ul>
</article>
<article class="panel" id="performance">
<h2>เป้าหมายรายเดือน</h2>
<div class="progress-row">
<div class="progress-label"><span>ยอดขาย</span><strong>72%</strong></div>
<div class="progress-track" role="progressbar" aria-label="เป้าหมายยอดขาย" aria-valuemin="0" aria-valuemax="100" aria-valuenow="72"><div class="progress-fill" style="width:72%"></div></div>
</div>
<div class="progress-row">
<div class="progress-label"><span>คำสั่งซื้อ</span><strong>58%</strong></div>
<div class="progress-track" role="progressbar" aria-label="เป้าหมายคำสั่งซื้อ" aria-valuemin="0" aria-valuemax="100" aria-valuenow="58"><div class="progress-fill" style="width:58%"></div></div>
</div>
<div class="progress-row">
<div class="progress-label"><span>ความพึงพอใจ</span><strong>91%</strong></div>
<div class="progress-track" role="progressbar" aria-label="เป้าหมายความพึงพอใจ" aria-valuemin="0" aria-valuemax="100" aria-valuenow="91"><div class="progress-fill" style="width:91%"></div></div>
</div>
</article>
</section>
</main>
<footer class="footer">Responsive Business Dashboard — ตัวอย่างการประยุกต์ใช้ CSS Grid และ Flexbox</footer>
</div>
</body>
</html>
14. สรุปบทเรียน EP11
CSS Grid ขั้นสูงช่วยให้สร้างหน้าเว็บที่มีโครงสร้างซับซ้อนได้อย่างเป็นระบบ คุณได้เรียนรู้ Grid Lines, การใช้ `span`, Named Lines, `grid-template-areas`, Media Queries, `minmax()` และ Auto-placement รวมถึงวิธีใช้ Grid ร่วมกับ Flexbox เพื่อแบ่งหน้าที่การจัดเลย์เอาต์ให้เหมาะสมกับแต่ละส่วนของเว็บไซต์
🎯 ภารกิจหลังเรียนนำตัวอย่าง Dashboard ไปปรับใช้กับข้อมูลของคุณเอง เพิ่มการ์ดข้อมูลอย่างน้อยหนึ่งใบ ทดลองเปลี่ยนผังบนหน้าจอมือถือ และตรวจสอบว่าเนื้อหายังคงอ่านง่าย ไม่มีการซ้อนทับโดยไม่ตั้งใจ และใช้งานด้วยคีย์บอร์ดได้
เลย์เอาต์ที่แข็งแรงเริ่มจากโครงสร้างที่ชัดเจน แล้วจึงเพิ่มรายละเอียดเพื่อให้เหมาะกับผู้ใช้และอุปกรณ์แต่ละประเภท
อ่านจนถึงระยะ 500px ก่อนท้ายหน้าและอยู่ในหน้านี้อย่างน้อย 5 นาที เพื่อรับ EXP วันละครั้ง
⏳ กำลังจับเวลาอ่าน...