Guest Adventurer 🥉 Lv.1
🪙 50
🔷 10
← กลับคลังเนื้อหา
Article · programming

HTML & CSS EP12: Responsive Design ขั้นสูง — สร้างเว็บไซต์ที่ปรับตัวได้ทุกหน้าจอ

HTML & CSS

บทเรียน HTML และ CSS ตอนที่ 12 เรียนรู้การออกแบบ Responsive Web Design ขั้นสูง ตั้งแต่ Mobile First, Media Queries, Container Queries, Fluid Typography, CSS clamp(), รูปภาพ Responsive, การจัดการเมนูและการ์ดสินค้า ไปจนถึงการสร้างหน้าเว็บไซต์ตัวอย่างที่ปรับตามขนาดหน้าจอได้จริง พร้อมโค้ด HTML เต็มหน้าสำหรับนำไปทดลอง

HTML & CSS EP12: Responsive Design ขั้นสูง

เว็บไซต์ที่ดีไม่ควรทำงานได้เฉพาะบนหน้าจอคอมพิวเตอร์ แต่ต้องอ่านง่าย ใช้งานสะดวก และรักษาความสำคัญของเนื้อหาไว้ได้ทั้งบนมือถือ แท็บเล็ต โน้ตบุ๊ก และจอขนาดใหญ่ ในบทนี้เราจะนำพื้นฐาน HTML, CSS Grid และ Flexbox มาต่อยอดเป็นแนวทาง Responsive Web Design ที่ใช้ได้กับเว็บไซต์ธุรกิจ หน้า Landing Page ระบบหลังบ้าน และหน้ารายการสินค้า
💡 เป้าหมายของบทเรียนหลังเรียนจบ คุณจะสามารถออกแบบด้วยแนวคิด Mobile First เลือกจุดเปลี่ยนหน้าจออย่างมีเหตุผล ใช้ CSS clamp() เพื่อปรับขนาดอย่างยืดหยุ่น ใช้ Container Queries สำหรับคอมโพเนนต์ และสร้างหน้าเว็บที่ลดปัญหาข้อความล้นหรือแถบเลื่อนแนวนอน

1. Responsive Design คืออะไร

Responsive Web Design คือแนวทางการออกแบบหน้าเว็บให้โครงสร้างและเนื้อหาปรับตัวตามพื้นที่แสดงผล แทนที่จะสร้างหน้าเว็บแยกสำหรับอุปกรณ์แต่ละชนิด โดยทั่วไปจะใช้ Layout ที่ยืดหยุ่น รูปภาพที่ปรับขนาดได้ และ Media Queries เพื่อเปลี่ยนรูปแบบเมื่อพื้นที่ไม่เหมาะกับเลย์เอาต์เดิม
การทำ Responsive ไม่ได้หมายถึงการย่อทุกอย่างให้เล็กลงเท่านั้น บางครั้งต้องเปลี่ยนจำนวนคอลัมน์ ย้ายแถบเมนู เรียงการ์ดใหม่ ลดรายละเอียดที่ไม่จำเป็น หรือเพิ่มพื้นที่ให้ปุ่มเพื่อให้แตะได้สะดวก สิ่งสำคัญคือเนื้อหาหลักต้องยังเข้าถึงได้และไม่สร้างภาระให้ผู้ใช้

2. เริ่มจาก Mobile First

แนวคิด Mobile First คือเริ่มออกแบบจากหน้าจอขนาดเล็กก่อน แล้วค่อยเพิ่มความสามารถของเลย์เอาต์เมื่อมีพื้นที่มากขึ้น วิธีนี้ช่วยให้เราจัดลำดับเนื้อหาอย่างชัดเจน ลดการพึ่งพาความกว้างคงที่ และหลีกเลี่ยงการสร้างเลย์เอาต์ขนาดใหญ่ที่ยากต่อการย่อให้เหมาะกับมือถือ
/* ค่าเริ่มต้นสำหรับหน้าจอขนาดเล็ก */
.product-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 16px;
}

