ابزارهای چارچوب برای جایگزینی فونت

جانیکلاس رالف جیمز
Janicklas Ralph James

سایت‌هایی که فونت‌ها را با نمایش فونت بارگیری می‌کنند: swap اغلب از تغییر طرح ( CLS ) رنج می‌برند که فونت وب بارگیری می‌شود و با فونت جایگزین جایگزین می‌شود.

می توانید با تنظیم ابعاد فونت بازگشتی برای مطابقت با فونت اصلی از 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 ، افکت jarring است، جایی که تفاوت در اندازه کاراکترهای دو فونت منجر به تغییر محتوای صفحه می شود. این منجر به نمرات ضعیف 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 در نسخه 13 Next.js معرفی شد. این مؤلفه یک 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' از 'next/font'، اعلان فونت Roboto را به جاوا اسکریپت خود منتقل کنید. مقدار بازگشتی تابع یک نام کلاسی خواهد بود که می توانید در قالب جزء خود استفاده کنید. به یاد داشته باشید که 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 : یک مقدار بولی که تعیین می‌کند برای کاهش جابجایی طرح‌بندی تجمعی از فونت بازگشتی خودکار استفاده شود یا خیر. پیش فرض درست است. Next.js بسته به نوع فونت (به ترتیب serif در مقابل sans-serif) به طور خودکار فونت بازگشتی شما را روی Arial یا Times New Roman تنظیم می کند.

برای @next/font/local : یک رشته یا مقدار نادرست بولی که تعیین می‌کند برای کاهش جابجایی طرح‌بندی تجمعی از فونت بازگشتی خودکار استفاده شود یا خیر. مقادیر ممکن Arial , Times New Roman یا false هستند . پیش فرض Arial است. اگر می خواهید از فونت سریف استفاده کنید، این مقدار را روی Times New Roman تنظیم کنید.

گزینه دیگری برای فونت های گوگل

اگر استفاده از مؤلفه next/font گزینه ای نیست، روش دیگری برای استفاده از این ویژگی با فونت های Google از طریق پرچم optimizeFonts است. Next.js دارای ویژگی optimizeFonts است که قبلاً به طور پیش فرض فعال شده است. این ویژگی CSS فونت گوگل را در پاسخ HTML قرار می دهد. علاوه بر این، می‌توانید با تنظیم پرچم experimental.adjustFontFallbacksWithSizeAdjust در next.config.js، ویژگی تنظیم بازگشت فونت را فعال کنید، همانطور که در قطعه زیر نشان داده شده است:

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

توجه : هیچ برنامه ای برای پشتیبانی از این ویژگی با app تازه معرفی شده dir وجود ندارد. در دراز مدت، استفاده از next/font ایده آل است.

نحوه تنظیم مجدد فونت ها با Nuxt

@nuxtjs/fontaine یک ماژول برای چارچوب Nuxt.js است که به طور خودکار مقادیر متریک فونت بازگشتی را محاسبه کرده و CSS @font-face را ایجاد می کند.

با افزودن @nuxtjs/fontaine به پیکربندی ماژول‌ها، ماژول را فعال کنید:

import { defineNuxtConfig } from 'nuxt'

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

اگر از فونت‌های Google استفاده می‌کنید یا برای یک فونت تعریف @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 برای تنظیم اندازه فونت کمک کنند.

با استفاده از کتابخانه فونتین

اگر از 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 را فراهم می کند تا به راحتی به زنجیره ساخت وصل شود، افزونه را همانطور که در جاوا اسکریپت زیر نشان داده شده است فعال کنید.

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. CreativeFontStack را از بسته اصلی وارد کنید:

    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. پشته قلم خود را ایجاد کنید، معیارها را به عنوان یک آرایه ارسال کنید، با همان ترتیبی که از طریق ویژگی font-family CSS انجام می دهید. 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 برای محاسبه مقادیر override استفاده کنید و خودتان آن‌ها را در 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));