Situs yang memuat font dengan font-display: swap sering mengalami pergeseran tata letak (CLS) saat font web dimuat dan ditukar dengan font pengganti.
Anda dapat mencegah CLS dengan menyesuaikan dimensi font penggantian 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 yang memungkinkan developer memiliki kontrol yang lebih besar terkait 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 di demo ini.
Artikel ini membahas cara penerapan penyesuaian ukuran font dalam framework Next.js dan Nuxt.js untuk menghasilkan CSS font pengganti dan mengurangi CLS. Contoh ini juga menunjukkan cara membuat font pengganti menggunakan alat lintas potong seperti Fontaine dan Capsize.
Latar belakang
tampilan font: pertukaran umumnya digunakan untuk mencegah FOIT (Flash teks yang tidak terlihat) dan 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 dengan swap
adalah efek yang mengerikan, ketika perbedaan ukuran karakter kedua font menyebabkan pergeseran konten layar. Hal ini menyebabkan skor CLS yang buruk, terutama untuk situs yang penuh teks.
Gambar berikut menunjukkan contoh masalah. Gambar pertama menggunakan font-display: swap
tanpa upaya untuk menyesuaikan ukuran font penggantian. Bagian kedua menunjukkan bagaimana penyesuaian ukuran menggunakan aturan @font-face
CSS akan meningkatkan pengalaman pemuatan.
Tanpa menyesuaikan ukuran font
body {
font-family: Inter, serif;
}
![Teks yang tiba-tiba berubah ukuran dan {i>font<i} menyebabkan efek yang mengagetkan.](/static/blog/framework-tools-font-fallback/image/text-suddenly-changes-f-ad5f294238b66.gif)
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 secara halus ke font lain.](https://developer.chrome.com/static/blog/framework-tools-font-fallback/image/text-transitions-smoothl-200e9965ef42b.gif?authuser=19&hl=id)
Menyesuaikan ukuran font pengganti dapat menjadi strategi yang efektif untuk mencegah pergeseran tata letak pemuatan font, tetapi menerapkan logika dari awal bisa jadi sedikit rumit, seperti yang dijelaskan dalam postingan ini tentang penggantian font. 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 Google Fonts atau font kustom ke halaman Anda dan menyertakan hosting file font bawaan secara otomatis.
Saat digunakan, metrik font penggantian dihitung secara otomatis dan dimasukkan ke dalam file CSS.
Misalnya, jika menggunakan font Roboto, biasanya Anda akan menentukannya dalam 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 font berikutnya:
Pindahkan deklarasi font Roboto ke dalam JavaScript dengan mengimpor fungsi 'Roboto' dari 'next/font'. Nilai fungsi yang ditampilkan akan menjadi nama class yang dapat Anda manfaatkan di template komponen. Jangan lupa menambahkan
display: swap
ke objek konfigurasi untuk mengaktifkan fitur tersebut.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 Pergeseran Tata Letak Kumulatif. 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
: String atau nilai boolean yang salah yang menetapkan apakah font penggantian otomatis harus digunakan untuk mengurangi Pergeseran Tata Letak Kumulatif. Nilai yang mungkin adalah Arial
, Times New Roman
, atau false
. Defaultnya adalah Arial
. Jika Anda ingin menggunakan font serif, sebaiknya setel nilai ini ke Times New Roman
.
Pilihan 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 fituroptimizeFonts yang sudah diaktifkan secara default. Fitur ini menyelaraskan CSS Google Font dalam respons HTML. Selanjutnya, Anda dapat mengaktifkan fitur penyesuaian penggantian font dengan menyetel 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, Anda akan menggunakan 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 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 secara otomatis memindai CSS Anda untuk membaca deklarasi @font-face dan menghasilkan aturan @font-face pengganti.
@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 pada contoh berikut
:root {
font-family: 'Roboto';
/* This becomes */
font-family: 'Roboto', 'Roboto override';
}
Membuat CSS sendiri
Library mandiri juga dapat membantu Anda menghasilkan CSS untuk penyesuaian ukuran font penggantian.
Menggunakan library Fontaine
Jika 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 secara otomatis memasukkan CSS font pengganti 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, aktifkan plugin seperti yang ditunjukkan pada 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 seperti berikut:
// Webpack
export default {
plugins: [FontaineTransform.webpack(options)]
}
Modul ini 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 penggantian 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 Anda menentukan stack font (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
.
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 jenis font.
javascript const { fontFamily, fontFaces } = createFontStack([ lobster, helveticaNeue, arial, ]);
Hasil dari kueri tersebut adalah sebagai 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 Anda. Kode berikut menunjukkan cara menerapkannya dalam style sheet 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));
Ucapan terima kasih
Banner besar oleh Alexander Andrews di Unsplash.