เว็บไซต์ที่โหลดแบบอักษรด้วย font-display: swap มักประสบปัญหาการเปลี่ยนแปลงเลย์เอาต์ (CLS) เมื่อเว็บฟอนต์โหลดและมีการแลกเปลี่ยนกับแบบอักษรสำรอง
คุณสามารถป้องกัน CLS ได้โดยการปรับขนาดของแบบอักษรสำรองให้ตรงกับแบบอักษรหลัก พร็อพเพอร์ตี้ เช่น size-adjust
, ascent-override
, descent-override
และ line-gap-override
ในกฎ @font-face
จะช่วยลบล้างเมตริกของแบบอักษรสำรอง ซึ่งช่วยให้นักพัฒนาซอฟต์แวร์ควบคุมวิธีแสดงแบบอักษรได้มากขึ้น อ่านข้อมูลเพิ่มเติมเกี่ยวกับ font-fallbacks และพร็อพเพอร์ตี้การลบล้างได้ในโพสต์นี้ นอกจากนี้ คุณยังดูการใช้งานเทคนิคนี้ที่ใช้งานได้จริงในเดโมนี้ได้ด้วย
บทความนี้จะอธิบายวิธีใช้การปรับขนาดแบบอักษรในเฟรมเวิร์ก Next.js และ Nuxt.js เพื่อสร้าง CSS ของแบบอักษรสำรองและลด CLS รวมถึงสาธิตวิธีสร้างแบบอักษรสำรองโดยใช้เครื่องมือข้ามส่วนต่างๆ เช่น Fontaine และ Capsize
ข้อมูลเบื้องต้น
โดยทั่วไป font-display: swap จะใช้เพื่อป้องกัน FOIT (Flash of invisible text) และเพื่อแสดงเนื้อหาบนหน้าจอได้เร็วขึ้น ค่า swap
จะบอกให้เบราว์เซอร์แสดงข้อความที่ใช้แบบอักษรนั้นทันทีโดยใช้แบบอักษรของระบบ และแทนที่แบบอักษรของระบบก็ต่อเมื่อแบบอักษรที่กำหนดเองพร้อมใช้งานเท่านั้น
ปัญหาใหญ่ที่สุดของ swap
คือเอฟเฟกต์ที่สะดุดตา ซึ่งความแตกต่างของขนาดอักขระของแบบอักษร 2 แบบทำให้เนื้อหาบนหน้าจอเลื่อนไปมา ซึ่งส่งผลให้คะแนน CLS ต่ำ โดยเฉพาะอย่างยิ่งสำหรับเว็บไซต์ที่มีข้อความมาก
รูปภาพต่อไปนี้แสดงตัวอย่างของปัญหา รูปภาพแรกใช้ font-display: swap
โดยไม่มีการพยายามปรับขนาดแบบอักษรสำรอง ส่วนภาพที่สองแสดงการปรับขนาดโดยใช้กฎ CSS @font-face
ที่ช่วยปรับปรุงประสบการณ์การโหลด
โดยไม่ปรับขนาดแบบอักษร
body { font-family: Inter, serif; }

หลังจากปรับขนาดแบบอักษร
body { font-family: Inter, fallback-inter, serif; } @font-face { font-family: "fallback-inter"; ascent-override: 90.20%; descent-override: 22.48%; line-gap-override: 0.00%; size-adjust: 107.40%; src: local("Arial"); }