/* เพิ่มคอลัมน์เมื่อมีพื้นที่เพียงพอ */
@media (min-width: 600px) {
  .product-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 960px) {
  .product-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
ตัวอย่างนี้เริ่มจากหนึ่งคอลัมน์บนมือถือ เพิ่มเป็นสองคอลัมน์เมื่อหน้าจอกว้างอย่างน้อย 600 พิกเซล และเพิ่มเป็นสามคอลัมน์เมื่อกว้างอย่างน้อย 960 พิกเซล ค่าดังกล่าวเป็นตัวอย่างสำหรับฝึกฝน ไม่ใช่มาตรฐานที่ต้องใช้กับทุกเว็บไซต์ จุดเปลี่ยนควรพิจารณาจากเนื้อหาและพื้นที่จริง

3. เลือก Breakpoints จากเนื้อหา ไม่ใช่จากชื่ออุปกรณ์

Breakpoints คือค่าความกว้างที่ใช้เปลี่ยนกฎ CSS เช่น เปลี่ยนจากแถบนำทางแนวนอนเป็นเมนูที่จัดเรียงหลายบรรทัด หรือเปลี่ยนการ์ดสามคอลัมน์ให้เหลือสองคอลัมน์ ไม่จำเป็นต้องมีจุดเปลี่ยนตรงกับชื่อมือถือหรือแท็บเล็ต เพราะอุปกรณ์แต่ละรุ่นมีขนาดหน้าจอและสัดส่วนต่างกัน
สถานการณ์สิ่งที่ควรตรวจสอบแนวทางปรับ
เมนูเริ่มเบียดกันความกว้างรวมของลิงก์และช่องว่างให้เมนูขึ้นบรรทัดใหม่หรือเปลี่ยนรูปแบบ
การ์ดอ่านยากความกว้างขั้นต่ำที่เนื้อหาต้องการลดจำนวนคอลัมน์
บทความมีบรรทัดยาวเกินไปความกว้างของพื้นที่อ่านข้อความจำกัดความกว้างเนื้อหา
แถบด้านข้างเบียดพื้นที่หลักพื้นที่ที่เหลือสำหรับเนื้อหาย้ายแถบด้านข้างลงด้านล่าง
ปุ่มเรียงชิดกันเกินไปขนาดปุ่มและระยะห่างจัดปุ่มเป็นแนวตั้งบนหน้าจอเล็ก
✅ วิธีคิดที่ช่วยลดการแก้ CSS ซ้ำลองปรับความกว้างหน้าต่างเบราว์เซอร์อย่างช้า ๆ แล้วสังเกตว่าจุดใดที่เนื้อหาเริ่มอึดอัด จากนั้นจึงกำหนด Breakpoint เพื่อแก้ปัญหาที่พบจริง แทนการเลือกตัวเลขจากอุปกรณ์รุ่นใดรุ่นหนึ่ง

4. ใช้ clamp() เพื่อกำหนดขนาดที่ยืดหยุ่น

การกำหนดขนาดตัวอักษรด้วยค่าคงที่เพียงค่าเดียวอาจทำให้หัวข้อเล็กเกินไปบนจอใหญ่ หรือใหญ่เกินไปบนมือถือ ฟังก์ชัน `clamp(min, preferred, max)` ช่วยกำหนดค่าต่ำสุด ค่าที่ต้องการให้ปรับตามพื้นที่ และค่าสูงสุดได้ในคำสั่งเดียว
h1 {
  font-size: clamp(2rem, 1.2rem + 3vw, 4rem);
  line-height: 1.15;
}

.page-intro {
  font-size: clamp(1rem, 0.92rem + 0.5vw, 1.25rem);
  line-height: 1.7;
}

.section-title {
  font-size: clamp(1.35rem, 1rem + 1.2vw, 2rem);
}
ค่า `2rem` ในตำแหน่งแรกเป็นขนาดต่ำสุด ส่วน `1.2rem + 3vw` เป็นค่าที่ปรับตามพื้นที่ และ `4rem` เป็นขนาดสูงสุด วิธีนี้ช่วยให้หัวข้อปรับขนาดได้ต่อเนื่องโดยไม่ต้องสร้าง Media Query หลายชุดสำหรับตัวอักษรแต่ละขนาด อย่างไรก็ตาม ควรทดสอบการขยายตัวอักษรและตรวจสอบว่าเนื้อหาไม่ทับซ้อนกัน

5. ใช้ min(), max() และ minmax() ให้เหมาะกับพื้นที่

CSS มีฟังก์ชันสำหรับคำนวณขนาดหลายรูปแบบ `min()` เลือกค่าที่เล็กกว่า `max()` เลือกค่าที่ใหญ่กว่า และ `minmax()` ใช้กำหนดช่วงขนาดของแทร็กใน CSS Grid ฟังก์ชันเหล่านี้ช่วยให้คอลัมน์มีความยืดหยุ่นโดยไม่ต้องระบุความกว้างเป็นพิกเซลทุกส่วน
.article-container {
  width: min(100% - 32px, 1100px);
  margin-inline: auto;
}

.content-layout {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(220px, 1fr);
  gap: 24px;
}

.responsive-card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
  gap: 16px;
}
การใช้ `width: min(100% - 32px, 1100px)` ช่วยให้คอนเทนเนอร์เหลือระยะขอบบนหน้าจอแคบ และจำกัดความกว้างสูงสุดบนจอกว้าง ส่วน `repeat(auto-fit, minmax(...))` ช่วยให้ Grid เลือกจำนวนคอลัมน์ตามพื้นที่ที่มี โดยไม่ต้องกำหนด Media Query สำหรับจำนวนคอลัมน์ทุกระดับ

