Knowledge Hub

Design System - Reference UI

ข้อกำหนดการออกแบบฉบับรวมจากการตรวจเว็บ reference ครอบคลุมโครงหน้าจอ 25 route และ template 8 แบบ, design token, component 37 ชิ้น, interaction, accessibility และข้อจำกัดของการตรวจ ใช้เป็นแบบตั้งต้นสร้าง UI ใหม่ที่มี UX คล้ายกันแต่ใช้งานออกแบบของตัวเอง

UI Reverse Engineeringอ่าน 35 นาทีเผยแพร่ 14 ตุลาคม 2569

วิธีอ่านเอกสาร

เอกสารนี้รวม Design Spec, Component Inventory, Page Inventory และ Reference Audit เป็นฉบับเดียว ทุกค่าติดป้ายบอกที่มา เพื่อแยกสิ่งที่วัดจริงออกจากข้อเสนอ

ข้อมูลมาจากการตรวจเว็บ reference เมื่อ 2026-10-12 ที่ 3 ความกว้าง (1440, 820, 390) ด้วย REA 6.4.0 บน Chrome 155 headless ไม่ได้แตะ source code ของแอปใดเลย

Note ชื่อแบรนด์ สี ภาพ และข้อความของ reference เป็นของเจ้าของเว็บ เอกสารนี้ใช้เรียนรู้รูปแบบ UX เท่านั้น งานที่สร้างต้องใช้ชื่อ สี โลโก้ ภาพ และ copy ของตัวเอง

ป้าย ความหมาย
O Observed: วัดจาก DOM / computed style ของ reference
I Inferred: สรุปจากโครงสร้างหรือ class
R Recommended: ข้อเสนอของเราสำหรับงานใหม่ ไม่ใช่ค่าของ reference
U Unverified: ยังไม่ได้ยืนยัน ต้องตรวจด้วยมือ (ดูส่วนท้ายเอกสาร)
Add ข้อกำหนดที่ reference ไม่มีหรือไม่ครบ ต้องเพิ่มในงานใหม่
  • ชื่อ component เป็นชื่อที่เราตั้ง ไม่ใช่ชื่อใน source ของ reference
  • ค่าสีของ reference ในเอกสารนี้เป็นตัวอย่างโครงสร้าง token เท่านั้น ให้แทนที่ด้วย palette ของแบรนด์ใหม่ โดยคง role ของแต่ละ token ไว้

ภาพรวมสั้น

สิ่งที่ควรจำถ้าอ่านแค่ส่วนนี้

หัวข้อ สรุป
Visual language ขอบ 2px + hard shadow + ปุ่ม pill + card มุม 16px
Shell sidebar 288px (>= 1024px) หรือ header + tab bar + bottom sheet (< 1024px)
Token semantic token + 3 ธีม x light/dark ผ่าน data-theme และ .dark
Component 37 ชิ้นใน 5 ชั้น แกนที่ใช้ซ้ำคือ Button, Card (3 variant), NavLink, Badge
ลอกได้ โครง shell, bottom sheet, tab bar, ลำดับ block ของ hero และหน้าคอร์ส
ห้ามลอก / ต้องแก้ contrast ปุ่มส้ม 2.8:1, ไม่มี skip link, tap 24-28px, grid 205px
ประเภทเว็บ ขายคอร์สสอน AI แบบนัดสด 1:1 + คลังบทความ/คลิป/ebook + ระบบสนับสนุนเงิน
Stack ที่เห็นจาก bundle Next.js (Turbopack), React, Tailwind v4, host บน Vercel
หน้า 25 route สาธารณะ จัดเป็น template 8 แบบ (T1-T8)
ความครบของการตรวจ หน้าแรก, list/detail, auth, 404 ครบ 3 viewport. หน้าอื่นตรวจระดับ list
ความเชื่อมั่น token, breakpoint, ขนาด: สูง. focus, keyboard, loading/error: ยังไม่ตรวจ

ลำดับเอกสาร

ส่วน เนื้อหา ใช้ตอน
โครงหน้าจอ shell, breakpoint, ตำแหน่งจริงของหน้าแรก วางโครงหน้า
หน้าและ template route 25 หน้า, template T1-T8, งาน backend วางแผนหน้าและข้อมูล
Design token สี ธีม ตัวอักษร ระยะ ขอบ เงา ตั้งค่า CSS variable
Component 5 ชั้น 37 ชิ้น พร้อมขนาด variant state สร้างทีละชิ้น
Interaction และ state hover, active, loading, empty, error, motion ออกแบบพฤติกรรม
Accessibility contrast ที่คำนวณ และข้อกำหนด WCAG 2.2 AA ตรวจก่อนส่งงาน
Implement stack ที่แนะนำ เริ่มลงมือ
ข้อมูลทางเทคนิคและข้อจำกัด วิธีตรวจ, bundle, สมมติฐาน, สิ่งที่ยังไม่ยืนยัน ประเมินความน่าเชื่อถือ

โครงหน้าจอ

Shell สองแบบ

Shell เปลี่ยนที่ 1024px ฝั่ง desktop ใช้ sidebar ฝั่ง mobile ใช้ header บนกับ tab bar ล่าง

ช่วงกว้าง โครง
lg (>= 1024px) aside ซ้าย 288px sticky สูง 100vh เลื่อนในตัวเอง + main ที่เหลือ
< 1024px header sticky สูง 58px + main + tab bar ลอยล่าง + bottom sheet
Desktop (>= 1024px)                    Mobile (< 1024px)
+----------+----------------------+    +------------------------+
| Sidebar  |                      |    | MobileHeader (58px)    |
| 288px    |        main          |    +------------------------+
| sticky   |                      |    |                        |
| scroll   |                      |    |         main           |
| ในตัว    |                      |    |                        |
| Footer   |                      |    |  [ BottomTabBar 72px ] |
+----------+----------------------+    +------------------------+
                                        + BottomSheet (เปิดจาก "เมนู")

Breakpoint และ responsive

Breakpoint ที่วัดจริงด้วย viewport หลายความกว้าง (O) ตรงกับ breakpoint ของ Tailwind

Viewport Sidebar Header บน Tab bar ล่าง Grid คอร์ส
1024 แสดง ซ่อน ซ่อน 3 คอลัมน์ (การ์ดกว้าง 205px)
1023 ซ่อน แสดง แสดง 2 คอลัมน์
768 ซ่อน แสดง แสดง 2 คอลัมน์
640 ซ่อน แสดง แสดง 2 คอลัมน์
639 ซ่อน แสดง แสดง 1 คอลัมน์

Media / container query ทั้งหมดที่พบใน CSS: min-width 40rem, 48rem, 64rem, 80rem, 96rem, max-width: 600px, container query 42rem และ 80rem, hover: hover, hover: none and pointer: coarse, prefers-reduced-motion (3 รูปแบบ), print

ค่าของเนื้อหาในแต่ละช่วง

ส่วน < 640 640 - 1023 >= 1024
Grid คอร์ส 1 คอลัมน์ 2 3
Hero คอลัมน์เดียว, H1 30px คอลัมน์เดียว, H1 48px 2 คอลัมน์ (ข้อความ + price card)
Stat tiles 3 คอลัมน์, ตัวเลข 20px 3 3, กว้างสูงสุด 672px
Page padding 16px 24px 32px

Warning ที่ viewport 1024px แสดง 3 คอลัมน์ทั้งที่ sidebar กิน 288px การ์ดเหลือ 205px กว้างเกินไปสำหรับข้อความไทย งานใหม่ควรเปลี่ยนเป็น 3 คอลัมน์เมื่อ >= 1280px (container query ของเนื้อหา)

ลำดับสายตาของหน้าแรก

ลำดับจากบนลงล่าง (O): hero 2 คอลัมน์ (headline + stat, price card) -> แถบสนับสนุนพื้นรอง -> การ์ดความน่าเชื่อถือ 3 ใบ -> หัวข้อ "คอร์ส" พร้อมลิงก์ดูทั้งหมด -> grid คอร์ส 9 ใบ

