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 cocok 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 sehingga developer memiliki kontrol lebih besar atas cara font ditampilkan. Anda dapat membaca lebih lanjut tentang penggantian font dan properti penggantian di postingan ini. Anda juga dapat melihat implementasi teknik ini yang berfungsi dalam demo ini.

Artikel ini membahas cara penyesuaian ukuran font diterapkan di framework Next.js dan Nuxt.js untuk membuat CSS font penggantian dan mengurangi CLS. Panduan ini juga menunjukkan cara membuat font pengganti menggunakan alat lintas-potongan seperti Fontaine dan Capsize.

Latar belakang

font-display: swap biasanya digunakan untuk mencegah FOIT (Flash of invisible text) dan untuk menampilkan konten 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 dengan swap adalah efek yang mengganggu, yaitu perbedaan ukuran karakter dari kedua font menyebabkan konten layar bergeser. Hal ini menyebabkan skor CLS yang buruk, terutama untuk situs yang banyak berisi teks.

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

Tanpa menyesuaikan ukuran font

body {
  font-family: Inter, serif;
}
Teks yang tiba-tiba berubah font dan ukurannya menyebabkan efek yang mengganggu.

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 yang berbeda.

Menyesuaikan ukuran font penggantian dapat menjadi strategi yang efektif untuk mencegah pergeseran tata letak pemuatan font, tetapi menerapkan logika dari awal bisa jadi rumit, seperti yang dijelaskan dalam postingan tentang penggantian font ini. Untungnya, beberapa opsi alat sudah tersedia untuk mempermudah proses 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 Google Fonts atau font kustom ke halaman Anda dan menyertakan hosting mandiri otomatis bawaan untuk file font.

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

​​Misalnya, jika menggunakan font Roboto, Anda biasanya akan menentukannya di CSS sebagai 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 fungsi akan berupa nama class yang dapat Anda manfaatkan dalam template komponen. Jangan lupa untuk 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 Cumulative Layout Shift. Nilai defaultnya adalah benar (true). Next.js secara otomatis menetapkan font penggantian Anda ke Arial atau Times New Roman, bergantung pada jenis font (masing-masing serif vs sans-serif).

Untuk @next/font/local: Nilai string atau boolean salah yang menetapkan apakah font penggantian otomatis harus digunakan untuk mengurangi Cumulative Layout Shift. Kemungkinan nilainya adalah Arial, Times New Roman, atau false. Defaultnya adalah Arial. Jika Anda ingin menggunakan font serif, sebaiknya tetapkan nilai ini ke Times New Roman.

Opsi lain untuk font Google

Jika menggunakan komponen next/font bukan merupakan opsi, pendekatan lain untuk menggunakan fitur ini dengan Google Fonts adalah melalui tanda optimizeFonts. Next.js memiliki fitur optimizeFonts yang sudah diaktifkan secara default. Fitur ini menyisipkan CSS Google Font dalam respons HTML. Selain itu, Anda dapat mengaktifkan fitur penyesuaian penggantian font dengan menetapkan tanda 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 font-family, font family penggantian, dan jenis tampilan.

Jika font ditentukan di tempat yang tidak dapat ditemukan oleh modul, Anda dapat meneruskan info metrik seperti yang ditunjukkan 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 akan otomatis memindai CSS Anda untuk membaca deklarasi @font-face dan membuat aturan @font-face penggantian.

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

Sekarang Anda 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 tidak menggunakan Nuxt atau Next.js, Anda dapat menggunakan Fontaine. Fontaine adalah library dasar yang mendukung @nuxtjs/fontaine. Anda dapat menggunakan library ini dalam project untuk memasukkan CSS font penggantian 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 pengubah Vite dan Webpack untuk dihubungkan ke rantai build dengan mudah, aktifkan plugin seperti yang ditunjukkan dalam 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 kini 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 membuat 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 Anda tentukan untuk stack font (properti font-family).

Anda dapat membaca dokumentasi tentang cara menggunakan Capsize di sini.

Contoh

Pertimbangkan contoh berikut: Font web yang diinginkan adalah Lobster, yang 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 font-family. 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 ke CSS sendiri.

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