Category: guide
Web Fonts Optimization — โหลด Font เร็วโดยไม่กระทบ UX
วิธี optimize font loading ให้ไม่เกิด FOUT/FOIT, ใช้ font-display อย่างถูกต้อง, preconnect, self-host fonts, variable fonts และ system font stack
สารบัญ
ปัญหาของ Web Fonts
FOUT (Flash of Unstyled Text) — text แสดงด้วย fallback font ก่อน แล้วกระพริบเป็น web font เมื่อโหลดเสร็จ
FOIT (Flash of Invisible Text) — text ซ่อนอยู่จนกว่า web font จะโหลดเสร็จ ผู้ใช้เห็นหน้าว่าง
Layout Shift — ขนาด/spacing ของ font ต่างกันทำให้ element เลื่อน (ส่งผลต่อ CLS score)
font-display — ควบคุม Loading Behavior
@font-face {
font-family: 'Inter';
src: url('/fonts/inter.woff2') format('woff2');
font-display: swap; /* แนะนำสำหรับ body text */
}
| Value | Behavior | เหมาะกับ |
|---|---|---|
auto | ขึ้นกับ browser (มักเหมือน block) | ไม่แนะนำ |
block | ซ่อน text สั้นๆ แล้วใช้ font | icon fonts |
swap | fallback ก่อน → swap เมื่อพร้อม | body text |
fallback | ซ่อน 100ms → fallback → swap ใน 3 วิ | เหมือน swap แต่ window สั้นกว่า |
optional | ใช้ font ถ้าโหลดเสร็จในเวลาสั้น มิฉะนั้นใช้ fallback ตลอด | nice-to-have fonts |
Preconnect — เตรียม Connection ล่วงหน้า
<!-- ใส่ใน <head> ก่อน Google Fonts stylesheet -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet" />
preconnect ทำ DNS lookup + TCP handshake + TLS negotiation ล่วงหน้า ประหยัดเวลา ~200-300ms
Self-hosting Fonts (ดีที่สุด)
ไม่ต้องพึ่ง CDN ภายนอก โหลดเร็วกว่า และ privacy ดีกว่า:
# ใช้ fontsource สำหรับ npm-based self-hosting
npm install @fontsource/inter
npm install @fontsource-variable/inter # variable font version
// src/layouts/Layout.astro หรือ global CSS
import '@fontsource/inter/400.css';
import '@fontsource/inter/700.css';
// หรือ import ใน CSS
@import '@fontsource/inter';
Variable Fonts — 1 ไฟล์แทน N ไฟล์
Variable font บรรจุ weight/style หลายอัน (100–900) ในไฟล์เดียว:
/* ปกติ: 6 font files */
@font-face { font-family: 'Inter'; font-weight: 400; src: url('inter-regular.woff2'); }
@font-face { font-family: 'Inter'; font-weight: 500; src: url('inter-medium.woff2'); }
@font-face { font-family: 'Inter'; font-weight: 700; src: url('inter-bold.woff2'); }
/* Variable font: 1 ไฟล์ */
@font-face {
font-family: 'Inter var';
src: url('inter-variable.woff2') format('woff2-variations');
font-weight: 100 900; /* range */
font-display: swap;
}
ขนาด 1 variable font file มักเล็กกว่าหลาย static files รวมกัน
System Font Stack — ไม่ต้องโหลดเลย
สำหรับ project ที่ไม่ต้องการ web font:
:root {
--font-sans:
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica,
Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji';
--font-mono:
'SF Mono', 'Cascadia Code', 'Fira Code', Consolas,
'DejaVu Sans Mono', monospace;
}
body { font-family: var(--font-sans); }
code { font-family: var(--font-mono); }
ข้อดี: โหลดเร็วสุด (ไม่มี network request), text ดูคุ้นเคยสำหรับผู้ใช้
สำหรับภาษาไทย (Noto Sans Thai)
<!-- โหลดเฉพาะ weight ที่ใช้ — Thai subset -->
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Thai:wght@400;600;700;800&display=swap" rel="stylesheet" />
body {
font-family: 'Noto Sans Thai', sans-serif;
}
/* fallback สำหรับ Latin text */
h1, p { font-family: 'Inter', 'Noto Sans Thai', sans-serif; }
ตรวจสอบ Performance
# ดูว่า font โหลดช้าไหม
curl -o /dev/null -s -w "DNS: %{time_namelookup}s | Connect: %{time_connect}s | Total: %{time_total}s\n" \
"https://fonts.gstatic.com/..."
# หรือดูใน DevTools > Network > Fonts tab
Lighthouse จะแจ้ง warning ถ้า:
- ไม่มี
font-display - ไม่มี preconnect สำหรับ third-party fonts
- ใช้ fonts ที่ทำให้เกิด layout shift