ระดับ เทคนิคที่ใช้
1 (เด่นสุด) H1 หนา 900 ขนาดใหญ่ + คำสำคัญสีหลัก, price card ลอยด้วย hard shadow
2 CTA แบบ pill 2 แบบ (secondary ขาวขอบดำ, primary สีหลัก)
3 stat tile กรอบเรียบ (ไม่มีเงา) ต่างจาก card ที่มีเงา
4 การ์ดคอร์ส: ภาพ 96px, ชื่อ 18px, คำอธิบายตัด 3 บรรทัด, ราคา + ลิงก์
พื้นหลัง dot-grid, วงกลม/สี่เหลี่ยมมนตกแต่ง (aria-hidden), แถบเฉียงเหนือแถบสนับสนุน

ตำแหน่งจริงของหน้าแรก desktop 1440px

ส่วน x, y, กว้าง x สูง (O)
main x 288, กว้าง 1137 (เหลือ scrollbar 15px)
Hero section y 0, 1137 x 648, ขอบล่าง 2px
Hero H1 x 320, y 127, 673 x 152 (2 บรรทัด)
Price card x 1033, y 85, 360 x 339
แถบสนับสนุน y 648, 1137 x 85
ส่วนการ์ดความน่าเชื่อถือ y 733, สูง 304, การ์ด 344 x 192
Grid คอร์ส x 320, กว้าง 1073, การ์ด 344 x 291, ภาพปก 340 x 96

หน้าและ template

รายการ route จาก sitemap, เมนู และการเปิดทดสอบ 25 หน้าสาธารณะ จัดเป็น template 8 แบบ พร้อมระบุว่าอะไรต้องใช้ backend และอะไรทำฝั่ง frontend ได้

ขอบเขตและความมั่นใจ

รายการ ค่า
วิธีได้ route sitemap.xml, ลิงก์ใน DOM, string ใน bundle, เปิดจริงด้วย GET
หน้าที่เปิดสำเร็จ (HTTP 200) 25 (หน้าแรก + 24 หน้า)
หน้าที่ถ่าย screenshot 3 viewport หน้าแรก, courses (list+detail), tips, guides, login, register, community, 404
หน้าที่ดูแค่ HTML ฝั่ง server (curl) ที่เหลือ: จัดกลุ่ม template จากจำนวน heading และลิงก์ ไม่ได้ดูภาพ
Path ที่ robots.txt ห้าม (ไม่ได้เปิดดู) admin, api, account, schedule, my-courses, enrollments, learn, teach ฯลฯ

Note template ของหน้าที่ดูแค่ HTML เป็น I (สรุปจากโครงสร้าง) ให้เปิดดูจริงก่อนใช้เป็นแบบ

Route ทั้งหมด

คอลัมน์ Template อ้างรหัส T1-T8 จากหัวข้อ Template ที่ใช้ซ้ำ

เมนู "เรียน"

Route H1 (ย่อ) Template Auth ข้อมูลจาก
/courses เรียนสดตัวต่อตัว เลือกวันเวลาเอง T2 catalog ไม่ต้อง ฐานข้อมูลคอร์ส
/courses/[slug] ชื่อคอร์ส T3 course detail ดูได้ จองต้อง login คอร์ส + ตารางว่าง
/group-courses คอร์สสอนแบบกลุ่ม T5 marketing ไม่ต้อง เนื้อหา static
/workshop AI ใช้จริง เปลี่ยนงานให้เป็นเรื่องง่าย T5 marketing ไม่ต้อง เนื้อหา static

เมนู "อ่านและดู"

Route H1 (ย่อ) Template Auth ข้อมูลจาก
/tips เคล็ดลับที่ dev ใช้จริง T2 catalog (แบ่งหน้า) ไม่ต้อง บทความ
/tips/[slug] ชื่อบทความ T2b article (U) ไม่ต้อง บทความ
/guides สอนใช้เครื่องมือ AI ตั้งแต่ยังไม่เคยแตะ T2 catalog จัดกลุ่มเป็น series ไม่ต้อง บทความ
/guides/[slug] ชื่อคู่มือ T2b article (U) ไม่ต้อง บทความ
/watch เปิดคลิปไว้ข้าง ๆ แล้วทำตามไปพร้อมกัน T2 catalog วิดีโอ ไม่ต้อง รายการวิดีโอ
/shorts เรื่องเดียวจบใน 1 นาที T2 catalog วิดีโอสั้น ไม่ต้อง รายการวิดีโอ
/ebooks จ่ายครั้งเดียว ได้ของที่ยังโตต่อไปเรื่อย ๆ T2 catalog สินค้า ซื้อต้อง login (I) สินค้า
/feed ทุกโพสต์บนเพจ เก็บไว้ที่นี่แบบเต็ม T2 catalog โพสต์ ไม่ต้อง โพสต์
/stories คลังคลิปเรื่องเล่า T2 catalog วิดีโอ ไม่ต้อง วิดีโอ

เมนู "ลงมือ"

Route H1 (ย่อ) Template Auth ข้อมูลจาก
/projects โปรเจคพร้อมโค้ด T2 catalog (การ์ด 7 ใบ) ไม่ต้อง รายการโปรเจค
/prompt-lab คลิป AI ไวรัล พร้อมพรอมต์ T2 catalog ไม่ต้อง รายการ
/one-prompt หัวข้อภาษาอังกฤษแบบ landing T5 marketing + ตัวอย่าง ไม่ต้อง รายการ
/playground เดโมลองเล่น T2 catalog เดโม 14 รายการ ไม่ต้อง รายการเดโม
/behind-the-pixels หลังฉากภาพ AI T2 gallery (ไม่มี heading ย่อย) ไม่ต้อง ภาพ (ไฟล์ HTML 589KB)

เมนู "อื่น ๆ" และหน้าระบบ

Route H1 (ย่อ) Template Auth ข้อมูลจาก
/community ถาม-ตอบ T7 community list อ่านได้ โพสต์ต้อง login โพสต์ + ไลก์
/community/[id] หัวข้อกระทู้ T7b thread (U) อ่านได้ โพสต์ (UUID)
/shop ของที่ dev คัดมาแนะนำ T2 catalog สินค้าแนะนำ ไม่ต้อง รายการสินค้า
/supporters ผู้สนับสนุน T5 marketing + รายชื่อ ไม่ต้อง รายชื่อผู้สนับสนุน
/promise จ่ายแล้วได้อะไรกลับไป T6 prose (7 หัวข้อ) ไม่ต้อง เนื้อหา static
/terms ข้อกำหนดการใช้งาน T6 prose ไม่ต้อง เนื้อหา static
/privacy นโยบายความเป็นส่วนตัว T6 prose ไม่ต้อง เนื้อหา static
/login เข้าสู่ระบบ T4 auth ไม่ต้อง -
/register สมัครสมาชิก T4 auth ไม่ต้อง -
path ที่ไม่มี 404 T8 not found - -

Query และ hash ที่พบ

รูปแบบ ใช้ทำอะไร
/login?next=%2Fschedule กลับหน้าเดิมหลัง login (ค่าที่เห็นคือ /schedule)
/register?next=... เหมือนกัน
/community?course=[slug] เปิดบอร์ดถาม-ตอบกรองตามคอร์ส (ลิงก์จากหน้าคอร์ส)
/courses/[slug]#enroll เลื่อนไปการ์ดจอง
/supporters#support เลื่อนไปฟอร์มสนับสนุน
/#courses เลื่อนไปส่วนคอร์สบนหน้าแรก
/tips?page=N แบ่งหน้า (พบลิงก์ /tips?page=2 ใน HTML)

ลำดับชั้นของหน้า