6. รูปภาพ Responsive และอัตราส่วนภาพ

รูปภาพเป็นสาเหตุหนึ่งที่ทำให้หน้าเว็บล้น หากระบุความกว้างตายตัวโดยไม่คำนึงถึงคอนเทนเนอร์ ควรกำหนด `max-width: 100%` และ `height: auto` เป็นพื้นฐาน หากต้องการกรอบภาพที่มีอัตราส่วนแน่นอน สามารถใช้ `aspect-ratio` และ `object-fit` เพื่อให้ภาพอยู่ในพื้นที่อย่างเหมาะสม
img,
picture,
video {
  max-width: 100%;
}

img {
  height: auto;
}

.product-image {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 12px;
}

.hero-image {
  width: 100%;
  aspect-ratio: 16 / 7;
  object-fit: cover;
}
สำหรับภาพสำคัญที่ต้องมีหลายขนาด สามารถใช้ `<picture>` และ `<source>` เพื่อให้เบราว์เซอร์เลือกไฟล์ที่เหมาะสมตาม Media Query หรือชนิดไฟล์ที่รองรับ หากเว็บไซต์มีภาพจำนวนมาก ควรปรับขนาดไฟล์และการบีบอัดด้วย เพื่อไม่ให้ผู้ใช้มือถือเสียเวลาโหลดภาพขนาดใหญ่โดยไม่จำเป็น
<picture>
  <source
    media="(max-width: 600px)"
    srcset="images/banner-mobile.webp"
  >
  <source
    media="(min-width: 601px)"
    srcset="images/banner-desktop.webp"
  >
  <img
    src="images/banner-desktop.jpg"
    alt="ภาพแบนเนอร์แสดงสินค้าในร้าน"
    loading="lazy"
  >
</picture>
⚠️ ระวังการใช้ loading="lazy"การโหลดภาพแบบ Lazy Loading เหมาะกับภาพที่อยู่ต่ำกว่าส่วนแรกของหน้า แต่ภาพหลักที่ผู้ใช้เห็นทันทีเมื่อเปิดหน้าเว็บอาจไม่ควรใช้ Lazy Loading เพราะอาจทำให้ภาพปรากฏช้าลง ควรเลือกตามตำแหน่งและความสำคัญของภาพ

7. Container Queries: ปรับคอมโพเนนต์ตามพื้นที่ของตัวเอง

Media Queries ตรวจสอบคุณสมบัติของหน้าจอหรือ Viewport ส่วน Container Queries ช่วยให้คอมโพเนนต์ปรับรูปแบบตามขนาดคอนเทนเนอร์ที่ครอบอยู่ เหมาะกับการ์ดสินค้า กล่องแนะนำบทความ หรือวิดเจ็ตที่อาจถูกนำไปวางในหลายตำแหน่งซึ่งมีความกว้างไม่เท่ากัน
.product-panel {
  container-type: inline-size;
}

.product-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 12px;
}

@container (min-width: 520px) {
  .product-card {
    grid-template-columns: 180px minmax(0, 1fr);
    align-items: center;
  }
}

.product-card img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}
ในตัวอย่างนี้ `.product-panel` ทำหน้าที่เป็น Query Container เมื่อพื้นที่ภายในกว้างอย่างน้อย 520 พิกเซล การ์ดจึงเปลี่ยนเป็นสองคอลัมน์ แม้การ์ดจะอยู่ในหน้าจอใหญ่ แต่หากถูกวางในแผงแคบก็ยังสามารถใช้รูปแบบหนึ่งคอลัมน์ได้ ทำให้คอมโพเนนต์นำกลับมาใช้ซ้ำได้ง่ายขึ้น

8. ออกแบบเมนูให้ใช้งานได้บนหน้าจอเล็ก

เมนูนำทางมักมีพื้นที่จำกัดบนมือถือ หากมีลิงก์หลายรายการ ควรจัดลำดับความสำคัญและเลือกแนวทางที่ไม่ทำให้ข้อความเบียดกัน ตัวอย่างพื้นฐานต่อไปนี้ใช้ Flexbox ให้ลิงก์ขึ้นบรรทัดใหม่ได้โดยไม่บังคับให้มี JavaScript หากต้องการทำเมนูเปิดปิด ควรใช้ปุ่มจริงและอัปเดตสถานะการเข้าถึงให้เหมาะสม
.site-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px 20px;
}

