Typografia i web fonty w Next.jsnext/font, subsetting i CLS bez regresji (2026)

typografia6 min czytania20 lipca 2026

Autor: DevStudio.it

TL;DR

Font z Google Fonts przez <link> w <head> to w 2026 roku regresja wydajności na autopilocie — dodatkowy DNS lookup, render-blocking CSS i CLS, gdy tekst „skacze" po załadowaniu wagi 600. next/font self-hostuje pliki w bundlu, ustawia font-display: swap i generuje CSS ze zmiennymi — bez requestu do fonts.googleapis.com. Subsetting do latin + latin-ext (polskie znaki ą, ę, ł) obcina 60–80% rozmiaru pliku względem pełnego unicode. Rezerwacja miejsca przez size-adjust lub stałe line-height na hero zapobiega layout shift. Hosting na DevStudioIT Cloud serwuje fonty z tego samego origin co HTML — jeden TLS, cache długoterminowy po buildzie.

Dla kogo to jest

  • Stron firmowych z custom typography w Figma — dev musi to odwzorować bez psucia LCP
  • Zespołów Next.js widzących CLS 0,15+ w Lighthouse mimo zoptymalizowanych obrazów
  • Projektów wielojęzycznych PL/DE z latin-ext — pełny charset bez zbędnych glifów CJK
  • Designerów wybierających 4 wagi fontu „bo wygląda" — trzeba uzasadnić budżet KB
  • Każdego po audycie CWV, gdzie „font loading" pojawia się w diagnostyce

Fraza (SEO)

next/font subsetting, web fonty wydajność, cls fonty nextjs, font-display swap, typografia strona firmowa, google fonts self hosting 2026

Problem: zewnętrzne fonty a Core Web Vitals

Klasyczny import:

<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet">

Łańcuch requestów:

Krok Opóźnienie Wpływ
DNS fonts.googleapis.com 20–80 ms TTFB HTML już minął
CSS font-face blocking lub FOIT Opóźniony pierwszy tekst
DNS fonts.gstatic.com kolejny RTT LCP na tekście = gorsze
Pobranie .woff2 400 + 600 + 700 150–400 KB Main thread decode

CLS powstaje, gdy fallback (Arial) ma inne metryki niż Inter — nagłówek hero zmienia wysokość o 4–12 px po swap.

next/font — self-hosting w Next.js App Router

// app/fonts.ts
import { Inter } from 'next/font/google';

export const inter = Inter({
  subsets: ['latin', 'latin-ext'],
  weight: ['400', '600'],
  display: 'swap',
  variable: '--font-inter',
  preload: true,
  adjustFontFallback: true,
});

Layout:

import { inter } from './fonts';

export default function RootLayout({ children }) {
  return (
    <html lang="pl" className={inter.variable}>
      <body className="font-sans antialiased">{children}</body>
    </html>
  );
}

Tailwind (tailwind.config.ts):

