Alat framework untuk penggantian font

Janicklas Ralph James
Janicklas Ralph James

Situs yang memuat font dengan font-display: swap sering mengalami pergeseran tata letak (CLS) saat font web dimuat dan ditukar dengan font penggantian.

Anda dapat mencegah CLS dengan menyesuaikan dimensi font pengganti agar sesuai dengan font utama. Properti seperti size-adjust, ascent-override, descent-override, dan line-gap-override dalam aturan @font-face dapat membantu mengganti metrik font penggantian yang memungkinkan developer lebih mengontrol cara font ditampilkan. Anda dapat membaca selengkapnya tentang penggantian font dan properti penggantian di postingan ini. Anda juga dapat melihat penerapan teknik ini yang berfungsi dalam demo ini.

Artikel ini membahas cara penyesuaian ukuran font diterapkan di framework Next.js dan Nuxt.js untuk menghasilkan CSS font pengganti dan mengurangi CLS. Contoh ini juga menunjukkan cara menghasilkan font pengganti menggunakan alat cross-cut seperti Fontaine dan Capsize.

Latar belakang

font-display: swap umumnya digunakan untuk mencegah FOIT (Flash teks yang tidak terlihat) dan untuk menampilkan konten dengan lebih cepat di layar. Nilai swap memberi tahu browser bahwa teks yang menggunakan font harus segera ditampilkan menggunakan font sistem dan untuk mengganti font sistem hanya jika font kustom sudah siap.

Masalah terbesar pada swap adalah efek yang menggemparkan, di mana perbedaan ukuran karakter dari kedua font menyebabkan konten layar bergeser. Hal ini menyebabkan skor CLS yang buruk, terutama untuk situs yang berisi banyak teks.

Gambar berikut menunjukkan contoh masalah. Gambar pertama menggunakan font-display: swap tanpa upaya untuk menyesuaikan ukuran font penggantian. Bagian kedua menunjukkan bagaimana menyesuaikan ukuran menggunakan aturan @font-face CSS dapat meningkatkan pengalaman pemuatan.

Tanpa menyesuaikan ukuran font

body {
  font-family: Inter, serif;
}
Teks yang tiba-tiba berubah ukuran font dan ukuran sehingga menimbulkan efek mengagetkan.

Setelah menyesuaikan ukuran font

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");
}
Teks yang bertransisi dengan lancar ke font lain.

Menyesuaikan ukuran font penggantian dapat menjadi strategi efektif untuk mencegah pergeseran tata letak pemuatan font, tetapi menerapkan logika dari awal bisa jadi sulit, seperti yang dijelaskan dalam postingan tentang penggantian font ini. Untungnya, beberapa opsi alat sudah tersedia untuk mempermudah hal ini saat mengembangkan aplikasi.

Cara mengoptimalkan penggantian font dengan Next.js

Next.js menyediakan cara bawaan untuk mengaktifkan pengoptimalan font penggantian. Fitur ini diaktifkan secara default saat Anda memuat font menggunakan komponen @next/font.

Komponen @next/font diperkenalkan di Next.js versi 13. Komponen ini menyediakan API untuk mengimpor font Google atau font kustom ke halaman Anda dan menyertakan hosting mandiri otomatis untuk file font.

Saat digunakan, metrik font pengganti otomatis dihitung dan dimasukkan ke dalam file CSS.

Misalnya, jika menggunakan font Roboto, biasanya Anda akan menentukannya di CSS seperti berikut:

@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;
}

Untuk bermigrasi ke next/font:

  1. Pindahkan deklarasi font Roboto ke JavaScript Anda dengan mengimpor fungsi 'Roboto' dari 'next/font'. Nilai yang ditampilkan dari fungsi adalah nama class yang dapat Anda manfaatkan di template komponen. Jangan lupa menambahkan display: swap ke objek konfigurasi untuk mengaktifkan fitur.

     import { Roboto } from '@next/font/google';
    
    const roboto = Roboto({
      weight: '400',
      subsets: ['latin'],
      display: 'swap' // Using display swap automatically enables the feature
    })
    
  2. Di komponen Anda, gunakan nama class yang dihasilkan: javascript export default function RootLayout({ children }: { children: React.ReactNode; }) { return ( <html lang="en" className={roboto.className}> <body>{children}</body> </html> ); }

Opsi konfigurasi adjustFontFallback:

Untuk @next/font/google: Nilai boolean yang menetapkan apakah font penggantian otomatis harus digunakan untuk mengurangi Pergeseran Tata Letak Kumulatif. Nilai defaultnya adalah benar (true). Next.js secara otomatis menetapkan font penggantian ke Arial atau Times New Roman, bergantung pada jenis font (masing-masing serif vs sans-serif).

Untuk @next/font/local: Nilai salah string atau boolean yang menetapkan apakah font penggantian otomatis harus digunakan untuk mengurangi Pergeseran Tata Letak Kumulatif. Kemungkinan nilainya adalah Arial, Times New Roman, atau false. Defaultnya adalah Arial. Jika Anda ingin menggunakan font serif, pertimbangkan untuk menyetel nilai ini ke Times New Roman.

Opsi lain untuk Google Fonts

Jika tidak dapat menggunakan komponen next/font, pendekatan lain untuk menggunakan fitur ini dengan Google Fonts adalah melalui flag optimizeFonts. Next.js memiliki fitur optimizeFonts yang sudah diaktifkan secara default. Fitur ini membuat CSS Font Google menjadi inline dalam respons HTML. Selanjutnya, Anda dapat mengaktifkan fitur penyesuaian penggantian font dengan menyetel flag experimental.adjustFontFallbacksWithSizeAdjust di next.config.js, seperti yang ditunjukkan dalam cuplikan berikut:

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