/ (home)
|-- เรียน
|   |-- /courses --> /courses/[slug] --> (จอง, ต้อง login) --> /schedule
|   |-- /group-courses
|   `-- /workshop
|-- อ่านและดู
|   |-- /tips --> /tips/[slug]
|   |-- /guides --> /guides/[slug]
|   |-- /watch, /shorts, /stories
|   |-- /ebooks
|   `-- /feed
|-- ลงมือ
|   |-- /projects, /prompt-lab, /one-prompt
|   |-- /playground
|   `-- /behind-the-pixels
|-- อื่น ๆ
|   |-- /community --> /community/[id]
|   |-- /shop
|   `-- /supporters
|-- ข้อมูลและกฎ: /promise, /terms, /privacy
`-- บัญชี: /login, /register

Template ที่ใช้ซ้ำ

ID ชื่อ หน้าที่ใช้ โครงหลัก
T1 Home / landing / hero 2 คอลัมน์, แถบสนับสนุน, การ์ดความน่าเชื่อถือ, grid คอร์ส
T2 Catalog courses, tips, guides, watch, shorts, feed, shop ฯลฯ (12 หน้า) H1 + lead, grid การ์ด, แบ่งหน้า, บล็อกสินค้า, บล็อกสนับสนุน
T3 Course detail /courses/[slug] ลิงก์ย้อนกลับ, hero + price, วิดีโอ, การ์ดจอง, เนื้อหา, FAQ
T4 Auth /login, /register คอลัมน์กลาง, ฟอร์ม, ปุ่ม OAuth, ลิงก์สลับหน้า
T5 Marketing /group-courses, /workshop, /one-prompt, /supporters หัวข้อใหญ่, section สลับ, CTA
T6 Prose / legal /promise, /terms, /privacy H1 + H2 หลายหัวข้อ ข้อความยาว
T7 Community /community H1, รายการกระทู้, ปุ่มถูกใจ, ลิงก์ login/สมัคร
T8 Not found path ที่ไม่มี ค่า default ของ framework (ข้อความอังกฤษ) ใน shell

โครง T1 Home (O)

ลำดับ Block หมายเหตุ
1 Hero (ซ้าย: badge, eyebrow, H1, lead, 2 CTA, 3 stat; ขวา: price card) สูง 648px desktop
2 แถบสนับสนุนพื้นรอง + แถบเฉียงด้านบน 2 ปุ่มเล็ก
3 การ์ดความน่าเชื่อถือ 3 ใบ (ไอคอน, หัวข้อ, ข้อความ) 2 ใน 3 ใบเป็นลิงก์
4 หัวข้อ section + ลิงก์ดูทั้งหมด -
5 Grid การ์ดคอร์ส 9 ใบ 1/2/3 คอลัมน์
6 Footer (mobile) หรือท้าย sidebar (desktop) ลิงก์นโยบาย + เวอร์ชัน

โครง T2 Catalog (O, ตัวอย่าง /tips)

ลำดับ Block หมายเหตุ
1 H1 + lead + badge หมวดที่ active -
2 Grid การ์ดบทความ 24 ใบ/หน้า (3 คอลัมน์ desktop) ภาพปก, tag, ชื่อ, สรุป, เวลาอ่าน, จำนวนอ่าน
3 แถบแบ่งหน้า: ก่อนหน้า, "หน้า 1 จาก 2", ถัดไป ปุ่มก่อนหน้าดูจางในหน้าแรก (U ว่า disabled จริง)
4 บล็อกสินค้าแนะนำ 3 ใบ ภาพสินค้า, ราคา, ปุ่มไปร้านค้า
5 บล็อกสนับสนุน: จำนวนเงิน, ชื่อ, QR, ลิงก์ ซ้ำกับ /courses, /guides

โครง T3 Course detail (O, ตัวอย่างคอร์สหนึ่ง)

ลำดับ Block
1 ลิงก์ย้อนกลับ + ปุ่มแชร์
2 Badge, H1, lead, ราคา, CTA เลื่อนไปจอง; ขวา: การ์ดภาพ + ข้อดี 3 ข้อ
3 วิดีโอตัวอย่าง (ภาพ thumbnail + ปุ่มเล่น) + ไทล์จุดขาย 3 ใบ
4 Chip สรุป (ราคา, รูปแบบ, ระยะเวลา, ระดับ)
5 การ์ดจอง: แพ็กเกจ, หัวข้อ (combobox), ราคา, วัน, เวลา, โค้ดส่วนลด, ปุ่มยืนยัน
6 การ์ดรับประกัน + ลิงก์ไปหน้าเงื่อนไข
7 ผลลัพธ์หลังเรียน (2 คอลัมน์), รายละเอียด, สิ่งที่จะได้เรียน 6 ข้อ
8 เหมาะกับใคร / ในคอร์สมีอะไร (2 คอลัมน์), ผู้สอน, FAQ
9 รายการหัวข้อ 30 ข้อ (2 คอลัมน์) + ลิงก์ไปถาม-ตอบของคอร์ส

โครง T4 Auth

Block ค่า (O)
คอลัมน์ กลาง กว้างราว 704px (x 512-1216 ที่ 1440px)
H1 ชิดซ้ายในคอลัมน์
ฟอร์ม card ขอบหนา, label ตัวหนา 14px, input สูง 48px
ปุ่มหลัก เต็มความกว้าง pill สีหลัก
ตัวคั่น เส้นพร้อมคำว่า "หรือ"
OAuth ปุ่มขาว pill พร้อมโลโก้ Google
ลิงก์สลับ ท้ายฟอร์ม พก next
field login: อีเมล + รหัสผ่าน. สมัคร: ชื่อ + อีเมล + รหัสผ่าน

ต้องใช้ backend หรือ frontend ล้วน

ความสามารถ ฝั่ง เหตุผล
Shell, เมนู, sheet, สลับธีม, dark mode Frontend เก็บ localStorage
Cookie consent Frontend (+ backend ถ้าต้องบันทึกหลักฐาน) consent-storage ในเบราว์เซอร์
แสดงหน้า catalog / บทความ Frontend + ไฟล์/CMS อ่านอย่างเดียว render ฝั่ง server ได้
ค้นหา Backend พบ /api/search, ต้อง index เนื้อหา
สมัคร / เข้าสู่ระบบ / OAuth Google Backend พบ /api/auth
จองคาบเรียน (วัน/เวลา/โค้ด) Backend ต้องเช็คเวลาว่าง, โค้ดส่วนลด, สร้างรายการจอง
ชำระเงิน Backend + ผู้ให้บริการ U: ไม่เห็น flow ชำระเงินเพราะต้อง login
โพสต์ / ไลก์ใน community Backend ต้องมีบัญชีและตรวจสิทธิ์
ระบบสนับสนุนเงิน, รายชื่อผู้สนับสนุน Backend บันทึกยอด และอนุมัติรายชื่อ
ตัวนับผู้เรียน / จำนวนอ่าน Backend ตัวเลขเปลี่ยนตามเวลา
หน้าหลัง login (/schedule, /my-courses, /learn) Backend ต้องมีบัญชี ห้ามสำรวจตาม robots
Share, ลิงก์ภายนอก (ร้านค้า) Frontend แค่ลิงก์

หน้าที่ต้องเปิดดูเพิ่มก่อนใช้เป็นแบบ

หน้า เหตุผล วิธีตรวจ
/tips/[slug], /guides/[slug] ยังไม่รู้ layout บทความ (TOC, โค้ดบล็อก, ปุ่มแชร์) เปิดจริง 1 หน้า
/community/[id] ยังไม่รู้ layout กระทู้และกล่องตอบ เปิดจริง 1 หน้า
/watch, /shorts, /stories ไม่รู้ player / modal วิดีโอ เปิดและกดเล่น
/ebooks ไม่รู้ flow ซื้อ ดูหน้า ไม่กดซื้อ
ค้นหา ไม่ได้ทดสอบผลลัพธ์ / ว่าง / error พิมพ์ 2 ตัวอักษรและจับ network

Design token

โครง token แบบ semantic

Reference ใช้ CSS variable บน html[data-theme] และ class .dark ซ้อนกัน 3 ธีม x 2 โหมด

Token Role ตัวอย่าง O (Bold, light)
--background พื้นหน้าเว็บ #fefce8
--foreground ข้อความหลัก #1c1917
--card พื้น card / dialog / input #ffffff
--muted ข้อความรอง #78716c
--muted-surface พื้นแถบ / chip รอง #fff7ed
--border เส้นขอบ 2px ทุกจุด #15161c
--ring focus ring #fb923c
--brand-50..900 สเกลสีหลัก 10 ขั้น 500 = #f97316, 700 = #c2410c
--accent-100..800 สีเน้นรอง 500 = #f43f5e
--on-brand ตัวอักษรบนสีหลัก #ffffff
--success/warning/error/info และ -surface สถานะ success #15803d บน #dcfce7
--code-surface / --code-foreground บล็อกโค้ด #0d1117 / #e6edf3

ค่าของแต่ละธีม

ธีม Brand 500 Background light Background dark Border dark
Bold #f97316 #fefce8 #0f0a05 #78350f
Ocean #0891b2 #f3fbfd #06171c #204551
Grape #7c3aed #faf6ff #120b1f #3a2c5c
  • ใน dark mode สเกล brand-50..300 กลายเป็นสีมืดและ brand-700..900 กลายเป็นสีสว่าง (สลับด้านของสเกล) เพื่อให้ class เดิมใช้ได้ทั้งสองโหมด ตรงนี้เป็นวิธีที่ควรเลียนแบบ
  • พบกฎ html[data-theme] อีกชุดที่บังคับเป็นสีกระดาษ (--doc-paper #fff, --doc-ink #16181d) ข้อสรุป (I): เป็น stylesheet สำหรับ print เพราะพบ @media print ใน CSS

ตัวอักษร

Font (O): Noto Sans Thai (body) และ Exo 2 600/700 (wordmark, label กลุ่ม)

Role ขนาด / line-height น้ำหนัก Tracking
Hero H1 (sm ขึ้นไป) 48px / 60px 900 -1.2px
Hero H1 (mobile) 30px / 37.5px 900 -0.75px
Eyebrow 16px (mobile 14px) 800, uppercase 0.1em
Section H2 30px / 36px 900 -0.75px
Card title H3 18px / 24.75px 800 ปกติ
Lead 18px / 29.25px 400 ปกติ
Body 16px / 24px 400 ปกติ
Small / nav / card desc 14px / 20px 400-600 ปกติ
Caption / stat label 12px / 16.5px 400-700 ปกติ
Nav group label 10.88px / 16.3px 700, uppercase 0.025em
Stat number 24px (mobile 20px) 900 ปกติ

ราคาและตัวเลข stat ใช้น้ำหนัก 900 กับสีหลักหรือสี foreground

ระยะ ขอบ มุม เงา

ฐาน spacing 4px (--spacing: .25rem) ทุกค่าด้านล่างเป็น O

รายการ ค่า
Page padding แนวนอน 16px (mobile), 24px (sm), 32px (lg)
Section padding แนวตั้ง 56px
Hero padding แนวตั้ง 56px (mobile), 80px (sm ขึ้นไป)
Gap grid การ์ด 20px
Gap stat tile 12px
Padding card หลัก 24px (price card), 20px (เนื้อหาใน course card)
Border ทั่วไป 2px solid --border
Radius card 16px
Radius input / ลิงก์ในเมนู 12px
Radius ปุ่ม / badge / chip 9999px (pill)
Radius tab bar ล่าง 26px
Radius bottom sheet (บน) 24px
Shadow ค่า (O)
Card 4px 4px 0 0 สี --border (hard shadow ไม่เบลอ)
Card hover 6px 6px 0 0 และ translate(-2px,-2px)
ปุ่ม 3px 3px 0 0 สี --border
Tab bar ล่าง Tailwind shadow-lg
Bottom sheet 0 25px 50px -12px rgba(0,0,0,.25)
Header บน (mobile) ไม่มีเงา ใช้ border-bottom: 2px และ backdrop-filter: blur(8px)

Recommendation Hard shadow + ขอบหนา คือ "ลายเซ็น" ของ reference ถ้าต้องการให้คนไม่เห็นว่าเลียนแบบ ให้เปลี่ยนความหนาขอบหรือระยะเงา และใช้ palette ใหม่

Component

37 component ใน 5 ชั้น แต่ละชิ้นมีขนาดที่วัดได้ state ที่เห็น และข้อกำหนดที่ต้องเพิ่ม ค่าสีอ้างเป็นชื่อ token จากส่วน Design token

ชั้น Component
Shell AppShell (รวม Footer), Sidebar, NavGroup, NavLink, MobileHeader, BottomTabBar
Overlay BottomSheet, SearchDialog, CookieBanner, Backdrop
ปุ่มและฟอร์ม Button, IconButton, ThemeSwitcher, Input, Combobox, Chip x2, OAuth, Divider
Card Card, CourseCard, ArticleCard, ProductCard, StatTile, PriceCard, TrustCard
เนื้อหา Hero, SectionHeader, Sponsor x2, Accordion, Checklist, Badge, Video, Pagination

ชั้น Shell

AppShell

รายการ ข้อกำหนด
หน้าที่ วาง Sidebar หรือ MobileHeader + BottomTabBar รอบ main
ขนาด (O) desktop: sidebar 288px + main. mobile: main เต็มจอ + padding ล่างเผื่อ tab bar
จุดเปลี่ยน (O) 1024px
Footer desktop (O) ท้าย sidebar: 4 ลิงก์ 12px + เวอร์ชัน
Footer mobile (O) footer ท้ายหน้า ลิงก์เดียวกัน padding ล่าง 128px เผื่อ tab bar
Add skip link เป็น element แรก, main มี id ให้ลิงก์ข้าม

Sidebar

รายการ ข้อกำหนด
ขนาด (O) กว้าง 288px, padding 20px 12px, ขอบขวา 2px, sticky top 0, สูง 100vh, เลื่อนในตัว
โครง (O) logo, ปุ่มเข้า/สมัคร, ค้นหา, 4 NavGroup, ธีม, ลิงก์นโยบาย, เวอร์ชัน
Logo (O) 40x40px + wordmark
ปุ่มเข้าสู่ระบบ / สมัคร (O) เต็มความกว้าง 239x44px, pill, ปุ่มสมัครใช้สีหลัก
ช่องค้นหา (O) ปุ่มหน้าตาเหมือน input 239x50px, radius 12px, badge shortcut ขวา
กลุ่มเมนู (O) 4 กลุ่ม (เรียน, อ่านและดู, ลงมือ, อื่น ๆ) รวม 21 ลิงก์
แถวธีม (O) 4 ปุ่ม pill 55x24px (3 ธีม + dark mode) ใช้ aria-pressed
ท้าย sidebar (O) ลิงก์ 4 ตัว ขนาด 12px และ string เวอร์ชัน/commit
State scroll ภายใน ไม่มีการพับ/ย่อ (O)
A11y (Add) aria-label ที่ nav, scrollbar ที่มี focus ได้

Sidebar สูงกว่า viewport 900px (แถวธีมอยู่ที่ y=1095) จึงต้องเลื่อนภายใน aside

NavGroup และ NavLink

รายการ ข้อกำหนด
NavGroup label (O) 10.88px, 700, uppercase, tracking 0.025em, สี --muted, padding 0 12px 4px
NavLink ขนาด (O) สูง 36px (sidebar) หรือ 40px (sheet), padding 8px 12px, radius 12px
NavLink เนื้อหา (O) ไอคอน (ราว 20px ไม่ได้วัด) + ข้อความ 14px/600, gap 12px
Default (O) สี --muted, พื้นโปร่ง
Hover (O) พื้น --muted-surface, ข้อความ --foreground, 150ms
Active (O/I) พื้นสีหลัก + ข้อความขาว (เห็นในภาพ), aria-current
Focus (Add) ring --ring 2px

MobileHeader

รายการ ข้อกำหนด
ขนาด (O) สูง 58px, padding 10px 16px, sticky top-0, z-index 20
พื้น (O) --background 90% + blur 8px, ขอบล่าง 2px
เนื้อหา (O) logo + wordmark ซ้าย, ปุ่มค้นหากับปุ่มบัญชีขวา (ไอคอน)
A11y (O) ปุ่ม aria-label ค้นหา / เข้าสู่ระบบ, aria-haspopup=dialog

BottomTabBar

รายการ ข้อกำหนด
ตำแหน่ง (O) fixed, กลางจอ, ห่างล่าง max(12px, safe-area-inset-bottom)
ขนาด (O) กว้าง calc(100% - 24px) สูงสุด 460px, สูง 72px, radius 26px, ขอบ 2px, --card
รายการ (O) 5 ช่อง (คอร์ส, บทความ, วิดีโอ, ถาม-ตอบ, เมนู) ช่องละ ~68px ไอคอน + ป้าย 10-11px
ช่อง "เมนู" (O) button เปิด BottomSheet, aria-label "เมนูเนื้อหาทั้งหมด"
ช่องกลาง (I) เด่นกว่าช่องอื่น (วงกลม) U: ไม่รู้ว่าเด่นถาวรหรือตาม active
ซ่อน (O) ตั้งแต่ 1024px
Add aria-current="page" ที่ช่อง active, ไม่ให้ cookie banner บัง

ชั้น Overlay

BottomSheet

รายการ ข้อกำหนด
ตำแหน่ง (O) ชิดล่าง absolute inset-x-0 bottom-0 กว้างเต็ม, สูงสุด 85vh, เลื่อนในตัว
รูปทรง (O) radius บน 24px, ขอบบน 2px, padding 12px 16px 24px, handle เล็กตรงกลางบน
ตัวอย่างที่มี (O) เมนูเนื้อหา (21 ลิงก์ แถวละ 40px), บัญชี (login, สมัคร, ธีม, ลิงก์นโยบาย)
ARIA (O) role=dialog, aria-modal=true, aria-label
Backdrop (O) ปุ่มเต็มจอ aria-label="ปิดเมนู", ดำโปร่ง 40%
Motion (I) เลื่อนขึ้นจากล่าง ซ่อนด้วย transform (sheet ยังอยู่ใน DOM)
Add focus trap, Esc ปิด, คืน focus ไปปุ่มที่เปิด, inert พื้นหลัง

SearchDialog

รายการ ข้อกำหนด
Trigger desktop (O) ปุ่มหน้าตา input 239x50px + badge shortcut Cmd+K (U: ยังไม่ได้ทดสอบการกด)
Trigger mobile (O) ไอคอนใน header
Desktop (O) กล่องกลางจอ ราว 575px, y 90px, backdrop ดำโปร่ง, input + badge Esc
Mobile (O) แผงกว้าง 358px ห่างขอบ 16px, ชิดบน
ข้อความช่วย (O) บอกให้พิมพ์ >= 2 ตัวอักษร และบอกขอบเขตที่ค้นได้
ปิด (O) Esc ปิดได้ (desktop)
Backend ต้องมี endpoint ค้นหา (พบ /api/search)
Add เลือกผลลัพธ์ด้วยลูกศร, สถานะ loading / ไม่พบ / error, role=combobox

CookieBanner

รายการ ข้อกำหนด
ตำแหน่ง (O) fixed ล่างสุดเต็มกว้าง, พื้น --card, ขอบบนหนา, ทับ tab bar จนกว่าจะตอบ
เนื้อหา (O) ข้อความ 14px 4 บรรทัด (desktop) + ลิงก์นโยบาย, 3 ปุ่ม
ปุ่ม (O) ไม่ยินยอม (secondary เล็ก), ยอมรับ (primary เล็ก), ปิด (ไอคอน aria-label)
ปุ่มขนาด (O) สูง 28px (เล็กกว่า 44px)
ความจำ (O) consent-storage {consentedVersion, analytics}
ARIA (O) role=region
Add ไม่ทับ tab bar, ปุ่มสูง >= 44px บนจอสัมผัส, ปุ่มปิด = ไม่ยินยอม

ชั้นปุ่มและฟอร์ม

Button

Variant ค่าที่วัด (O)
primary พื้น --brand-500, ตัวอักษร --on-brand, ขอบ 2px --border, pill
secondary พื้น --card, ตัวอักษร --foreground, ขอบ 2px, pill
ghost พื้นโปร่ง, ตัวอักษร --muted, ขอบโปร่ง 2px
ขนาดปกติ สูง 44px, padding 10px 20px, 16px/700, gap 8px
ขนาดเล็ก สูง 28px (ปุ่มในแถบสนับสนุนและ cookie)
เงา 3px 3px 0 0 สี --border (ghost ไม่มีเงา)
Transition transform + box-shadow 100ms
กว้างเต็ม ปุ่มใน sidebar, price card, ฟอร์ม login
State ข้อกำหนด
Hover (U) ไม่ได้วัด ใช้ translate -1/-1px และเงา 4px (R)
Active (U) ค่าที่เลื่อนไม่ได้วัด ใช้ translate +1/+1px และเงา 1px (R)
Focus (U) ring 2px --ring offset 2px (Add)
Disabled (I) จางลง ปุ่มจองก่อนกรอกครบ (เห็นในภาพ) Add: aria-disabled + เหตุผลใกล้ปุ่ม
Loading (Add) spinner + aria-busy, กันกดซ้ำ
Contrast (Add) primary ขาวบนส้ม #f97316 ได้ 2.8:1 ไม่ผ่าน AA ต้องเปลี่ยนสีพื้น

IconButton

รายการ ข้อกำหนด
ใช้ที่ (O) ค้นหา, บัญชี, ปิด, ถูกใจ
A11y (O) มี aria-label ทุกตัวที่ตรวจ (ค้นหา, ปิด, ถูกใจ, เล่นวิดีโอ)
ขนาดแตะ (Add) >= 44x44px (ปุ่มปิดใน cookie สูง 28px)

ThemeSwitcher

รายการ ข้อกำหนด
โครง (O) แถวเดียว 4 ปุ่ม pill 55x24px: 3 ธีม (ไอคอน) + dark mode
ARIA (O) aria-pressed, title บอกชื่อ (Bold, Ocean, Grape, โหมดมืด)
สถานะเลือก (O) พื้นสีหลัก, ตัวอักษรขาว
พฤติกรรม (O) เปลี่ยนทันทีไม่ reload
การเก็บ (O) theme-storage {template, dark} ใน localStorage
ที่อยู่ (O) desktop: ท้าย sidebar. mobile: ใน sheet บัญชี
Add ปุ่มมี aria-label (ไม่พึ่ง title), เคารพ prefers-color-scheme ครั้งแรก

Field / Input

รายการ ข้อกำหนด (O)
Label 14px ตัวหนา อยู่เหนือ input
Input สูง 48px, ขอบ 2px --border, radius 12px, พื้น --card
ชนิดที่พบ email, password, text (ชื่อ), text (โค้ดส่วนลด)
Add label ผูก input, autocomplete, error ใต้ field พร้อม aria-describedby
Add ปุ่มแสดงรหัสผ่าน

Combobox (เลือกหัวข้อ)

รายการ ข้อกำหนด
ARIA (O) role=combobox, aria-haspopup, aria-expanded
ตัวเลือกแรก (O) ให้ผู้สอนช่วยเลือก (ค่า default)
Add ลูกศร/Enter/Esc, aria-activedescendant, ค้นในรายการ 30 ข้อได้

TimeSlotChip และ AmountChip

รายการ ข้อกำหนด
TimeSlot (O) chip เวลาเริ่ม 09:00 ถึง 21:00 เว้นทีละ 2 ชั่วโมง, ขอบ 2px, pill
AmountChip (O) 10, 20, 50, 100, 500, 1,000, อื่นๆ; ขนาดสูง ~24px
State (I) เลือกแล้วพื้นสีหลัก
Add role=radiogroup + aria-checked, ขนาดแตะ >= 44px

OAuthButton และ Divider

รายการ ข้อกำหนด (O)
OAuthButton secondary pill เต็มกว้าง สูง 48px, โลโก้ Google 20px + ข้อความ
Divider เส้น 2px 2 ฝั่ง คำว่า "หรือ" 12px สี --muted กลางเส้น
งานใหม่ ใช้ปุ่มตามแนวทางแบรนด์ของผู้ให้บริการ OAuth ที่เลือก

ชั้น Card

Card (พื้นฐาน)

Variant ค่าที่วัด (O)
card พื้น --card, ขอบ 2px --border, radius 16px, เงา 4px 4px 0
card-flat เหมือน card แต่ไม่มีเงา (ใช้กับ stat tile)
lift เพิ่ม hover: translate(-2px,-2px) + เงา 6px, transition 120ms
padding p-6 24px หรือ p-0 เมื่อมีภาพชิดขอบ

CourseCard

รายการ ข้อกำหนด (O)
ขนาด desktop 344x291px (ใน grid 3 คอลัมน์ 1073px)
ภาพปก สูง 96px, object-fit: cover, ต้นฉบับ 475x250, loading=lazy
Overlay บนภาพ badge ซ้ายบน (รูปแบบเรียน), chip ขวาบน (ไอคอนตา + ตัวเลข)
เนื้อหา padding 20px: H3 18px/800, คำอธิบาย 14px ตัด 3 บรรทัด
ท้ายการ์ด เส้นคั่น + ราคา 16px ตัวหนา ซ้าย, "ดูรายละเอียด" + ลูกศร ขวา
ทั้งใบ เป็นลิงก์ <a> เดียว
State hover = lift. Add: focus ring รอบการ์ด, skeleton ขณะโหลด

ArticleCard

รายการ ข้อกำหนด
โครง (O) ภาพปก, tag, ชื่อ, สรุป 3 บรรทัด, เวลาอ่าน + จำนวนอ่าน, ลิงก์อ่านต่อ
Grid (O) 3 คอลัมน์ desktop, 24 ใบ/หน้า
ความสูงการ์ด (I) ใกล้เคียงกันด้วย flex-col + flex-1 ที่คำอธิบาย
บางใบไม่มีภาพ (O) แสดงเป็นการ์ดตัวอักษรล้วน (แถวล่างของหน้า tips)

ProductCard

รายการ ข้อกำหนด
โครง (O) พื้นที่ภาพ, ชื่อสินค้า, คำอธิบายสั้น, ราคา, ลิงก์ไปร้านค้า
สังเกต (O) พื้นที่ภาพว่างในภาพที่ตรวจ (ภาพยังไม่โหลดหรือไม่มีภาพ)
Add ระบุว่าเป็นลิงก์ affiliate/ภายนอก (rel, ข้อความกำกับ)

StatTile

รายการ ข้อกำหนด (O)
ชนิด card-flat, padding 16px 12px
เนื้อหา ตัวเลข 24px/900 (mobile 20px) + ป้าย 12px สี --muted
วาง grid 3 คอลัมน์ gap 12px, กว้างรวมสูงสุด 672px
ข้อมูล ตัวเลขมาจากระบบ (ผู้เรียน, หัวข้อ, บรรทัดโค้ด) ต้องมี backend

PriceCard

รายการ ข้อกำหนด (O)
ขนาด 360x339px, card p-6
ลำดับ badge, ป้าย "เริ่มต้น", ราคาใหญ่, หน่วย, checklist 3 ข้อ, ปุ่ม primary
หน้าแรก อยู่ขวาของ hero ที่ >= 1024px, ลงมาอยู่ใต้ข้อความบนมือถือ

TrustCard

รายการ ข้อกำหนด (O)
ขนาด 344x192px
โครง ไทล์ไอคอน 48px (สีต่างกัน 3 ใบ), H3 18px/800, ข้อความ 14px
ลิงก์ 2 ใน 3 ใบเป็นลิงก์ (ไปหน้าเงื่อนไขและหน้าโปรเจค)
หมายเหตุ ใช้ H3 โดยไม่มี H2 คั่น (ข้ามระดับ heading)

ชั้นเนื้อหา

Hero

รายการ ข้อกำหนด (O)
โครง desktop 2 คอลัมน์: ซ้าย badge, eyebrow, H1, lead, CTA x2, stat x3; ขวา price card
โครง mobile คอลัมน์เดียวเรียงตามลำดับเดิม
ขนาด สูง 648px (desktop), padding แนวตั้ง 80px (sm) / 56px
H1 48px/60px/900 (mobile 30px), คำเน้นสี --brand-500
พื้นหลัง dot-grid + รูปทรงตกแต่ง aria-hidden + pointer-events: none
Add ปรับสีคำเน้นให้ contrast >= 3:1 (ตัวใหญ่)

SectionHeader

รายการ ข้อกำหนด (O)
H2 30px/36px/900, tracking -0.75px
ลิงก์ขวา "ดูทั้งหมด" + ลูกศร 14px สีหลัก
ระยะ ห่าง grid ด้านล่าง 20px

SponsorBar และ SponsorBlock

รายการ ข้อกำหนด
SponsorBar (O) เต็มกว้าง สูง 85px, พื้น --muted-surface, ไอคอนหัวใจ, ข้อความ, ปุ่มเล็ก 2 ปุ่ม
แถบเฉียง (O) ลายเฉียงคั่นเหนือแถบ ความสูงราว 20px
SponsorBlock (O) หัวข้อ + AmountChip + ปุ่มสนับสนุนแบบมีชื่อ + QR + ลิงก์ ขอชื่อ/ดูรายชื่อ
Backend ต้องมี: บันทึกยอด, ชื่อ, ตรวจรายการโอน
ข้อควรระวัง งานใหม่ต้องมีเส้นแบ่งชัดเจนว่าเป็นการสนับสนุน ไม่ใช่การสั่งซื้อ

Accordion

รายการ ข้อกำหนด
ใช้ที่ (O) FAQ ในหน้าคอร์ส (ปุ่ม toggle พร้อม aria-expanded) animation: U
รายการ "สิ่งที่จะได้เรียน" (I) การ์ดมีเลขลำดับ 1-6 อาจขยายได้ U
Add aria-controls, Enter/Space สลับ, ไอคอนหมุน, เคารพ reduced motion

Checklist

รายการ ข้อกำหนด (O)
รายการ ไอคอนวงกลมติ๊กสีเขียว (--success) + ข้อความ 14px ตัวหนา
ใช้ที่ price card, ผลลัพธ์หลังเรียน (2 คอลัมน์ในการ์ด)
Add ไอคอน aria-hidden, ใช้ <ul>

Badge

รายการ ข้อกำหนด (O)
ขนาด 12px/700, สูง ~20px, pill, ไอคอนนำหน้า 12px
Variant พื้น --card ตัวอักษรเข้ม (บนภาพ), พื้น --muted-surface ตัวอักษรรอง (ในการ์ด)
ใช้ที่ "รูปแบบเรียนสด" บนการ์ดและ hero, tag บทความ

VideoThumbnail

รายการ ข้อกำหนด
ภาพ (O) กว้างเต็มคอลัมน์ มีปุ่มเล่นกลางภาพ aria-label="เล่นวิดีโอ: ชื่อ"
ปุ่มประกอบ (O) "ดูบน YouTube" ลิงก์ภายนอก
Add โหลด player เมื่อกดเท่านั้น (lazy), ปุ่มเล่น focus ได้

Pagination

รายการ ข้อกำหนด
โครง (O) ปุ่มก่อนหน้า, ข้อความ "หน้า N จาก M", ปุ่มถัดไป
ลิงก์ (O) /tips?page=2 (เป็นลิงก์จริง ไม่ใช่ state ฝั่ง client)
Add nav aria-label, aria-current="page", ปุ่มก่อนหน้า aria-disabled ในหน้าแรก

DecorativeLayer

รายการ ข้อกำหนด (O)
ชนิด วงกลมเส้น, สี่เหลี่ยมมนหมุน, จุด dot-grid, แถบเฉียง
ข้อกำหนด aria-hidden, pointer-events: none, อยู่ใน hero เท่านั้น
Motion animation-name: none ที่ตรวจ ไม่มี animation

Component ที่ reference ไม่มีหรือไม่ครบ

งานใหม่ต้องเพิ่มทั้งหมดนี้ (Add)

Component เหตุผล
SkipLink ไม่พบ
Toast / Alert พบ aria-live 1 จุด แต่ไม่เห็น UI ของ toast
Skeleton ไม่ได้เห็นสถานะ loading
EmptyState ไม่ได้เห็น (ค้นหาไม่พบ, ยังไม่มีโพสต์)
ErrorState / ErrorBoundary หน้า 404 เป็นค่า default ของ framework
Tooltip ปุ่มธีมใช้ title เท่านั้น
Modal ยืนยัน ไม่ได้เห็น (อาจอยู่หลัง login)

Interaction และ state

Interaction ที่วัดหรือสังเกตได้

รายละเอียดของ search, theme, cookie, accordion, combobox และ chip เวลาอยู่ในหัวข้อ component ของแต่ละชิ้นแล้ว ตารางนี้เก็บเฉพาะที่ข้ามหลาย component

Interaction ข้อกำหนด ที่มา
Hover ลิงก์เมนู พื้น --muted-surface + ข้อความเข้มขึ้น, 150ms O (class)
Hover ลิงก์เล็ก ข้อความเข้มขึ้น + ขีดเส้นใต้ O (class)
Active (กดค้าง) ปุ่มใช้ transition ของ transform/shadow 100ms O (transition), ค่า U
เมนู mobile แตะ "เมนู" เปิด sheet จากล่าง O
ลิงก์สมัคร / เข้าสู่ระบบ พก ?next= กลับหน้าเดิม O

พฤติกรรมที่ทดสอบจริง

ผลจากการทดสอบด้วย CDP (hover, เปิด/ปิด dialog, สลับธีม) พร้อมหลักฐานที่ใช้ยืนยัน

พฤติกรรม ผลที่เห็น หลักฐาน
Hover การ์ดคอร์ส translate(-2px,-2px) และเงา 4px เป็น 6px ใน 0.12 วินาที computed style ก่อน/หลัง
เปิดค้นหา (desktop) overlay กลางหน้า, input, badge Esc, ข้อความให้พิมพ์ 2 ตัวอักษรขึ้นไป screenshot i-desk-search.png
กด Esc (desktop) overlay ค้นหาหาย เช็ค dialog ที่มองเห็นหลังกด
ปุ่ม ไม่ยินยอม banner หาย, เก็บ analytics:false ใน consent-storage อ่าน localStorage
สลับ dark mode html.dark, พื้นหลัง #0f0a05, เก็บ dark:true computed style + storage
สลับธีม Ocean/Grape data-theme เปลี่ยน, สีพื้นเปลี่ยนทันที computed style
Mobile: ปุ่ม เมนู เปิด bottom sheet เมนู 21 ลิงก์ แถวละ 40px screenshot j-mob-menu-open.png
Cookie banner บน mobile ทับ tab bar ล่างจนกว่าจะตอบ screenshot i-mob-home-viewport.png

สถานะที่ต้องออกแบบ

Reference ตรวจสถานะเหล่านี้ไม่ครบ งานใหม่ต้องมีทุกข้อ (R)

สถานะ ข้อกำหนด
Loading skeleton ของการ์ดขนาดเท่าจริง (ภาพ 96px + 3 บรรทัด) กัน layout shift
Empty ข้อความ + ปุ่มกลับ (ค้นหาไม่พบ, ยังไม่มีโพสต์)
Error กล่องสี --error-surface ขอบ --error, ปุ่ม ลองใหม่, ไม่เด้ง toast อย่างเดียว
Focus ring 2px สี --ring ห่างจากขอบ 2px ใช้ :focus-visible
Disabled ปุ่มจองก่อนเลือกครบ: จาง + aria-disabled + บอกเหตุผลใกล้ปุ่ม
Expanded accordion/combobox ต้องหมุนไอคอนและตั้ง aria-expanded
Offline / 404 404 ของ reference เป็นอังกฤษ default ควรทำให้ภาษาและสไตล์เดียวกับเว็บ

Motion

ที่มา ข้อกำหนด
O duration 100-150ms (--default-transition-duration: .15s)
O easing cubic-bezier(.4,0,.2,1)
O CSS มีกฎ prefers-reduced-motion ครบทั้ง reduce และ no-preference
R ปิด transform/translate ทุกตัวเมื่อ prefers-reduced-motion: reduce
R ห้ามใช้ animation ที่วนซ้ำ ยกเว้น loading

Accessibility

เป้าหมายคือ WCAG 2.2 AA ตาราง contrast คำนวณเองจากค่า token ที่ O ยังไม่ได้ยืนยันด้วย Lighthouse

Contrast ของ reference

คู่สี อัตราส่วน ผล AA
ข้อความหลัก บนพื้น (Bold light) 16.9 : 1 ผ่าน
ข้อความ muted บนพื้น 4.64 : 1 ผ่านเฉียด
ข้อความ muted บน muted-surface 4.52 : 1 ผ่านเฉียด
ตัวอักษรขาวบนปุ่ม brand-500 (ส้ม) 2.80 : 1 ไม่ผ่าน (ตัวหนา 14-16px)
ตัวอักษรขาวบน brand-700 5.18 : 1 ผ่าน
Eyebrow brand-700 บนพื้น 5.01 : 1 ผ่าน
คำเน้นสีส้ม 48px/900 บนพื้น 2.71 : 1 ไม่ผ่าน (เกณฑ์ตัวใหญ่ 3 : 1)
ธีม Ocean: ขาวบน brand-500 3.68 : 1 ไม่ผ่าน (ตัวเล็ก)
ธีม Grape: ขาวบน brand-500 5.70 : 1 ผ่าน
Dark Bold: muted บนพื้น 7.81 : 1 ผ่าน

Warning ปุ่มหลักสีส้มของ reference มี contrast ต่ำกว่า AA งานใหม่ต้องเลือกสีหลักที่ให้ตัวอักษรบนปุ่มได้ >= 4.5 : 1 (เช่น ใช้ขั้น 700 ของสเกล หรือตัวอักษรสีเข้มบนสีสว่าง)

ข้อกำหนดตามหมวด

หมวด ข้อกำหนด สถานะใน reference
Landmark header, nav ใส่ aria-label, main 1 ตัว มี landmark ครบ แต่ nav ไม่มี label
Skip link ลิงก์ "ข้ามไปเนื้อหา" เป็น focus แรก ไม่พบ
Heading ลำดับ H1 -> H2 -> H3 ไม่ข้ามระดับ หน้าแรกมี H3 ก่อน H2 (ข้ามระดับ)
Dialog aria-modal, focus เข้า dialog, trap, Esc ปิด, คืน focus มี role/aria-modal, ส่วนอื่น U
ปุ่มไอคอน ต้องมี aria-label ปุ่ม ค้นหา/ปิด/ถูกใจ มี, ปุ่มธีมใช้ title + aria-pressed
ภาพ alt ที่สื่อความหมาย, ภาพตกแต่ง alt="" โลโก้ alt ว่าง, ภาพปกคอร์สมี alt
ขนาดแตะ >= 44x44px (WCAG 2.2 AA ขั้นต่ำ 24x24px) ปุ่มใน cookie banner และแถบสนับสนุนสูง 28px, chip จำนวนเงิน 24px
ภาษา lang="th" มี
Zoom / reflow ไม่มี scroll แนวนอนที่ 320-1440px ไม่พบ overflow ที่ 390, 820, 1440
Focus ring มองเห็นชัดทุกองค์ประกอบ U
Live region ข้อความ toast/สถานะใช้ aria-live พบ aria-live 1 ตัว
สีอย่างเดียว สถานะไม่สื่อด้วยสีอย่างเดียว เมนูที่เลือกใช้พื้นสี + aria-current

แนวทาง implement

ข้อเสนอทั้งหมดเป็น R

เรื่อง ข้อเสนอ
CSS Tailwind v4 + CSS variable semantic token ตามตารางใน Design token
ธีม data-theme + .dark บน html ตั้งก่อน paint (inline script) กัน flash
Responsive เนื้อหา ใช้ container query กับ grid เพื่อแก้ปัญหา 205px
Font variable font ที่รองรับไทย + font-display: swap + size-adjust กัน layout shift
ภาพ next/image หรือเทียบเท่า, ภาพปกสัดส่วนคงที่ (340x96 ~ 3.5:1), lazy
ไอคอน ชุดไอคอน stroke เดียวกันทั้งเว็บ (reference ใช้ลายเส้น 24px)

ข้อมูลทางเทคนิคและข้อจำกัดของการตรวจ

ส่วนนี้บอกว่าค่าข้างบนได้มาอย่างไร เชื่อได้แค่ไหน และอะไรยังต้องตรวจเอง

วิธีตรวจ

REA เชื่อมกับ Chrome ผ่าน CDP (loopback) แล้วเก็บหลักฐานแบบ passive ส่วน computed style ดึงด้วย script CDP แยกอีกชุดเพราะ REA ไม่เก็บ CSS ราย element

ขั้น เครื่องมือ ได้อะไร
1 curl + robots.txt + sitemap.xml status, header, กฎ crawl, รายการ route
2 rea inspect-web-page DOM 996 node, accessibility tree 589 node, 45 script, 27 resource
3 rea analyze-web-bundle parse 45 script ครบ 0 error, route string, fingerprint framework
4 rea capture-web-screenshot viewport PNG พร้อม sha256 (1418x802)
5 CDP script (Runtime.evaluate) computed style, CSS variable, media query, rect ของ element
6 CDP Input events hover, เปิด/ปิด dialog, ปิด cookie banner, สลับธีม/dark mode

Chrome ที่ใช้เป็น profile แยกในโฟลเดอร์ชั่วคราวของเครื่อง ไม่ใช่ profile หลักของผู้ใช้ ไม่มีการ login, ไม่ส่งฟอร์ม, ไม่ยิง request เกิน GET หน้าสาธารณะ 24 route (เว้น 0.5 วินาที) และไม่แตะ path ที่ robots.txt ห้าม

ข้อมูลจาก DOM และ bundle

ส่วนบนวัดจาก DOM/CSS จริง ส่วนล่างมาจาก static analysis ของ script 45 ไฟล์ (parse ผ่านทั้งหมด) ไม่ได้รัน JavaScript

รายการ (O) ค่าที่วัดได้
<html> lang="th", data-theme="bold", class dark เมื่อเปิด dark mode
Landmark aside (desktop), header (mobile), nav x2, main, footer (mobile)
Dialog role=dialog, aria-modal=true 2 ตัว (เมนูเนื้อหา, บัญชี) + 1 ตัวค้นหา
Storage theme-storage และ consent-storage เก็บเป็น JSON {state, version}
Meta og:* และ twitter:* ครบ, og:image 1942x809
Framework Next.js (ความมั่นใจสูง), React (กลาง) จาก detector ของ REA
Route string ใน bundle 9 path เมนู เช่น /tips, /courses, /projects, /group-courses
API path ที่เห็นเป็น string /api/auth, /api/search (regex บน script ที่ดาวน์โหลด)
Image pipeline /_next/image ส่ง webp, loading=lazy ที่การ์ด
Hosting header Server: Vercel, X-Powered-By: Next.js
Security header HSTS preload, X-Frame-Options: SAMEORIGIN, CSP แบบจำกัด

ข้อสันนิษฐาน

สมเหตุสมผลจากหลักฐาน แต่ไม่มีการยืนยันตรง ๆ (I)

ข้อสันนิษฐาน เหตุผล
ใช้ Tailwind v4 CSS variable --spacing, --text-* และ class h-18, max-w-115
State ธีม/consent อาจใช้ Zustand persist รูป JSON {state, version} ตรงกับ persist middleware, bundle มีคำ zustand 1 จุด
ไอคอนมาจาก Lucide มี string lucide จำนวนมากใน bundle
หน้าเนื้อหา (tips, guides) render ฝั่ง server จาก CMS/ไฟล์ HTML ตอบกลับมีเนื้อหาครบ ขนาด 200-330KB ต่อหน้า
การจองคอร์สต้อง login ลิงก์สมัคร/เข้าสู่ระบบพก next=%2Fschedule
ระบบสนับสนุนเงินใช้ QR โอนเงิน หน้า tips/courses แสดง QR และปุ่มจำนวนเงิน 10-1,000
ค้นหาเรียก /api/search พบ path เป็น string ใน bundle ยังไม่ได้จับ request จริง

สิ่งที่ยังไม่ยืนยัน

ต้องตรวจเองด้วยมือ (U) รายการหน้าที่ต้องเปิดดูเพิ่มอยู่ในส่วนหน้าและ template

รายการ ทำไมยังไม่รู้ วิธีตรวจ
Focus ring ของ keyboard focus() ใน script ได้ outline ของ browser ไม่ใช่ :focus-visible จริง กด Tab ใน browser จริง
Esc ปิด bottom sheet บน mobile sheet อยู่ใน DOM ตลอด ซ่อนด้วย transform จึงเช็คไม่ได้ ทดสอบบนจอสัมผัสหรือ DevTools
Focus trap ใน dialog ไม่ได้ส่ง Tab ผ่าน dialog กด Tab วนใน dialog
ปุ่มลัด Cmd/Ctrl+K หน้าจอแสดง badge แต่ไม่ได้ส่ง key combo กดจริงใน browser
Loading / empty / error ของค้นหาและจอง ไม่มี request ให้ trigger โดยไม่ login / ไม่ยิง API ใช้ network throttle ใน DevTools
Active state ของเมนู sidebar เห็นจาก screenshot (พื้นส้ม) ไม่ได้อ่าน computed style getComputedStyle บนลิงก์ที่ aria-current
Active state ของปุ่ม (กดค้าง) ไม่ได้ตรวจ :active เลย กดค้างแล้วอ่าน transform/shadow
Animation ของ hero, accordion, sheet animation-name ของ hero ได้ none ส่วนอื่นไม่ได้วัด เปิด DevTools Animations
ช่องกลางของ tab bar ("วิดีโอ") ไม่รู้ว่าเด่นถาวรหรือเพราะเป็น state active เปิดหน้าอื่นแล้วดูว่าเด่นตามหรือไม่
Contrast ratio จริง ค่าในส่วน Accessibility คำนวณเอง ยืนยันด้วย Lighthouse
หน้าหลัง login (/schedule, /account) robots.txt ห้าม และต้องมีบัญชี ไม่ตรวจ ตามกติกาของงาน
Request จริงของ /api/* REA ไม่เก็บ body และเปิด session สั้น จับ HAR ด้วย rea observe-web-session

ข้อจำกัดและหลักฐาน

  • Full-page screenshot ของ CDP วาง element แบบ fixed และ sticky ผิดตำแหน่ง (cookie banner, sidebar) รูปที่ใช้เทียบ layout จึงเป็นรูปขนาด viewport
  • ตรวจใน headless Chrome 155 เท่านั้น ไม่ได้ทดสอบ Safari หรือ Firefox
  • ไม่ได้ตรวจ hover ของทุก element
  • ตัวเลขสถิติหน้าแรก (จำนวนผู้เรียน ฯลฯ) เป็นข้อมูล ณ เวลาที่ตรวจ ไม่ใช่ค่าคงที่ของ UI
  • rea doctor รายงาน Hopper/Ghidra/IDA ไม่พร้อม ไม่กระทบงานนี้เพราะเป็นเครื่องมือวิเคราะห์ native binary

หลักฐานที่เก็บไว้ในเครื่อง

ไฟล์ เนื้อหา
<evidence>\inspect.json Evidence จาก rea inspect-web-page
<evidence>\bundle.json Evidence จาก rea analyze-web-bundle
<evidence>\probe-*.json computed style และโครงหน้า 9 route x 3 viewport
<evidence>\shot-*.png, i-*.png, j-*.png, k-*.png screenshot ทุกสถานะที่อ้างถึง
<evidence>\r.json, q-desktop.json ค่า typography, breakpoint, ธีม

คำถามที่ต้องตัดสินใจก่อนสร้าง

ข้อ คำถาม
1 งานใหม่ต้องรองรับกี่ธีม (reference มี 3 ธีม x 2 โหมด = 6 ชุด)
2 ภาษาที่รองรับ: ไทยอย่างเดียว หรือหลายภาษา (กระทบ font และ layout)

Summary

ข้อ สรุป
Visual language ขอบ 2px + hard shadow + ปุ่ม pill + card มุม 16px
Shell sidebar 288px (>= 1024) หรือ header + tab bar + sheet (< 1024)
Token semantic token + 3 ธีม x light/dark ผ่าน data-theme และ .dark
Component 37 ชิ้นใน 5 ชั้น แกนคือ Button, Card (3 variant), NavLink, Badge
สิ่งที่ลอกได้ โครง shell, bottom sheet, tab bar, ลำดับ block ของ hero และหน้าคอร์ส
สิ่งที่ต้องแก้ contrast ปุ่ม, tap target 24-28px, focus/keyboard, state ที่หายไป
Template 8 แบบ (T1-T8) โดย T2 catalog ใช้ซ้ำมากที่สุด (12 หน้า)
ส่วนที่ซ้ำทุกหน้า shell, cookie banner, บล็อกสนับสนุน, บล็อกสินค้าแนะนำ
ส่วนที่ต้อง backend auth, จอง, ค้นหา, community, สนับสนุน, ตัวนับ
ต้องตรวจมือต่อ focus ring, ปุ่ม active, keyboard ใน dialog, animation, player, หน้าบทความ
ข้อควรระวัง ห้ามใช้ชื่อ สี ภาพ ข้อความของ reference ตรง ๆ
Facebook