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

ג'ניקלאס ראלף ג'יימס
ג'ניקלאס ראלף ג'יימס

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

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

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

רקע

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

הבעיה הגדולה ביותר ב-swap היא האפקט הצורם, שבו ההבדל בגודל התווים בשני הגופנים גורם להזזה של תוכן המסך. המצב הזה מוביל לציוני CLS נמוכים, במיוחד באתרים עם הרבה טקסט.

התמונות הבאות מציגות דוגמה לבעיה. בתמונה הראשונה נעשה שימוש ב-font-display: swap ללא ניסיון להתאים את גודל הגופן החלופי. השנייה מראה איך התאמת הגודל באמצעות הכלל @font-face של CSS משפרת את חוויית הטעינה.

בלי לשנות את גודל הגופן

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 הושק בגרסה 13 של Next.js. הרכיב מספק ממשק 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;
}

כדי לעבור לפריט הבא/גופן:

  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 (CLS). ברירת המחדל היא TRUE. Next.js מגדיר באופן אוטומטי את גופן הגיבוי ל-Arial או ל-Times New Roman, בהתאם לסוג הגופן (serif לעומת Sans Serif בהתאמה).

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

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

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

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

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

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

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

כדי להפעיל את המודול, מוסיפים @nuxtjs/fontaine להגדרת המודולים:

import { defineNuxtConfig } from 'nuxt'

export default defineNuxtConfig({
  modules: ['@nuxtjs/fontaine'],
})

אם נעשה שימוש ב-Google Fonts או שאין לך הצהרת @font-face לגופן, אפשר להצהיר עליהם כאפשרויות נוספות.

ברוב המקרים, המודול יכול לקרוא את הכללים של @font-face מה-CSS ולהסיק באופן אוטומטי פרטים כמו משפחת הגופנים, משפחת הגופנים החלופית וסוג התצוגה.

אם הגופן מוגדר במקום שאינו גלוי למודול, ניתן להעביר את מידע הערכים כפי שמוצג בקטע הקוד הבא.

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).

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

דוגמה

נתייחס לדוגמה הבאה: גופן האינטרנט הרצוי הוא Lobster, עם חזרה ל-H התראה Newe ולאחר מכן 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));

אישורים

תמונה ראשית (Hero) מאת אלכסנדר אנדרוז בתוכנית Unense.