Catatan: Tidak ada rencana untuk mendukung fitur ini dengan direktori app yang baru diperkenalkan. Dalam jangka panjang, sebaiknya gunakan next/font.

Cara menyesuaikan penggantian font dengan Nuxt

@nuxtjs/fontaine adalah modul untuk framework Nuxt.js yang secara otomatis menghitung nilai metrik font penggantian dan menghasilkan CSS @font-face penggantian.

Aktifkan modul dengan menambahkan @nuxtjs/fontaine ke konfigurasi modul Anda:

import { defineNuxtConfig } from 'nuxt'

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

Jika menggunakan Google Fonts atau tidak memiliki deklarasi @font-face untuk font, Anda dapat mendeklarasikannya sebagai opsi tambahan.

Pada umumnya, modul dapat membaca aturan @font-face dari CSS Anda dan secara otomatis menyimpulkan detail seperti jenis font, jenis font penggantian, dan jenis tampilan.

Jika font ditentukan di tempat yang tidak dapat ditemukan oleh modul, Anda dapat meneruskan info metrik seperti yang ditampilkan dalam cuplikan kode berikut.

export default defineNuxtConfig({
  modules: ['@nuxtjs/fontaine'],
  fontMetrics: {
  fonts: ['Inter', { family: 'Some Custom Font', src: '/path/to/custom/font.woff2' }],
},
})

Modul ini otomatis memindai CSS Anda untuk membaca deklarasi @font-face dan menghasilkan aturan penggantian @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%;
}

Anda kini dapat menggunakan Roboto override sebagai font pengganti di CSS, seperti yang ditunjukkan dalam contoh berikut

:root {
  font-family: 'Roboto';
  /* This becomes */
  font-family: 'Roboto', 'Roboto override';
}

Membuat CSS sendiri

Library mandiri juga dapat membantu Anda membuat CSS untuk penyesuaian ukuran font penggantian.

Menggunakan library Fontaine

Jika Anda tidak menggunakan Nuxt atau Next.js, Anda dapat menggunakan Fontaine. Fontaine adalah library pokok yang mendukung @nuxtjs/fontaine. Anda dapat menggunakan library ini dalam project untuk memasukkan CSS font pengganti secara otomatis menggunakan plugin Vite atau Webpack.

Bayangkan Anda memiliki font Roboto yang ditentukan dalam file 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 menyediakan transformer Vite dan Webpack untuk dihubungkan ke rantai build dengan mudah, mengaktifkan plugin seperti yang ditunjukkan di JavaScript berikut.

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
}

Jika Anda menggunakan Vite, tambahkan plugin seperti ini: javascript // Vite export default { plugins: [FontaineTransform.vite(options)] }

Jika menggunakan Webpack, aktifkan sebagai berikut:

// Webpack
export default {
  plugins: [FontaineTransform.webpack(options)]
}

Modul akan otomatis memindai file Anda untuk mengubah aturan @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%; }

Anda sekarang dapat menggunakan Roboto override sebagai font pengganti di CSS. css :root { font-family: 'Roboto'; /* This becomes */ font-family: 'Roboto', 'Roboto override'; }

Menggunakan library Capsize

Jika Anda tidak menggunakan Next.js, Nuxt, Webpack, atau Vite, opsi lainnya adalah menggunakan library Capsize untuk menghasilkan CSS penggantian.

API createFontStack baru

API ini adalah bagian dari paket @capsize/core yang disebut createFontStack, yang menerima array metrik font dalam urutan yang sama seperti yang akan Anda tentukan stack font Anda (properti font-family).

Anda dapat membaca dokumentasi tentang penggunaan Capsize di sini.

Contoh

Pertimbangkan contoh berikut: Font web yang diinginkan adalah Lobster, kembali ke Helvetica Neue, lalu Arial. Di CSS, font-family: Lobster, 'Helvetica Neue', Arial.

  1. Impor createFontStack dari paket inti:

    import { createFontStack } from '@capsizecss/core';
    
  2. Impor metrik font untuk setiap font yang diinginkan (lihat Metrik Font di atas): javascript import lobster from '@capsizecss/metrics/lobster'; import helveticaNeue from '@capsizecss/metrics/helveticaNeue'; import arial from '@capsizecss/metrics/arial';`

  3. Buat stack font, dengan meneruskan metrik sebagai array, menggunakan urutan yang sama seperti yang Anda lakukan melalui properti CSS jenis font. javascript const { fontFamily, fontFaces } = createFontStack([ lobster, helveticaNeue, arial, ]);

Tindakan ini akan menampilkan hal berikut:

{
  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%;
     }
   }
 ]
}

Anda harus menambahkan kode fontFamily dan fontFaces ke CSS. Kode berikut menunjukkan cara menerapkannya di sheet gaya CSS, atau dalam blok <style>.

<style type="text/css">
  .heading {
    font-family: 
  }

  
</style>

Tindakan ini akan menghasilkan CSS berikut:

.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%;
}

Anda juga dapat menggunakan paket @capsize/metrics untuk menghitung nilai penggantian, dan menerapkannya sendiri ke 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));

Ucapan terima kasih

Banner besar oleh Alexander Andrews di Unsplash.