Font custom adalah file font yang harus di-download browser dulu, misalnya Inter, bukan font yang sudah ada di perangkat. Selama file itu belum datang, browser harus memutuskan: teksnya disembunyikan dulu, atau ditampilkan pakai font cadangan (fallback) lalu diganti belakangan. Bayangin presentasi di kelas, tapi proyektornya belum nyala. Kamu bisa diam menunggu sampai slide muncul (kelas bengong), atau mulai menjelaskan pakai papan tulis, lalu pindah ke slide begitu proyektornya siap. font-display adalah aturan yang memilih di antara dua cara itu.
🧪 Tebakan cepat: di 3G lambat, file
inter.woff2di halaman contoh baru datang di 2.900 ms. Denganfont-display: block, kapan judulnya bisa dibaca: 500 ms, 2.900 ms, atau 3.500 ms? Jawabannya ada di bagian "Empat nilai font-display".
Halaman contoh
Halaman blog di latihan cuma punya satu judul, <h1>Hello World</h1>, yang memakai font Inter. Ini yang terjadi (angka simulasi, sama dengan di Lab):
- Di 500 ms HTML dan CSS sudah siap, dan browser siap menggambar halaman (first paint).
- Baru saat itu browser tahu judulnya butuh Inter, jadi
inter.woff2mulai di-download di 500 ms. - Di 4G, download-nya 300 ms: font datang di 800 ms. Di 3G lambat, 2.400 ms: font datang di 2.900 ms.
Selama 500 ms sampai font datang, judulnya harus digambar dengan sesuatu. Di situ font-display berperan.
FOIT dan FOUT
Begitu browser butuh font yang belum datang, jam font-nya mulai jalan. Ada dua periode:
- Block period: teks disembunyikan (digambar transparan, tempatnya tetap ada). Kalau font datang di periode ini, teks langsung tampil pakai font itu.
- Swap period: sesudah block period habis, teks tampil pakai font fallback. Kalau font datang di periode ini, browser menukarnya (swap) ke font custom.
- Kalau swap period juga habis dan font belum datang, fallback dipakai terus sampai halaman ditutup.
Dua efek yang bisa dilihat pengunjung punya nama:
- FOIT (flash of invisible text): teks gak kelihatan selama block period. Halaman tampak kosong, dan LCP ikut tertunda kalau elemen terbesarnya teks.
- FOUT (flash of unstyled text): teks langsung tampil pakai fallback, lalu berganti ke font custom. Teks cepat terbaca, tapi ada momen hurufnya berganti.
Empat nilai font-display
| Nilai | Block period | Swap period | Di 3G lambat (font datang 2.900 ms) | Cocok untuk |
|---|---|---|---|---|
block |
sekitar 3 detik | tanpa batas | gak kelihatan 500 sampai 2.900 ms, lalu Inter | font ikon, yang salah kalau tampil pakai fallback |
swap |
hampir 0 | tanpa batas | fallback di 500 ms, Inter di 2.900 ms | teks isi dan judul yang harus cepat terbaca |
fallback |
sekitar 100 ms | sekitar 3 detik | gak kelihatan sampai 600 ms, fallback, Inter di 2.900 ms | font yang boleh berganti asal cepat |
optional |
sekitar 100 ms | 0 | gak kelihatan sampai 600 ms, lalu fallback sampai halaman ditutup | font hiasan, halaman gak boleh berganti font |
Jam font mulai di 500 ms, jadi block period block habis di 3.500 ms. Font datang di 2.900 ms, sebelum block period habis, jadi teksnya disembunyikan sampai 2.900 ms lalu langsung tampil pakai Inter. Itu jawaban tebakannya: 2.900 ms. Pengunjung menatap halaman tanpa judul selama 2,4 detik.
- Angka 3 detik dan 100 ms itu anjuran spec CSS. Tiap browser boleh sedikit beda.
auto(bawaan kalau gak ditulis) menyerahkan ke browser, dan kebanyakan browser memperlakukannya sepertiblock.- Dengan
optional, file font biasanya tetap di-download dan disimpan di cache. Halaman berikutnya di situs yang sama langsung pakai Inter.
Kode di latihan
<head>
<title>Blog Post</title>
<link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin>
<style>
@font-face {
font-family: "Inter";
src: url("/fonts/inter.woff2") format("woff2");
font-display: swap; /* teks langsung tampil pakai fallback */
}
body {
font-family: "Inter", system-ui, sans-serif;
}
</style>
</head>
@font-facemendaftarkan font custom: namanya (font-family: "Inter") dan file-nya (src). Mendaftarkan saja, belum dipakai elemen mana pun.format("woff2"): WOFF2 adalah format font terkompresi yang paling kecil, didukung semua browser modern.font-display: swapharus di dalam blok@font-faceyang sama. Di luar@font-face(misalnya dibody), browser mengabaikannya.font-family: "Inter", system-ui, sans-serifdibodymemakai font itu, dengan daftar cadangan.system-uiadalah font bawaan sistem: San Francisco di Mac dan iPhone, Segoe UI di Windows, Roboto di Android.sans-serifcadangan terakhir.rel="preload"menyuruh browser men-download file sekarang, sebelum dia sadar butuh.as="font"jenisnya,type="font/woff2"formatnya.crossoriginwajib untuk font, walau file-nya di domain sendiri. Font selalu diambil dengan mode CORS, dan preload tanpacrossorigingak cocok dengan permintaan aslinya, jadi font di-download dua kali.hrefdi preload danurl()disrcharus file yang sama persis.
Preload: font datang sebelum first paint
Dengan preload, browser mulai men-download font di 100 ms, waktu membaca <head>, bukan di 500 ms.
| 4G (300 ms) | 3G lambat (2.400 ms) | |
|---|---|---|
| tanpa preload | Inter tampil di 800 ms | Inter tampil di 2.900 ms |
| dengan preload | font datang di 400 ms, judul pakai Inter sejak first paint di 500 ms | Inter tampil di 2.500 ms |
Di 4G dengan preload, font sudah ada sebelum first paint, jadi gak ada FOIT atau FOUT sama sekali, nilai font-display apa pun. Di 3G, preload memajukan Inter 400 ms, dan swap tetap menjaga teks terbaca di 500 ms. Preload cuma font yang dipakai di layar pertama, satu atau dua file. Preload terlalu banyak bikin font berebut jalur dengan CSS dan gambar utama.
🎯 Biar gak loncat: fallback yang ukurannya mirip
FOUT punya efek samping: huruf system-ui lebih lebar atau lebih sempit dari Inter, jadi waktu font ditukar, baris teks bisa pindah dan konten di bawahnya bergeser. Pergeseran ini dihitung CLS. Obatnya, bikin font fallback yang ukurannya disamakan dengan Inter:
@font-face {
font-family: "Inter Fallback";
src: local("Arial");
size-adjust: 107%;
ascent-override: 90%;
descent-override: 22%;
line-gap-override: 0%;
}
body {
font-family: "Inter", "Inter Fallback", system-ui, sans-serif;
}
local("Arial") memakai Arial yang sudah ada di perangkat. size-adjust memperbesar hurufnya supaya selebar Inter, dan tiga *-override menyamakan tinggi baris. Angka di atas contoh kira-kira untuk Inter di atas Arial. Tool seperti Fontaine atau next/font menghitungnya otomatis.
📌 Bikin file font-nya kecil
- Self-host: taruh file font di domain sendiri. Sejak 2020 browser memisahkan cache per situs, jadi font dari Google Fonts gak lagi "sudah ada di cache dari situs lain". Self-host juga menghemat koneksi ke domain lain dan bisa di-preload.
- Subset dengan
unicode-range: file font lengkap berisi huruf ratusan bahasa. Pisahkan per kelompok huruf, dan browser cuma men-download yang dipakai halaman. Halaman berbahasa Indonesia cukup subset Latin, misalnyaunicode-range: U+0000-00FF;. - Variable font: satu file berisi semua ketebalan. Tulis
font-weight: 100 900;di@font-face, lalu pakai angka bebas sepertifont-weight: 650. Satu file variable biasanya lebih kecil dari lima file statis.
⚠️ Jebakan yang sering ketemu
- Preload tanpa
crossorigin. Gak ada error, tapi font-nya di-download dua kali dan preload-nya sia-sia. font-displayditaruh dibody. Itu descriptor milik@font-face. Di tempat lain dia diabaikan, dan teks tetap mengikuti perilakuauto.font-family: "Inter"tanpa cadangan. Selama menunggu, browser memakai font bawaannya (sering Times New Roman), jadi loncatannya besar. Selalu tutup dengansystem-ui, sans-serif.- Path preload beda dengan
src./fonts/inter.woff2dan/fonts/Inter.woff2dianggap file berbeda: preload-nya terbuang dan font di-download lagi.
TL;DR: Font custom belum ada sampai di-download. font-display memilih apa yang tampil selama menunggu: block menyembunyikan teks sampai sekitar 3 detik (FOIT), swap langsung pakai fallback lalu menukarnya (FOUT), fallback menyembunyikan 100 ms dan menukar cuma dalam 3 detik, optional menyembunyikan 100 ms lalu gak pernah menukar. Untuk teks yang harus cepat terbaca, pakai font-display: swap di dalam @font-face, font-family: "Inter", system-ui, sans-serif di body, dan <link rel="preload" as="font" type="font/woff2" crossorigin> dengan path yang sama dengan src. Di contoh ini, block di 3G lambat menyembunyikan judul sampai 2.900 ms, swap menampilkannya di 500 ms.