.site-nav__links {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.site-nav__links a {
  display: inline-block;
  padding: 10px 12px;
  border-radius: 8px;
  text-decoration: none;
}

@media (max-width: 560px) {
  .site-nav {
    align-items: stretch;
    flex-direction: column;
  }

  .site-nav__links {
    flex-direction: column;
  }

  .site-nav__links a {
    display: block;
  }
}

9. สร้างปุ่มและพื้นที่แตะที่เหมาะกับมือถือ

การใช้งานบนหน้าจอสัมผัสต่างจากการใช้เมาส์ ผู้ใช้ต้องแตะเป้าหมายบนหน้าจอ จึงควรมีพื้นที่แตะที่เหมาะสมและมีระยะห่างระหว่างปุ่มเพื่อลดการกดผิด อย่าพึ่งพา Hover เพียงอย่างเดียวในการแสดงข้อมูลสำคัญ และควรรักษา Focus Indicator สำหรับผู้ใช้คีย์บอร์ดด้วย
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 10px 16px;
  border: 0;
  border-radius: 10px;
  font: inherit;
  text-align: center;
  cursor: pointer;
}

.button:focus-visible,
a:focus-visible,
input:focus-visible {
  outline: 3px solid #93c5fd;
  outline-offset: 3px;
}

.button-group {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

@media (max-width: 480px) {
  .button-group .button {
    flex: 1 1 100%;
  }
}

10. ป้องกันข้อความและองค์ประกอบล้นหน้าจอ

ข้อความยาว URL ตาราง และองค์ประกอบที่กำหนดความกว้างคงที่อาจทำให้เกิด Horizontal Overflow ได้ ควรเริ่มตรวจจากองค์ประกอบที่มี `width` หรือ `min-width` มากเกินไป จากนั้นตรวจ Grid Track และเนื้อหาภายในคอลัมน์ การใช้ `min-width: 0` กับรายการ Grid หรือ Flexbox บางประเภทช่วยให้เนื้อหาหดได้ตามพื้นที่ แต่ข้อความและตารางก็อาจต้องมีวิธีแสดงผลเฉพาะของตัวเอง
* {
  box-sizing: border-box;
}

.page-content,
.grid-item,
.flex-item {
  min-width: 0;
}

.long-content {
  overflow-wrap: anywhere;
}

.responsive-table-wrapper {
  max-width: 100%;
  overflow-x: auto;
}

.responsive-table-wrapper table {
  width: 100%;
  border-collapse: collapse;
}
🚫 อย่าแก้ปัญหาด้วยการซ่อน Overflow ทั้งหน้าโดยไม่ตรวจสอบการใช้ `overflow-x: hidden` กับ Body อาจซ่อนเนื้อหาที่ล้นและทำให้ผู้ใช้เข้าถึงข้อมูลบางส่วนไม่ได้ ควรหาต้นเหตุขององค์ประกอบที่กว้างเกินจริง แล้วแก้ที่ Grid, Flexbox, รูปภาพ ตาราง หรือข้อความที่เกี่ยวข้องก่อน

11. ใช้ CSS Variables เพื่อดูแลธีมและระยะห่าง

เมื่อเว็บไซต์มีหลายหน้า การกำหนดสี ระยะห่าง และความกว้างเนื้อหาไว้ใน CSS Variables ช่วยให้ปรับระบบดีไซน์ได้จากจุดเดียว นอกจากนี้ยังช่วยลดความคลาดเคลื่อนของรูปแบบระหว่างคอมโพเนนต์ และทำให้การแก้ไขธีมในอนาคตสะดวกขึ้น
:root {
  --content-max: 1120px;
  --space-sm: 0.75rem;
  --space-md: 1rem;
  --space-lg: 1.5rem;
  --space-xl: 2rem;
  --surface: #ffffff;
  --page-background: #f1f5f9;
  --text-primary: #172033;
  --text-muted: #64748b;
  --brand: #2563eb;
}

.container {
  width: min(100% - 2rem, var(--content-max));
  margin-inline: auto;
}

.section {
  padding-block: clamp(2rem, 5vw, 4.5rem);
}

.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
  gap: var(--space-lg);
}

12. ตารางเปรียบเทียบแนวทาง Responsive

เทคนิคเหมาะกับข้อควรพิจารณา
Mobile Firstหน้าเว็บที่ต้องรองรับอุปกรณ์หลากหลายเริ่มจากเนื้อหาสำคัญและพื้นที่จำกัด
Media Queriesการเปลี่ยนเลย์เอาต์ตาม Viewportกำหนด Breakpoint จากปัญหาจริง
CSS clamp()ตัวอักษรและระยะห่างแบบยืดหยุ่นกำหนดค่าต่ำสุดและสูงสุดให้เหมาะสม
CSS Gridการแบ่งพื้นที่เป็นแถวและคอลัมน์ใช้ minmax() เพื่อลดปัญหาคอลัมน์ล้น
Flexboxการจัดองค์ประกอบตามแกนเดียวกำหนด flex-wrap เมื่อจำเป็น
Container Queriesคอมโพเนนต์ที่อยู่ในคอนเทนเนอร์หลายขนาดต้องกำหนด Query Container ให้ถูกต้อง
Responsive Imagesภาพประกอบ แบนเนอร์ และภาพสินค้าคำนึงถึงอัตราส่วนและขนาดไฟล์