theme: {
  extend: {
    fontFamily: {
      sans: ['var(--font-inter)', 'system-ui', 'sans-serif'],
    },
  },
},
Opcja Efekt
subsets: [&#39;latin-ext&#39;] Polskie i niemieckie znaki diakrytyczne
weight: [&#39;400&#39;,&#39;600&#39;] Tylko używane wagi — każda waga = osobny plik
adjustFontFallback: true Automatyczny size-adjust na fallback
variable Jedna klasa CSS, brak osobnych @font-face w komponentach

Subsetting — co włączyć, co pominąć

Subset Kiedy Przybliżony rozmiar Inter 400
latin EN-only landing ~15 KB woff2
latin-ext PL, DE, CS, RO +8–12 KB
cyrillic Rynek UA/RU +20 KB — tylko jeśli locale
Pełny unicode ❌ unikaj 200 KB+

Dla DevStudio.it (pl/en/de) wystarczy [&#39;latin&#39;, &#39;latin-ext&#39;]. Nie importuj vietnamese „na wszelki wypadek".

Lokalny font (brand z licencją OTF):

import localFont from 'next/font/local';

export const brandSans = localFont({
  src: [
    { path: '../public/fonts/Brand-Regular.woff2', weight: '400' },
    { path: '../public/fonts/Brand-SemiBold.woff2', weight: '600' },
  ],
  display: 'swap',
  variable: '--font-brand',
});

Konwersja OTF → woff2: fonttools lub pipeline CI przed commitem.

CLS — rezerwacja miejsca poza next/font

adjustFontFallback pomaga, ale hero z dużym text-5xl i custom line-height nadal może shiftować:

.hero-title {
  font-size: clamp(2rem, 5vw, 3.5rem);
  line-height: 1.15;
  min-height: 2.3em; /* rezerwa na 2 linie */
}
Technika CLS Uwagi
font-display: optional Najniższy Ryzyko braku custom fontu na wolnej sieci
swap + size-adjust Niski Rekomendacja B2B
Preload tylko critical weight LCP lepsze preload: true w next/font domyślnie
System font stack na mobile Zero CLS Tradeoff brand vs metryki

Testuj mobile 4G throttling w Lighthouse — desktop ukrywa problem.

Typografia a LCP — gdy LCP to tekst

Na stronie z minimalnym hero LCP elementem może być nagłówek H1, nie obraz. Wtedy:

  1. Preload tylko waga użyta w H1 (400 lub 600)
  2. Unikaj @import fontów w CSS modułach — ładuje się późno
  3. Server Component na hero — HTML z fontem w pierwszym bajcie streamu

Font nie powinien być w osobnym Client Component lazy-loaded — to opóźnia LCP tekstu.

Wiele fontów — heading + body bez eksplozji KB

Pattern Pliki wwoff2 Rekomendacja
1 rodzina, 2 wagi 2 × ~20 KB ✅ domyślny wybór
Body + display (2 rodziny) 4 pliki OK jeśli display tylko H1–H2
4 wagi + 2 rodziny 8+ plików Review w performance budget

Display font na nagłówkach ładowany przez next/font z preload: false jeśli H1 poniżej fold na mobile — kontrowersyjne; lepiej zmniejszyć rozmiar display font subset.

Monitoring po deploy na DevStudioIT Cloud

Po wdrożeniu porównaj:

Źródło Metryka font-related
Lighthouse CI CLS, LCP, „Font display" audit
CrUX / Search Console Field CLS po 28 dniach
Web Vitals RUM layout-shift attributions

Regresja: designer dodał wagę 700 i italic „dla cytatu" — bundle +40 KB, CLS +0,05. Performance budget w CI powinien to złapać przy overall score; assert CLS bezpośrednio.

Handoff design → dev — typografia w Figma bez niespodzianek

Zanim developer implementuje fonty z mockupu, ustalcie tabelę decyzji:

Element Figma Implementacja Next.js Pytanie do designu
Inter 400 body next/font weight 400 Czy wystarczy?
Inter 600 nagłówki weight 600 Czy 700 jest konieczny?
Inter 700 CTA Osobny plik woff2 Czy 600 wystarczy na button?
Italic cytaty +plik italic Czy &lt;em&gt; w system font?

Design token w Figma powinien mapować 1:1 na fontFamily w Tailwind — unikacie sytuacji „w Figma jest Satoshi, na produkcji Arial bo licencja". Jeśli brand font wymaga licencji web, woff2 w repo przed startem sprintu, nie w ostatnim dniu przed go-live.

Line-height z Figma (np. 120%) przenieś na CSS z min-height rezerwą na wieloliniowe H1 — Figma nie mierzy CLS.

Typografia a dostępność — nie tylko KB

Wymóg Minimum Wpływ na fonty
Kontrast WCAG AA 4,5:1 body, 3:1 large text Cienka waga 300 na jasnym tle — unikaj
prefers-reduced-motion Brak animacji tekstu Nie animuj font-weight
Zoom 200% Tekst czytelny bez horizontal scroll rem zamiast sztywnego px na font-size

Custom font nie usprawiedliwia niskiego kontrastu — wybierz wagę 500/600 zamiast 300 na szarym tle.

FAQ

next/font google vs self-host pliki w public/?

next/font/google pobiera przy build i self-hostuje — równoważnik ręcznego public/, ale z automatycznym hash w nazwie pliku i zero konfiguracji CORS. Ręczny public/ tylko przy fontach spoza Google (brand OTF).

Czy variable fonts (.woff2 variable) zawsze mniejsze?

Często tak przy 3+ wagach — jeden plik zamiast trzech. Nie każda rodzina ma dobrze hintowaną zmienną wersję; testuj rozmiar i render na Windows.

Fonty z CDN third-party (Adobe Fonts, Fontshare)?

Każdy zewnętrzny origin to DNS + cache osobno. Dla CWV self-host po licencji. Adobe Fonts na stronie marketingowej z twardym budżetem LCP — ryzykowne.

Inter vs system-ui — kiedy rezygnować z custom?

Landing A/B testowany Ads z LCP > 3 s na mobile — wariant ze system-ui jako test. Jeśli konwersja bez różnicy, zostaw system stack.

Chcesz typografię bez regresji CLS?

Powiązane wpisy

Performance budget i Core Web Vitals dla zespołu — Lighthouse CI w praktyce (2026)
5 min czytania
Video hero a LCP — kiedy autoplay psuje wydajność strony firmowej (2026)
6 min czytania
Lighthouse CI — automatyczny audyt wydajności Next.js w GitHub Actions (2026)
9 min czytania

O autorze

Budujemy szybkie strony WWW, aplikacje web/mobile, chatboty AI i hosting — z naciskiem na SEO i konwersję.

Przydatne linki

Od teorii do produkcji — Branchly, hosting i realizacje.

Podoba Ci się nasze podejście? Zbudujmy coś razem.

Rozpocznij konfigurację projektu