Fundamentals
CSS Units (px, %, em, rem, vw, vh...)
เลือกหน่วยวัดให้ตรงกับเจตนา — แยกแยะ absolute unit อย่าง px ออกจาก relative unit อย่าง rem, em, % และ vw/vh ว่าหน่วยแต่ละตัวเทียบกับอะไร
1) ปัญหา: ทุกขนาดเป็น px — ต้องไล่แก้ทีละจุดทั้งไฟล์
สมมติว่าคุณเขียน font-size ด้วย px ล้วนทั้งไฟล์ (font-size จะได้เรียนเต็มในบทที่ 20; ที่นี่ใช้เป็นตัวอย่างเพื่อดูพฤติกรรมของหน่วย) — h1 เป็น 32px, p เป็น 16px, ข้อความเล็กเป็น 14px ตอนแรกดูเหมือนไม่มีอะไรผิด แต่พอถึงเวลาจริงจะเจอปัญหาเหล่านี้:
- ผู้ใช้อ่านหนังสือไม่ถนัด ปรับขนาดฟอนต์ในเบราว์เซอร์ให้ใหญ่ขึ้น — ข้อความที่เขียนด้วย px ไม่ขยับตามเลย
- อยากปรับสเกลทั้งระบบให้ใหญ่ขึ้น 20% — ต้องไล่เปลี่ยนทุกจุดที่มีค่า px อยู่
- อยากได้แถบแสดงความคืบหน้าที่กว้างเป็นสัดส่วนกับกล่องแม่ ไม่ใช่ความกว้างตายตัว
ทางออกไม่ใช่การเลือกค่า "หน่วยเดียวที่ใช่" สำหรับทุกอย่าง — แต่คือการรู้จักหน่วยแบบสัมพัทธ์ (relative unit) ที่ขยายหดตาม context กับรู้ว่าควรเทียบกับอะไรในแต่ละจุด
2) แนวคิดหลัก: absolute กับ relative — ทุกหน่วยคือคำตอบของคำถาม "เทียบกับอะไร"
หน่วยใน CSS แบ่งเป็นสองกลุ่ม: **Absolute unit (หน่วยตายตัว)** — ค่าคงที่ ไม่ขึ้นกับ context เช่น px; 1px เป็น 1px เสมอ ไม่ว่า parent จะเล็กหรือใหญ่ **Relative unit (หน่วยสัมพัทธ์)** — ค่าเทียบกับสิ่งใดสิ่งหนึ่ง ถามเสมอว่า "เทียบกับอะไร": root font (rem), parent (em, %), หรือ viewport (vw, vh)
| หน่วย | ชนิด | เทียบกับอะไร | ตัวอย่าง |
|---|---|---|---|
| px | absolute | ไม่มี — ค่าตายตัว | width: 200px |
| rem | relative | font-size ของ root (<html>) | font-size: 1.5rem |
| em | relative | font-size ของ parent หรือของตัวมันเอง | padding: 0.5em |
| % | relative | ค่าของ property เดียวกันใน parent | width: 70% |
| vw / vh | relative | ความกว้าง/สูงของ viewport | height: 100vh |
- ค่า default font-size ของ root คือ 16px — 1rem = 16px, 2rem = 32px เสมอ ถ้าไม่มีการแก้ html
- relative unit ทำให้ขนาดเปลี่ยนตาม context โดยไม่ต้องแก้หลายจุด
- ค่าที่ดู "เล็ก" ในหน่วย rem เช่น 0.875rem (14px) อาจมองเห็นไม่ต่างจาก px แต่พฤติกรรมต่างกันเมื่อ root เปลี่ยน
3) rem กับ em: เทียบ root หรือเทียบ parent
สองหน่วยที่ใช้กับขนาดฟอนต์บ่อยที่สุดต่างกันที่จุดอ้างอิง: **rem (root em)** เทียบกับ font-size ของ <html> เสมอ — 1rem เท่ากับ 16px โดย default ไม่ว่าอยู่ซ้อนลึกแค่ไหน จึงไม่มีทางสะสมทบกัน **em** เทียบกับ font-size ของ parent เมื่อใช้กับ font-size และเทียบกับ font-size ของตัว element เองเมื่อใช้กับ property อื่น เช่น padding (padding จะได้เรียนเต็มในเฟส Box Model — ในบทนี้เราใช้เพื่อดูว่าหน่วย em ปรับระยะตามขนาดฟอนต์ของ component อย่างไร) ข้อควรระวังของ em คือการทบกัน (compounding): ถ้า element ซ้อนกันหลายชั้นและแต่ละชั้นใช้ font-size: 0.9em ฟอนต์จะหดลงทุกชั้น (0.9 × 0.9 × 0.9 ของ root) **ทายก่อนเปิด preview:** ในตัวอย่างด้านล่าง html ถูกตั้ง font-size เป็น 20px — ปุ่มทั้งสองมี padding: 0.5em 1em เหมือนกัน แต่ปุ่มไหนจะได้ padding ที่กว้างกว่ากัน? (em ของ padding เทียบกับฟอนต์ของปุ่มตัวเอง — .btn-lg ที่ฟอนต์ใหญ่กว่าจึงได้ padding กว้างกว่าโดยไม่ต้องเขียน padding ซ้ำ)
rem สำหรับขนาดฟอนต์ทั่วทั้งหน้า — em สำหรับระยะที่อยากให้เป็นสัดส่วนกับฟอนต์ของ component
4) หน่วยที่เหลือ: % เทียบ parent — vw/vh เทียบ viewport
**%** เทียบกับค่าของ property เดียวกันใน parent — width: 70% คือกว้าง 70% ของความกว้าง parent (width จะได้เรียนเต็มในบทที่ 13; ที่นี่ใช้เพื่อแสดงการเทียบสัดส่วนของ %); ใช้กับขนาดฟอนต์ด้วยได้ (150% = 1.5 เท่าของ parent) แต่ที่พบบ่อยคือ width **vw (viewport width)** และ **vh (viewport height)** เทียบกับขนาดหน้าต่างเบราว์เซอร์ — 1vw = 1% ของความกว้างหน้าจอ, 100vh = ความสูงเต็มจอ เหมาะกับส่วนเปิดหน้า (hero) ที่อยากให้สูงเต็มจอ หรือแถบที่กว้างเต็มจอ **ทายก่อนเปิด preview:** .bar-fill กำหนด width: 70% — ถ้า .progress-track กว้างเท่าไร .bar-fill ก็จะกว้าง 70% ของมันเสมอ ใช่หรือไม่?
ตั้งคำถามทุกครั้งว่า "เทียบกับอะไร" — % เทียบ parent, vw/vh เทียบ viewport
5) เขียนผิด vs เขียนถูก: em ซ้อนกันโดยไม่ตั้งใจ
จุดที่ผู้เริ่มต้นพลาดบ่อยที่สุดของหน่วยสัมพัทธ์คือการใช้ em กับ font-size ใน element ที่ซ้อนกันหลายชั้น — ค่า 0.9em ในแต่ละชั้นถูกคำนวณจาก parent ที่ลดลงไปเรื่อย ๆ จนข้อความชั้นในสุดเล็กลงเรื่อย ๆ **ทายก่อนดูเฉลย:** HTML ด้านล่างเป็นกล่องซ้อนกัน 3 ชั้น โดยทุกชั้นมี class="nested" เหมือนกัน — ฝั่งซ้ายใช้ font-size: 0.9em ฝั่งขวาใช้ 0.9rem ข้อความชั้นในสุดของฝั่งไหนจะเล็กกว่าที่ตั้งใจไว้มาก
em กับ rem เมื่อ element ซ้อนหลายชั้น
กล่องที่ซ้อนกัน 3 ชั้นและทุกชั้นได้ font-size 0.9 เท่า — em ทบค่ากันทุกชั้น ส่วน rem เทียบ root จึงคงที่
ชั้นที่ 1 = 0.9 × 16px, ชั้นที่ 2 = 0.9 × 14.4px, ชั้นที่ 3 = 0.9 × 12.96px — ยิ่งซ้อนยิ่งเล็กโดยไม่ตั้งใจ
ทุกชั้นเท่ากับ 0.9 × 16px = 14.4px เสมอ — ไม่สะสมทบกัน ไม่ว่า HTML จะซ้อนลึกแค่ไหน
Lab 1 — ทำตาม: แปลงสเกลฟอนต์จาก px เป็น rem
สเปกในแท็บ CSS กำหนดขนาดฟอนต์เป็น rem ไว้ให้แล้ว — ให้เขียน rule ของ h1, p และ small ตามค่าในสเปก โดยคิดจาก root font-size default ที่ 16px (32px → 2rem, 16px → 1rem, 14px → 0.875rem)
Lab 2 — แก้: ปุ่มที่ยืดหยุ่น — เปลี่ยน padding จาก px เป็น em
มีปุ่มสองขนาด: .btn-sm ฟอนต์ 0.875rem และ .btn-lg ฟอนต์ 1.25rem สเปกต้องการให้ padding ของปุ่มเป็นสัดส่วนกับฟอนต์ของตัวเอง (ปุ่มใหญ่ได้พื้นที่รอบข้อความมากกว่าปุ่มเล็กตามสัดส่วน) แต่ตอนนี้ .btn ใช้ padding: 12px 24px แบบตายตัว — แก้ให้ padding เป็น em เพื่อให้เทียบกับฟอนต์ของปุ่มเอง
Lab 3 — แก้โจทย์: เลือกหน่วยให้ตรงกับเจตนา
การ์ดแสดงความคืบหน้ามีสามจุดที่ต้องใส่ขนาด โดยสเปกบอกเป็นเจตนาไม่ใช่ค่าสำเร็จรูป — คุณต้องเลือกหน่วยเอง: .title ใหญ่กว่า root 1.5 เท่า (ใช้ rem), .bar-fill กว้าง 70% ของราง (ใช้ %), และ .title มี padding บน/ล่าง 0.5 เท่าของฟอนต์ตัวเอง ซ้าย/ขวา 1 เท่า (ใช้ em)
ตรวจความเข้าใจและก้าวต่อไป
ตอบคำถามสั้น ๆ เพื่อเช็กว่าคุณแยก absolute ออกจาก relative และรู้ว่าหน่วยแต่ละตัวเทียบกับอะไร ก่อนไปเรียนการเลือก element ตามโครงสร้างด้วย combinator ในบทถัดไป
ตรวจความเข้าใจ: CSS Units
ต้องการให้ข้อความทุกขนาดในหน้าเว็บขยายตามการปรับขนาดฟอนต์ของผู้ใช้ ควรใช้หน่วยใดกับ font-size
html มี font-size: 20px และ element ซ้อนกัน 3 ชั้น แต่ละชั้นใช้ font-size: 0.9em — ข้อความชั้นในสุดจะได้ขนาดเท่าไรโดยประมาณ
ข้อใดจับคู่หน่วยกับสิ่งที่มันเทียบได้ถูกต้อง (เลือกทุกข้อที่ถูก)
root font-size default ของเบราว์เซอร์คือ 16px — 1.5rem จึงเท่ากับกี่ px? ตอบเป็นตัวเลข
- absolute unit (px) ตายตัว — relative unit ถามเสมอว่าเทียบกับอะไร
- rem เทียบ root — ใช้กับ font-size ทั้งหน้า ไม่ทบกัน
- em เทียบ parent/ตัวเอง — ใช้กับระยะของ component ที่อยากให้ยืดหยุ่น ระวัง compounding
- % เทียบ parent · vw/vh เทียบ viewport
- ก้าวต่อไป: ในบทถัดไป 'Combination Selectors' เราจะเรียนการเลือก element ตามความสัมพันธ์ในโครงสร้าง HTML ด้วย combinator เช่น descendant และ child (สมมติฐานความรู้ก่อนหน้า: แยกแยะหน่วย absolute กับ relative และรู้ว่า rem/em/% เทียบกับอะไรจากบทนี้ — เพราะ selector แบบ A B หรือ A > B จะเลือก element ตามโครงสร้าง ซึ่งต้องเข้าใจก่อนว่า element ไหนเป็น parent ของอีกตัว และค่าหน่วยสัมพัทธ์ของมันจะเทียบกับ element ไหน)