การปรับขนาดแบบอักษรสำรองอาจเป็นกลยุทธ์ที่มีประสิทธิภาพในการป้องกันการเปลี่ยนเลย์เอาต์เมื่อโหลดแบบอักษร แต่การใช้ตรรกะตั้งแต่ต้นอาจเป็นเรื่องยากตามที่อธิบายไว้ในโพสต์นี้เกี่ยวกับแบบอักษรสำรอง แต่โชคดีที่ตอนนี้มีตัวเลือกเครื่องมือหลายอย่างที่ช่วยให้คุณดำเนินการนี้ได้ง่ายขึ้นขณะพัฒนาแอป
วิธีเพิ่มประสิทธิภาพแบบอักษรสำรองด้วย Next.js
Next.js มีวิธีเปิดใช้การเพิ่มประสิทธิภาพแบบอักษรสำรองในตัว ฟีเจอร์นี้จะเปิดใช้โดยค่าเริ่มต้นเมื่อคุณโหลดแบบอักษรโดยใช้คอมโพเนนต์ @next/font
คอมโพเนนต์ @next/font เปิดตัวใน Next.js เวอร์ชัน 13 คอมโพเนนต์นี้มี API เพื่อนําเข้า Google Fonts หรือแบบอักษรที่กําหนดเองลงในหน้าเว็บ รวมถึงการโฮสต์ไฟล์แบบอักษรด้วยตนเองโดยอัตโนมัติในตัว
เมื่อใช้ ระบบจะคำนวณเมตริกแบบอักษรสำรองและแทรกลงในไฟล์ CSS โดยอัตโนมัติ
ตัวอย่างเช่น หากใช้แบบอักษร Roboto โดยทั่วไปคุณจะต้องกำหนดแบบอักษรใน CSS ดังนี้
@font-face { font-family: 'Roboto'; font-display: swap; src: url('/fonts/Roboto.woff2') format('woff2'), url('/fonts/Roboto.woff') format('woff'); font-weight: 700; } body { font-family: Roboto; }
วิธีย้ายข้อมูลไปยัง next/font
ย้ายการประกาศแบบอักษร Roboto ไปยัง JavaScript โดยการนําเข้าฟังก์ชัน "Roboto" จาก "next/font" ค่าที่แสดงผลของฟังก์ชันจะเป็นชื่อคลาสที่คุณใช้ประโยชน์ได้ในเทมเพลตคอมโพเนนต์ อย่าลืมเพิ่ม
display: swap
ลงในออบเจ็กต์การกําหนดค่าเพื่อเปิดใช้ฟีเจอร์import { Roboto } from '@next/font/google'; const roboto = Roboto({ weight: '400', subsets: ['latin'], display: 'swap' // Using display swap automatically enables the feature })
ในคอมโพเนนต์ ให้ใช้ชื่อคลาสที่สร้างขึ้น ดังนี้
javascript export default function RootLayout({ children }: { children: React.ReactNode; }) { return ( <html lang="en" className={roboto.className}> <body>{children}</body> </html> ); }
ตัวเลือกการกําหนดค่า adjustFontFallback
สําหรับ @next/font/google
: ค่าบูลีนที่กำหนดว่าควรใช้แบบอักษรสำรองอัตโนมัติเพื่อลด Cumulative Layout Shift หรือไม่ ค่าเริ่มต้นคือ "จริง" Next.js จะตั้งค่าแบบอักษรสำรองเป็น Arial
หรือ Times New Roman
โดยอัตโนมัติโดยขึ้นอยู่กับประเภทแบบอักษร (แบบ Serif กับแบบ Sans-serif ตามลำดับ)
สําหรับ @next/font/local
: ค่าสตริงหรือบูลีนเท็จที่กําหนดว่าควรใช้แบบอักษรสำรองอัตโนมัติเพื่อลด Cumulative Layout Shift หรือไม่ ค่าที่เป็นไปได้คือ Arial
, Times New Roman
หรือ false
ค่าเริ่มต้นคือ Arial
หากต้องการใช้แบบอักษรที่มีขีดหนา ให้ตั้งค่านี้เป็น Times New Roman
ตัวเลือกอื่นๆ สำหรับแบบอักษร Google
หากใช้คอมโพเนนต์ next/font
ไม่ได้ อีกวิธีในการใช้ฟีเจอร์นี้กับ Google Fonts คือการใช้ Flag optimizeFonts
Next.js เปิดใช้ฟีเจอร์ optimizeFonts ไว้โดยค่าเริ่มต้นแล้ว ฟีเจอร์นี้จะแทรก CSS ของ Google Font ไว้ในคำตอบ HTML นอกจากนี้ คุณยังเปิดใช้ฟีเจอร์การปรับแบบอักษรสำรองได้โดยการตั้งค่า Flag experimental.adjustFontFallbacksWithSizeAdjust
ใน next.config.js ดังที่แสดงในข้อมูลโค้ดต่อไปนี้
// In next.config.js module.exports = { experimental: { adjustFontFallbacksWithSizeAdjust: true, }, }
หมายเหตุ: ยังไม่มีแผนที่จะรองรับฟีเจอร์นี้กับ app
dir ที่เพิ่งเปิดตัวใหม่ ในระยะยาว คุณควรใช้ next/font
วิธีปรับแบบอักษรสำรองด้วย Nuxt
@nuxtjs/fontaine เป็นโมดูลสําหรับเฟรมเวิร์ก Nuxt.js ที่คํานวณค่าเมตริกแบบฟอนต์สำรองโดยอัตโนมัติและสร้าง @font-face
CSS สำรอง
เปิดใช้โมดูลโดยเพิ่ม @nuxtjs/fontaine
ลงในการกําหนดค่าโมดูล
import { defineNuxtConfig } from 'nuxt' export default defineNuxtConfig({ modules: ['@nuxtjs/fontaine'], })
หากใช้ Google Fonts หรือไม่มีการประกาศ @font-face
สำหรับแบบอักษร คุณสามารถประกาศแบบอักษรเป็นตัวเลือกเพิ่มเติมได้
ในกรณีส่วนใหญ่ โมดูลจะอ่านกฎ @font-face
จาก CSS และอนุมานรายละเอียดต่างๆ เช่น font-family, ชุดแบบอักษรสำรอง และประเภทการแสดงผลโดยอัตโนมัติ
หากโมดูลตรวจไม่พบแบบอักษรที่กําหนดไว้ คุณสามารถส่งข้อมูลเมตริกตามที่แสดงในข้อมูลโค้ดต่อไปนี้
export default defineNuxtConfig({ modules: ['@nuxtjs/fontaine'], fontMetrics: { fonts: ['Inter', { family: 'Some Custom Font', src: '/path/to/custom/font.woff2' }], }, })
โมดูลจะสแกน CSS โดยอัตโนมัติเพื่ออ่านการประกาศ @font-face และสร้างกฎ @font-face สำรอง
@font-face { font-family: 'Roboto'; font-display: swap; src: url('/fonts/Roboto.woff2') format('woff2'), url('/fonts/Roboto.woff') format('woff'); font-weight: 700; } /* This will be generated. */ @font-face { font-family: 'Roboto override'; src: local('BlinkMacSystemFont'), local('Segoe UI'), local('Roboto'), local('Helvetica Neue'), local('Arial'), local('Noto Sans'); ascent-override: 92.7734375%; descent-override: 24.4140625%; line-gap-override: 0%; }
ตอนนี้คุณใช้ Roboto override
เป็นแบบอักษรสำรองใน CSS ได้แล้ว ดังที่แสดงในตัวอย่างต่อไปนี้
:root { font-family: 'Roboto'; /* This becomes */ font-family: 'Roboto', 'Roboto override'; }
การสร้าง CSS ด้วยตนเอง
ไลบรารีแบบสแตนด์อโลนยังช่วยสร้าง CSS สําหรับการปรับขนาดแบบฟอนต์สำรองได้ด้วย
การใช้ไลบรารี Fontaine
หากไม่ได้ใช้ Nuxt หรือ Next.js คุณสามารถใช้ Fontaine ได้ Fontaine เป็นไลบรารีพื้นฐานที่ขับเคลื่อน @nuxtjs/fontaine คุณสามารถใช้ไลบรารีนี้ในโปรเจ็กต์เพื่อแทรก CSS ของแบบอักษรสำรองโดยอัตโนมัติได้โดยใช้ปลั๊กอิน Vite หรือ Webpack
สมมติว่าคุณได้กำหนดแบบอักษร Roboto ในไฟล์ CSS ดังนี้
@font-face { font-family: 'Roboto'; font-display: swap; src: url('/fonts/Roboto.woff2') format('woff2'), url('/fonts/Roboto.woff') format('woff'); font-weight: 700; }
Fontaine มีเครื่องมือเปลี่ยนรูปแบบ Vite และ Webpack เพื่อเสียบเข้ากับเชนการบิลด์ได้อย่างง่ายดาย เปิดใช้ปลั๊กอินตามที่แสดงใน JavaScript ต่อไปนี้
import { FontaineTransform } from 'fontaine' const options = { fallbacks: ['BlinkMacSystemFont', 'Segoe UI', 'Helvetica Neue', 'Arial', 'Noto Sans'], // You may need to resolve assets like `/fonts/Roboto.woff2` to a particular directory resolvePath: (id) => 'file:///path/to/public/dir' + id, // overrideName: (originalName) => `${name} override` // sourcemap: false }
หากคุณใช้ Vite ให้เพิ่มปลั๊กอินดังนี้ javascript // Vite export default { plugins: [FontaineTransform.vite(options)] }
หากใช้ Webpack ให้เปิดใช้ดังนี้
// Webpack export default { plugins: [FontaineTransform.webpack(options)] }
โมดูลจะสแกนไฟล์โดยอัตโนมัติเพื่อแก้ไขกฎ @font-face ดังนี้ css @font-face { font-family: 'Roboto'; font-display: swap; src: url('/fonts/Roboto.woff2') format('woff2'), url('/fonts/Roboto.woff') format('woff'); font-weight: 700; } /* This will be generated. */ @font-face { font-family: 'Roboto override'; src: local('BlinkMacSystemFont'), local('Segoe UI'), local('Roboto'), local('Helvetica Neue'), local('Arial'), local('Noto Sans'); ascent-override: 92.7734375%; descent-override: 24.4140625%; line-gap-override: 0%; }
ตอนนี้คุณใช้ Roboto override
เป็นแบบอักษรสำรองใน CSS ได้แล้ว css :root { font-family: 'Roboto'; /* This becomes */ font-family: 'Roboto', 'Roboto override'; }
การใช้ไลบรารี Capsize
หากไม่ได้ใช้ Next.js, Nuxt, Webpack หรือ Vite ตัวเลือกอื่นคือการใช้ไลบรารี Capsize เพื่อสร้าง CSS สำรอง
createFontStack API ใหม่
API เป็นส่วนหนึ่งของแพ็กเกจ @capsize/core ที่ชื่อ createFontStack
ซึ่งยอมรับอาร์เรย์ของเมตริกแบบอักษรตามลําดับเดียวกับที่คุณระบุสแต็กแบบอักษร (พร็อพเพอร์ตี้ font-family
)
คุณสามารถดูเอกสารประกอบเกี่ยวกับการใช้ Capsize ที่นี่
ตัวอย่าง
พิจารณาตัวอย่างต่อไปนี้ เว็บแบบอักษรที่ต้องการคือ Lobster ซึ่งจะใช้ Helvetica Neue เป็นค่าเริ่มต้นแทน และจะใช้ Arial เป็นค่าเริ่มต้นแทน Helvetica Neue ใน CSS font-family: Lobster, 'Helvetica Neue', Arial
นําเข้า createFontStack จากแพ็กเกจหลักโดยทำดังนี้
import { createFontStack } from '@capsizecss/core';
นําเข้าเมตริกแบบอักษรสําหรับแบบอักษรที่ต้องการแต่ละแบบ (ดูเมตริกแบบอักษรด้านบน)
javascript import lobster from '@capsizecss/metrics/lobster'; import helveticaNeue from '@capsizecss/metrics/helveticaNeue'; import arial from '@capsizecss/metrics/arial';`
สร้างกองแบบอักษรโดยส่งเมตริกเป็นอาร์เรย์โดยใช้ลําดับเดียวกับที่คุณใช้ผ่านพร็อพเพอร์ตี้ CSS font-family
javascript const { fontFamily, fontFaces } = createFontStack([ lobster, helveticaNeue, arial, ]);
ซึ่งจะแสดงผลดังนี้
{ fontFamily: Lobster, 'Lobster Fallback: Helvetica Neue', 'Lobster Fallback: Arial', fontFaces: [ { '@font-face' { 'font-family': '"Lobster Fallback: Helvetica Neue"'; src: local('Helvetica Neue'); 'ascent-override': '115.1741%'; 'descent-override': '28.7935%'; 'size-adjust': '86.8251%'; } '@font-face' { 'font-family': '"Lobster Fallback: Arial"'; src: local('Arial'); 'ascent-override': 113.5679%; 'descent-override': 28.392%; 'size-adjust': 88.053%; } } ] }
คุณต้องเพิ่มโค้ด fontFamily และ fontFaces ลงใน CSS โค้ดต่อไปนี้แสดงวิธีติดตั้งใช้งานในสไตล์ชีต CSS หรือภายในบล็อก <style>
<style type="text/css"> .heading { font-family: } </style>
ซึ่งจะสร้าง CSS ต่อไปนี้
.heading { font-family: Lobster, 'Lobster Fallback: Helvetica Neue', 'Lobster Fallback: Arial'; } @font-face { font-family: 'Lobster Fallback: Helvetica Neue'; src: local('Helvetica Neue'); ascent-override: 115.1741%; descent-override: 28.7935%; size-adjust: 86.8251%; } @font-face { font-family: 'Lobster Fallback: Arial'; src: local('Arial'); ascent-override: 113.5679%; descent-override: 28.392%; size-adjust: 88.053%; }
นอกจากนี้ คุณยังใช้แพ็กเกจ @capsize/metrics เพื่อคํานวณค่าการลบล้าง และใช้กับ CSS ด้วยตนเองได้
const fontMetrics = require(`@capsizecss/metrics/inter`); const fallbackFontMetrics = require(`@capsizecss/metrics/arial`); const mainFontAvgWidth = fontMetrics.xAvgWidth / fontMetrics.unitsPerEm; const fallbackFontAvgWidth = fallbackFontMetrics.xAvgWidth / fallbackFontMetrics.unitsPerEm; let sizeAdjust = mainFontAvgWidth / fallbackFontAvgWidth; let ascent = fontMetrics.ascent / (unitsPerEm * fontMetrics.sizeAdjust)); let descent = fontMetrics.descent / (unitsPerEm * fontMetrics.sizeAdjust)); let lineGap = fontMetrics.lineGap / (unitsPerEm * fontMetrics.sizeAdjust));