כלי מסגרת לחלופות של גופנים

Janicklas Ralph James
Janicklas Ralph James

אתרים שמטעינים גופנים באמצעות font-display: swap סובלים לעיתים קרובות מתזוזות בפריסת הדף (CLS) כשגופן ה-webfont נטען ומוחלף בגופן החלופי.

כדי למנוע CLS, אפשר לשנות את המאפיינים של גופן החלופה כך שיתאימו לאלה של הגופן הראשי. מאפיינים כמו size-adjust,‏ ascent-override,‏ descent-override ו-line-gap-override בכלל @font-face יכולים לעזור לשנות את המדדים של גופן חלופי, וכך לתת למפתחים יותר שליטה על אופן הצגת הגופנים. מידע נוסף על גופנים חלופיים ועל נכסי ההחרגה זמין בפוסט הזה. אפשר גם לראות הטמעה פעילה של הטכניקה הזו בהדגמה הזו.

במאמר הזה נסביר איך מתבצעת ההטמעה של התאמות של גודל הגופן במסגרות Next.js ו-Nuxt.js כדי ליצור את קובץ ה-CSS של הגופן החלופי ולצמצם את הערך של CLS. הוא גם מדגים איך אפשר ליצור גופנים חלופיים באמצעות כלי חיתוך לרוחב כמו Fontaine ו-Capsize.

רקע

בדרך כלל משתמשים ב-font-display: swap כדי למנוע FOIT (הבזק של טקסט בלתי נראה) ולהציג תוכן מהר יותר במסך. הערך של swap מורה לדפדפן להציג טקסט עם הגופן באופן מיידי באמצעות גופן מערכת, ולהחליף את גופן המערכת רק כשהגופן המותאם אישית יהיה מוכן.

הבעיה העיקרית ב-swap היא האפקט המזעזע, שבו ההבדל בגדלי התווים של שני הגופנים גורם לתנודות בתוכן המסך. המצב הזה מוביל לציוני 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 או גופנים מותאמים אישית לדפים, וכולל אירוח עצמאי מובנה ואוטומטי של קובצי הגופנים.

כשמשתמשים במדדי הגופן החלופיים, הם מחושבים באופן אוטומטי ומוטמעים בקובץ ה-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:

  1. יש להעביר את הצהרת הגופן 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
    })
    
  2. ברכיב שלכם, משתמשים בשם המחלקה שנוצר: 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. ברירת המחדל היא true. Next.js מגדיר באופן אוטומטי את הגופן החלופי ל-Arial או ל-Times New Roman, בהתאם לסוג הגופן (serif לעומת San-serif בהתאמה).

עבור @next/font/local: מחרוזת או ערך בוליאני False שקובעים אם צריך להשתמש בגופן חלופי אוטומטי כדי לצמצם את Cumulative Layout Shift (Cumulative Layout Shift). הערכים האפשריים הם Arial,‏ Times New Roman או false. ערך ברירת המחדל הוא Arial. אם רוצים להשתמש בגופן עם serif, מומלץ להגדיר את הערך הזה כ-Times New Roman.

אפשרות נוספת לגופנים של Google

אם אי אפשר להשתמש ברכיב next/font, אפשר להשתמש בדגל optimizeFonts כדי להשתמש בתכונה הזו עם Google Fonts. התכונה optimizeFonts מופעלת כברירת מחדל ב-Next.js. התכונה הזו מוסיפה את ה-CSS של Google Fonts לקוד התגובה ב-HTML. בנוסף, אפשר להפעיל את התכונה של התאמת גופנים חלופיים על ידי הגדרת הדגל experimental.adjustFontFallbacksWithSizeAdjust בקובץ next.config.js, כפי שמוצג בקטע הקוד הבא:

// In next.config.js
module.exports = {
 experimental: {
   adjustFontFallbacksWithSizeAdjust: true,
 },
}

הערה: אין תוכנית לתמוך בתכונה הזו באמצעות הספרייה החדשה app. לטווח הארוך, מומלץ להשתמש ב-next/font.

איך להתאים את החלופות של גופנים באמצעות Nuxt

@nuxtjs/fontaine הוא מודול של מסגרת Nuxt.js שמחשב באופן אוטומטי את ערכי המדדים של גופן חלופי ויוצר את שירות ה-CSS החלופי @font-face.

כדי להפעיל את המודול, מוסיפים את @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.

נניח שבקובץ ה-CSS מוגדר גופן Roboto:

@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 שניתן לחבר בקלות לשרשרת ה-build. כדי להפעיל את הפלאגין, צריך להשתמש בקוד ה-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 החלופי.

ממשק ה-API החדש createFontStack

ה-API הוא חלק מחבילת @capsize/core שנקראת createFontStack, שמקבלת מערך של מדדי גופנים באותו סדר שבו מציינים את סטאק הגופנים (הנכס font-family).

כאן אפשר לעיין במסמכי התיעוד בנושא השימוש ב-Capsize.

דוגמה

דוגמה: גופן האינטרנט הרצוי הוא Lobster, והחלופה היא Helvetica Neue ואז Arial. ב-CSS, font-family: Lobster, 'Helvetica Neue', Arial.

  1. מייבאים את createFontStack מחבילת הליבה:

    import { createFontStack } from '@capsizecss/core';
    
  2. מייבאים את מדדי הגופן של כל אחד מהגופנים הרצויים (ראו 'מדדי גופן' למעלה): javascript import lobster from '@capsizecss/metrics/lobster'; import helveticaNeue from '@capsizecss/metrics/helveticaNeue'; import arial from '@capsizecss/metrics/arial';`

  3. יוצרים את מקבץ הגופנים, מעבירים את המדדים כמערך, לפי אותו סדר שבו מעבירים אותם באמצעות מאפיין ה-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));