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

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

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

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

אם אי אפשר להשתמש ברכיב next/font, אפשר להשתמש בתכונה הזו עם Google Fonts באמצעות הדגל optimizeFonts. התכונה 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.

נניח שהגדרתם גופן 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 שאפשר לחבר בקלות לשרשרת ה-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));