13. ตัวอย่างเต็ม: Responsive Product Landing Page

ตัวอย่างต่อไปนี้เป็นหน้า HTML แบบเต็ม สามารถบันทึกเป็น `index.html` แล้วเปิดด้วยเบราว์เซอร์ได้ทันที หน้าเว็บใช้ CSS Grid สำหรับการ์ดสินค้า ใช้ Flexbox สำหรับเมนูและปุ่ม ใช้ `clamp()` สำหรับขนาดหัวข้อ และ Media Queries สำหรับปรับการจัดวางเมื่อหน้าจอแคบลง ข้อมูลทั้งหมดเป็นข้อมูลตัวอย่างสำหรับฝึกฝน
<!DOCTYPE html>
<html lang="th">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="description" content="ตัวอย่างหน้า Landing Page ที่รองรับหน้าจอหลายขนาด">
  <title>Responsive Product Landing Page</title>
  <style>
    * { box-sizing: border-box; }
    :root {
      --brand: #2563eb;
      --brand-dark: #1d4ed8;
      --ink: #172033;
      --muted: #64748b;
      --page: #f8fafc;
      --surface: #ffffff;
      --border: #e2e8f0;
      --radius: 18px;
    }
    body {
      margin: 0;
      font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
      color: var(--ink);
      background: var(--page);
      line-height: 1.6;
    }
    a { color: inherit; }
    .container {
      width: min(100% - 32px, 1120px);
      margin-inline: auto;
    }
    .site-header {
      position: relative;
      background: var(--surface);
      border-bottom: 1px solid var(--border);
    }
    .nav {
      display: flex;
      align-items: center;
      justify-content: space-between;
      flex-wrap: wrap;
      gap: 12px 24px;
      padding-block: 16px;
    }
    .brand {
      margin: 0;
      font-size: 1.25rem;
      font-weight: 800;
      letter-spacing: -0.03em;
    }
    .brand span { color: var(--brand); }
    .nav-links {
      display: flex;
      align-items: center;
      flex-wrap: wrap;
      gap: 8px;
      margin: 0;
      padding: 0;
      list-style: none;
    }
    .nav-links a {
      display: inline-block;
      padding: 9px 12px;
      color: var(--muted);
      text-decoration: none;
      border-radius: 9px;
    }
    .nav-links a:hover,
    .nav-links a:focus-visible {
      color: var(--brand-dark);
      background: #eff6ff;
    }
    .button {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      min-height: 44px;
      padding: 11px 18px;
      border: 1px solid transparent;
      border-radius: 11px;
      background: var(--brand);
      color: #fff;
      font: inherit;
      font-weight: 700;
      text-decoration: none;
      cursor: pointer;
      transition: background .2s ease, transform .2s ease;
    }
    .button:hover { background: var(--brand-dark); transform: translateY(-1px); }
    .button:focus-visible,
    a:focus-visible { outline: 3px solid #93c5fd; outline-offset: 3px; }
    .button--secondary {
      background: #fff;
      color: var(--brand-dark);
      border-color: #bfdbfe;
    }
    .button--secondary:hover { background: #eff6ff; }
    .hero {
      display: grid;
      grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr);
      align-items: center;
      gap: clamp(24px, 5vw, 64px);
      padding-block: clamp(48px, 8vw, 96px);
    }
    .eyebrow {
      display: inline-block;
      margin-bottom: 14px;
      padding: 6px 10px;
      border-radius: 999px;
      background: #dbeafe;
      color: #1d4ed8;
      font-size: .85rem;
      font-weight: 800;
    }
    .hero h1 {
      max-width: 700px;
      margin: 0;
      font-size: clamp(2rem, 1.1rem + 3vw, 3.8rem);
      line-height: 1.13;
      letter-spacing: -.04em;
    }
    .hero-copy {
      max-width: 620px;
      margin: 20px 0 0;
      color: var(--muted);
      font-size: clamp(1rem, .95rem + .35vw, 1.2rem);
    }
    .hero-actions {
      display: flex;
      flex-wrap: wrap;
      gap: 12px;
      margin-top: 26px;
    }
    .hero-visual {
      display: grid;
      place-items: center;
      min-height: 280px;
      padding: clamp(24px, 5vw, 48px);
      border-radius: 28px;
      background: linear-gradient(135deg, #dbeafe, #e0e7ff 55%, #f5d0fe);
    }
    .visual-card {
      width: min(100%, 340px);
      padding: 24px;
      border: 1px solid rgba(255,255,255,.8);
      border-radius: 20px;
      background: rgba(255,255,255,.92);
      box-shadow: 0 18px 50px rgba(30,64,175,.12);
    }
    .visual-card small { color: var(--muted); }
    .visual-card strong { display: block; margin-block: 8px; font-size: 2rem; }
    .visual-bar { height: 10px; margin-top: 16px; border-radius: 99px; background: #dbeafe; overflow: hidden; }
    .visual-bar span { display: block; width: 76%; height: 100%; border-radius: inherit; background: var(--brand); }
    .section { padding-block: clamp(40px, 6vw, 72px); }
    .section-heading { max-width: 720px; margin-bottom: 28px; }
    .section-heading h2 { margin: 0; font-size: clamp(1.6rem, 1.2rem + 1.5vw, 2.5rem); line-height: 1.2; }
    .section-heading p { margin: 12px 0 0; color: var(--muted); }
    .product-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(min(100%, 245px), 1fr));
      gap: 18px;
    }
    .product-card {
      display: flex;
      flex-direction: column;
      min-width: 0;
      padding: 22px;
      border: 1px solid var(--border);
      border-radius: var(--radius);
      background: var(--surface);
      box-shadow: 0 4px 16px rgba(15,23,42,.03);
    }
    .product-icon {
      display: grid;
      place-items: center;
      width: 48px;
      height: 48px;
      margin-bottom: 16px;
      border-radius: 14px;
      background: #eff6ff;
      font-size: 1.5rem;
    }
    .product-card h3 { margin: 0; font-size: 1.15rem; }
    .product-card p { margin: 10px 0 18px; color: var(--muted); }
    .product-card .button { align-self: flex-start; margin-top: auto; }
    .feature-band {
      display: grid;
      grid-template-columns: repeat(3, minmax(0, 1fr));
      gap: 18px;
      padding: 24px;
      border-radius: var(--radius);
      background: #172554;
      color: #fff;
    }
    .feature-band h3 { margin: 0 0 8px; }
    .feature-band p { margin: 0; color: #cbd5e1; font-size: .95rem; }
    .cta {
      display: flex;
      align-items: center;
      justify-content: space-between;
      flex-wrap: wrap;
      gap: 20px;
      padding: clamp(24px, 5vw, 44px);
      border: 1px solid #bfdbfe;
      border-radius: 22px;
      background: #eff6ff;
    }
    .cta h2 { margin: 0; font-size: clamp(1.4rem, 1.1rem + 1vw, 2rem); }
    .cta p { margin: 8px 0 0; color: var(--muted); }
    .site-footer { padding-block: 28px; border-top: 1px solid var(--border); color: var(--muted); font-size: .92rem; }
    .footer-inner { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 12px; }
    @media (max-width: 760px) {
      .hero { grid-template-columns: minmax(0, 1fr); }
      .hero-visual { min-height: 230px; }
      .feature-band { grid-template-columns: minmax(0, 1fr); }
      .nav { align-items: flex-start; }
      .nav-links { width: 100%; }
    }
    @media (max-width: 480px) {
      .container { width: min(100% - 24px, 1120px); }
      .nav-links { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); width: 100%; }
      .nav-links a { display: block; padding-inline: 8px; }
      .hero-actions { flex-direction: column; }
      .hero-actions .button { width: 100%; }
      .cta .button { width: 100%; }
    }
    @media (prefers-reduced-motion: reduce) {
      *, *::before, *::after { scroll-behavior: auto !important; transition-duration: .01ms !important; }
    }
  </style>
</head>
<body>
  <header class="site-header">
    <div class="container nav">
      <p class="brand">Level<span>Lance</span></p>
      <nav aria-label="เมนูหลัก">
        <ul class="nav-links">
          <li><a href="#products">บริการ</a></li>
          <li><a href="#features">จุดเด่น</a></li>
          <li><a href="#contact">ติดต่อ</a></li>
        </ul>
      </nav>
    </div>
  </header>

  <main>
    <section class="container hero">
      <div>
        <span class="eyebrow">แพลตฟอร์มตัวอย่างสำหรับธุรกิจ</span>
        <h1>เริ่มต้นธุรกิจออนไลน์ด้วยเว็บไซต์ที่พร้อมเติบโต</h1>
        <p class="hero-copy">จัดระเบียบสินค้า นำเสนอข้อมูลบริการ และสร้างประสบการณ์ใช้งานที่เหมาะกับลูกค้าทั้งบนมือถือและคอมพิวเตอร์</p>
        <div class="hero-actions">
          <a class="button" href="#products">สำรวจบริการ</a>
          <a class="button button--secondary" href="#features">ดูรายละเอียด</a>
        </div>
      </div>
      <div class="hero-visual" aria-label="ตัวอย่างแผงข้อมูลธุรกิจ">
        <div class="visual-card">
          <small>ภาพรวมประสิทธิภาพตัวอย่าง</small>
          <strong>76%</strong>
          <span>ความคืบหน้าตามเป้าหมาย</span>
          <div class="visual-bar" role="progressbar" aria-label="ความคืบหน้า" aria-valuemin="0" aria-valuemax="100" aria-valuenow="76"><span></span></div>
        </div>
      </div>
    </section>

    <section class="section" id="products">
      <div class="container">
        <div class="section-heading">
          <h2>เลือกเครื่องมือให้เหมาะกับธุรกิจ</h2>
          <p>ตัวอย่างการ์ดบริการที่ปรับจำนวนคอลัมน์โดยอัตโนมัติตามพื้นที่ที่มี</p>
        </div>
        <div class="product-grid">
          <article class="product-card">
            <div class="product-icon" aria-hidden="true">🛍️</div>
            <h3>ระบบร้านค้าออนไลน์</h3>
            <p>จัดแสดงสินค้า หมวดหมู่ และรายละเอียดที่ช่วยให้ลูกค้าตัดสินใจได้ง่ายขึ้น</p>
            <a class="button button--secondary" href="#contact">ดูรายละเอียด</a>
          </article>
          <article class="product-card">
            <div class="product-icon" aria-hidden="true">📊</div>
            <h3>ระบบจัดการธุรกิจ</h3>
            <p>รวบรวมข้อมูลสำคัญไว้ในพื้นที่เดียวเพื่อช่วยติดตามงานประจำวัน</p>
            <a class="button button--secondary" href="#contact">ดูรายละเอียด</a>
          </article>
          <article class="product-card">
            <div class="product-icon" aria-hidden="true">💬</div>
            <h3>ระบบสื่อสารกับลูกค้า</h3>
            <p>จัดระเบียบช่องทางติดต่อและช่วยให้ทีมตอบคำถามได้เป็นระบบ</p>
            <a class="button button--secondary" href="#contact">ดูรายละเอียด</a>
          </article>
        </div>
      </div>
    </section>

    <section class="section" id="features">
      <div class="container">
        <div class="feature-band">
          <div>
            <h3>📱 รองรับมือถือ</h3>
            <p>จัดลำดับเนื้อหาและปุ่มให้ใช้งานสะดวกบนหน้าจอขนาดเล็ก</p>
          </div>
          <div>
            <h3>🧩 โครงสร้างยืดหยุ่น</h3>
            <p>ใช้ Grid และ Flexbox เพื่อจัดวางส่วนต่าง ๆ ตามหน้าที่</p>
          </div>
          <div>
            <h3>♿ ใช้งานเข้าถึงได้</h3>
            <p>มีลำดับหัวข้อที่ชัดเจนและสถานะ Focus สำหรับคีย์บอร์ด</p>
          </div>
        </div>
      </div>
    </section>

    <section class="section" id="contact">
      <div class="container">
        <div class="cta">
          <div>
            <h2>พร้อมต่อยอดเว็บไซต์ของคุณแล้วหรือยัง?</h2>
            <p>ส่วนนี้เป็นตัวอย่าง Call to Action สามารถเปลี่ยนข้อความและลิงก์ให้ตรงกับโครงการจริงได้</p>
          </div>
          <a class="button" href="mailto:[email protected]">ติดต่อทีมงาน</a>
        </div>
      </div>
    </section>
  </main>

  <footer class="site-footer">
    <div class="container footer-inner">
      <span>© 2026 LevelLance — ตัวอย่างเพื่อการเรียนรู้</span>
      <span>HTML + CSS Responsive Layout</span>
    </div>
  </footer>
</body>
</html>

14. ตรวจสอบ Responsive ก่อนเผยแพร่เว็บไซต์

ก่อนนำเว็บไซต์ขึ้นใช้งานจริง ควรตรวจสอบความกว้างหลายระดับ ทดลองหมุนหน้าจอ ตรวจสอบข้อความยาว ภาพที่มีสัดส่วนต่างกัน ปุ่มและลิงก์บนหน้าจอสัมผัส รวมถึงการใช้คีย์บอร์ด หากมีฟอร์ม ตาราง หรือหน้าต่างโต้ตอบ ต้องตรวจสอบองค์ประกอบเหล่านั้นแยกต่างหากด้วย
ควรทดสอบที่ความกว้างเท่าไรบ้าง?
เริ่มจากหน้าจอแคบประมาณ 320–375 พิกเซล ทดสอบช่วงกลางประมาณ 600–800 พิกเซล และทดสอบจอเดสก์ท็อปที่กว้างขึ้น ค่าที่ใช้เป็นเพียงแนวทาง ควรเพิ่มการทดสอบที่ขนาดซึ่งเลย์เอาต์เริ่มมีปัญหาด้วย และอย่าลืมตรวจสอบการซูมข้อความ
ทำไมเว็บไซต์ยังล้นทั้งที่กำหนด width: 100% แล้ว?
ตรวจสอบ Padding, Border, min-width, รูปภาพที่มีขนาดคงที่ และลูกของ Grid หรือ Flexbox ที่มีขนาดขั้นต่ำใหญ่เกินไป การใช้ box-sizing: border-box ช่วยให้การคำนวณขนาดเข้าใจง่ายขึ้น แต่ยังต้องตรวจสอบองค์ประกอบภายในและเนื้อหาที่ยาวเป็นพิเศษ
ควรใช้ Media Queries หรือ Container Queries?
ใช้ Media Queries เมื่อการเปลี่ยนรูปแบบขึ้นอยู่กับขนาด Viewport เช่น การเปลี่ยนโครงสร้างหน้าเว็บโดยรวม ใช้ Container Queries เมื่อคอมโพเนนต์ควรปรับตัวตามพื้นที่ของกล่องที่ครอบอยู่ เช่น การ์ดที่นำไปใช้ในหลายคอลัมน์หรือแผงด้านข้าง ทั้งสองเทคนิคสามารถใช้ร่วมกันได้
ต้องเขียน CSS แยกทุกขนาดหน้าจอหรือไม่?
ไม่จำเป็น ควรเริ่มจาก Layout ที่ยืดหยุ่นด้วย Grid, Flexbox, minmax(), auto-fit, auto-fill และ clamp() ก่อน แล้วเพิ่ม Media Queries เฉพาะจุดที่เนื้อหาต้องเปลี่ยนโครงสร้างจริง วิธีนี้ช่วยลดจำนวนกฎ CSS และทำให้แก้ไขได้ง่ายขึ้น

15. แบบฝึกหัดท้ายบท

นำหน้า Product Landing Page จากตัวอย่างไปปรับใช้กับหัวข้อธุรกิจที่คุณสนใจ โดยต้องมี Header, Navigation, Hero Section, การ์ดอย่างน้อยสามใบ และส่วน Call to Action จากนั้นปรับให้การ์ดเรียงหนึ่งคอลัมน์บนมือถือและหลายคอลัมน์บนจอใหญ่ ทดลองใช้ `clamp()` กับหัวข้อ ตรวจสอบภาพที่มีอัตราส่วนต่างกัน และทดสอบด้วยข้อความที่ยาวกว่าตัวอย่าง
🎯 เกณฑ์ตรวจงานหน้าเว็บต้องไม่เกิดแถบเลื่อนแนวนอนโดยไม่จำเป็น เนื้อหาสำคัญต้องอ่านได้ทุกขนาด ปุ่มต้องเข้าถึงได้ด้วยคีย์บอร์ด รูปภาพต้องไม่ล้นคอนเทนเนอร์ และการ์ดต้องจัดวางใหม่อย่างเหมาะสมโดยไม่เกิดการซ้อนทับ

16. สรุปบทเรียน EP12

Responsive Design ที่ดีเกิดจากการวางโครงสร้างเนื้อหาอย่างรอบคอบและใช้เครื่องมือ CSS ให้เหมาะสม คุณได้เรียนรู้แนวคิด Mobile First การเลือก Breakpoints จากเนื้อหา การปรับขนาดด้วย clamp() การใช้ Grid และ Flexbox การแสดงภาพแบบ Responsive การใช้ Container Queries และการป้องกันองค์ประกอบล้นหน้าจอ เมื่อนำเทคนิคเหล่านี้มารวมกัน คุณจะสร้างเว็บไซต์ที่ยืดหยุ่นและดูแลรักษาได้ง่ายขึ้น
เว็บไซต์ที่ยืดหยุ่นไม่ได้เพียงย่อขนาดตามหน้าจอ แต่จัดลำดับประสบการณ์ใหม่ให้เหมาะกับพื้นที่และความต้องการของผู้ใช้
อ่านจนถึงระยะ 500px ก่อนท้ายหน้าและอยู่ในหน้านี้อย่างน้อย 5 นาที เพื่อรับ EXP วันละครั้ง

⏳ กำลังจับเวลาอ่าน...

🌐
เลือกภาษา / Select Language
21 World Languages Supported