Astro Fonts: จัดการ Web Fonts อย่างมีประสิทธิภาพ

#astro13 เม.ย. 2569

Astro Fonts: จัดการ Web Fonts อย่างมีประสิทธิภาพ

การจัดการ Web Fonts เป็นหนึ่งในปัจจัยสำคัญที่ส่งผลต่อ performance และ user experience ของเว็บไซต์ Astro มี built-in font optimization และรองรับการใช้งาน fonts จากหลายแหล่ง บทความนี้จะแนะนำวิธีการจัดการ fonts ใน Astro อย่างมีประสิทธิภาพ

ปัญหาของ Web Fonts

ก่อนจะเริ่ม ทำความเข้าใจปัญหาที่พบบ่อยกับ Web Fonts:

  • FOUT (Flash of Unstyled Text): ข้อความแสดงด้วย fallback font ก่อน custom font โหลด
  • FOIT (Flash of Invisible Text): ข้อความหายไปชั่วคราวระหว่างโหลด font
  • Layout Shift: font ที่โหลดมาทีหลังทำให้ layout เลื่อน
  • Performance: font files ขนาดใหญ่ทำให้ page โหลดช้า

Astro Font Optimization (v4+)

Astro 4.x มี experimental font optimization ในตัว:

// astro.config.mjs
import { defineConfig } from 'astro/config';

export default defineConfig({
  experimental: {
    fonts: [
      {
        provider: 'google',
        name: 'Sarabun',
        cssVariable: '--font-sarabun',
      },
      {
        provider: 'google',
        name: 'Noto Sans Thai',
        cssVariable: '--font-noto-thai',
      },
    ],
  },
});

ใช้งานใน layout:

---
import { fontFace } from 'astro:fonts';
---

<html>
  <head>
    <style is:global>
      :root {
        --font-sarabun: 'Sarabun', sans-serif;
      }
      body {
        font-family: var(--font-sarabun);
      }
    </style>
  </head>
</html>

ใช้ Google Fonts แบบ Manual

วิธีที่ง่ายที่สุดคือใช้ผ่าน <link> tag:

---
// src/layouts/BaseLayout.astro
---

<html lang="th">
  <head>
    <meta charset="UTF-8" />
    <!-- Preconnect เพื่อเพิ่มความเร็ว -->
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    
    <!-- โหลด font -->
    <link
      href="https://fonts.googleapis.com/css2?family=Sarabun:wght@300;400;500;600;700&family=Noto+Sans+Thai:wght@300;400;500;700&display=swap"
      rel="stylesheet"
    />
    
    <style>
      :root {
        --font-body: 'Sarabun', 'Noto Sans Thai', sans-serif;
        --font-heading: 'Sarabun', sans-serif;
      }
      
      body {
        font-family: var(--font-body);
      }
      
      h1, h2, h3, h4, h5, h6 {
        font-family: var(--font-heading);
      }
    </style>
  </head>
  <body>
    <slot />
  </body>
</html>

Self-hosted Fonts

การ host fonts เองให้ performance ดีกว่าและไม่ต้องพึ่ง third-party:

# ดาวน์โหลด font files
mkdir -p public/fonts
# วาง .woff2 files ใน public/fonts/

สร้าง CSS สำหรับ font:

/* src/styles/fonts.css */
@font-face {
  font-family: 'Sarabun';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/sarabun-regular.woff2') format('woff2');
  unicode-range: U+0E00-U+0E7F; /* Thai characters */
}

@font-face {
  font-family: 'Sarabun';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/fonts/sarabun-bold.woff2') format('woff2');
  unicode-range: U+0E00-U+0E7F;
}

@font-face {
  font-family: 'Sarabun';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/sarabun-latin-regular.woff2') format('woff2');
  unicode-range: U+0000-U+00FF; /* Latin characters */
}

Import ใน layout:

---
import '../styles/fonts.css';
---

ใช้ fontsource Package

fontsource เป็น npm package ที่รวม fonts ไว้ให้ใช้งานได้ง่าย:

npm install @fontsource/sarabun
npm install @fontsource-variable/noto-sans-thai

Import ใน layout:

---
// Import เฉพาะ weights ที่ต้องการ
import '@fontsource/sarabun/300.css';
import '@fontsource/sarabun/400.css';
import '@fontsource/sarabun/500.css';
import '@fontsource/sarabun/700.css';
---

<style is:global>
  body {
    font-family: 'Sarabun', sans-serif;
  }
</style>

Font Preloading

การ preload fonts ช่วยลด FOUT:

<head>
  <!-- Preload critical fonts -->
  <link
    rel="preload"
    href="/fonts/sarabun-regular.woff2"
    as="font"
    type="font/woff2"
    crossorigin
  />
  <link
    rel="preload"
    href="/fonts/sarabun-bold.woff2"
    as="font"
    type="font/woff2"
    crossorigin
  />
</head>

Variable Fonts

Variable fonts ช่วยลดจำนวน font files:

@font-face {
  font-family: 'Sarabun Variable';
  font-style: normal;
  font-weight: 100 900; /* รองรับทุก weight */
  font-display: swap;
  src: url('/fonts/sarabun-variable.woff2') format('woff2-variations');
}

:root {
  --font-body: 'Sarabun Variable', sans-serif;
}

.light { font-weight: 300; }
.regular { font-weight: 400; }
.medium { font-weight: 500; }
.bold { font-weight: 700; }
.black { font-weight: 900; }

Font Loading Strategy

สร้าง utility สำหรับจัดการ font loading:

// src/utils/fontLoader.ts
export function createFontLoadObserver(fontFamily: string): Promise<void> {
  return new Promise((resolve, reject) => {
    if ('fonts' in document) {
      document.fonts.load(`1em ${fontFamily}`).then(() => {
        resolve();
      }).catch(reject);
    } else {
      // Fallback for older browsers
      setTimeout(resolve, 3000);
    }
  });
}

export async function waitForFonts(fonts: string[]): Promise<void> {
  await Promise.all(fonts.map(createFontLoadObserver));
  document.documentElement.classList.add('fonts-loaded');
}

ใช้งานใน script:

<script>
  import { waitForFonts } from '../utils/fontLoader';
  
  waitForFonts(['Sarabun', 'Noto Sans Thai']).then(() => {
    console.log('Fonts loaded!');
  });
</script>

Tailwind CSS กับ Custom Fonts

ถ้าใช้ Tailwind CSS:

// tailwind.config.mjs
export default {
  theme: {
    extend: {
      fontFamily: {
        sans: ['Sarabun', 'Noto Sans Thai', 'sans-serif'],
        heading: ['Sarabun', 'sans-serif'],
        mono: ['JetBrains Mono', 'monospace'],
      },
    },
  },
};

สรุป

การจัดการ fonts ใน Astro มีหลายวิธีตั้งแต่ Google Fonts, self-hosted, ไปจนถึง fontsource packages การเลือกวิธีที่เหมาะสมขึ้นอยู่กับความต้องการด้าน performance, privacy, และความสะดวกในการ maintain สิ่งสำคัญคือใช้ font-display: swap และ preload critical fonts เพื่อให้ user experience ดีที่สุด