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;
}
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");
}
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:
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 })
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
.
Impor createFontStack dari paket inti:
import { createFontStack } from '@capsizecss/core';
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';`
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));