Docs

Fonts, paired with care

59 Google Fonts families in 7 categories, 16 curated pairings, and a loader that requests only what a page uses. Every family covers latin-ext, so Turkish ğ ş ı İ render in the chosen face.

CategoryFamiliesUse for
Sans23Geometric, humanist and grotesk faces for UI and body text
Serif13Text serifs for long reads, display serifs for headlines
Display4Posters, campaigns, one-word headlines
Rounded4Friendly products, education, health
Condensed5Tight headlines, stat-heavy headers, dense tables
Mono7Code, IDs, invoice numbers, tabular figures
Handwriting3Annotations and small accents — never body text

Catalog

Every row is set in its own face. Open one to load the full family and see each weight. The list comes from googleFonts, with weights, italics, variable axes, subsets and a short note per family — verified against the Google Fonts metadata.

Sans · Geometric7

Sans · Humanist7

Sans · Grotesk9

Serif · Text7

Serif · Display6

Display4

Rounded4

Condensed5

Mono7

Handwriting3

Pairings

A pairing is a heading face, a body face, a mono face and the character of display headings: weight, tracking, how <em> looks and a size scale (wide faces need less size). editorial, grotesk, corporate and system are also data-font presets in styles.css.

Editorial · Instrument Serif / Onest
Quarterly report, rewritten for humans

Condensed serif headlines over a warm sans. The house style. Çalışma takvimi, izin talepleri ve bordro özetleri aynı yerde.

Tokens

TokenSet fromRead by
--font-uipairing.bodyfont-sans (body, controls)
--font-headingpairing.headingfont-display
--font-codepairing.monofont-mono, tabular numbers
--display-weight · --display-trackingpairing.display.font-display
--display-empairing.display.em.font-display em
--display-scalepairing.display.scale--text-display-* sizes

Loading fonts

loadGoogleFonts adds one <link rel="stylesheet"> for the families a page has not requested yet, plus preconnect hints for both Google origins. Calling it again with the same families does nothing.

import { loadGoogleFonts, applyFontPairing, getFontPairing } from "@vamos-ui/react/fonts";

await loadGoogleFonts(["Fraunces", "Figtree"], { display: "swap" });

// Or apply a whole pairing to <html> (data-font + tokens + fonts):
await applyFontPairing(document.documentElement, getFontPairing("boutique")!);

On the server, render the link yourself with the same URL:

import { googleFontsUrl } from "@vamos-ui/react/fonts";

<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
<link rel="stylesheet" href={googleFontsUrl(["Newsreader", "Source Sans 3"])} />

For one section only — a website preview inside the CMS, an email template — wrap it in FontProvider. The rest of the panel keeps its fonts.

import { FontProvider, createFontPairing } from "@vamos-ui/react/fonts";

const brand = createFontPairing({ heading: "Young Serif", body: "Plus Jakarta Sans", mono: "JetBrains Mono" });

<FontProvider pairing={brand}>
  <HomepagePreview />
</FontProvider>

Font picker

FontPicker and FontPairingPicker let editors choose. The theme switcher in this site's header uses both: pick a pairing, or open Custom and mix any three families.

No flash on reload

Store the resolved pairing with serializeFontPairing; it keeps the stylesheet URL and the token values, so a tiny inline script can apply them before first paint without shipping the catalog. The link is marked blocking="render", so supporting browsers wait for the CSS (not the font files) and font-display: swap covers the rest.

import { fontBootScript, serializeFontPairing } from "@vamos-ui/react/fonts";

// When the user picks:
localStorage.setItem("vamos-theme", JSON.stringify({ ...theme, fontPairing: serializeFontPairing(pairing) }));

// app/layout.tsx
<head>
  <script dangerouslySetInnerHTML={{ __html: fontBootScript("vamos-theme", "fontPairing") }} />
</head>

Privacy

Google Fonts sees your visitors' IP addresses
Fetching CSS and font files from fonts.googleapis.com and fonts.gstatic.com sends each visitor's IP address and user agent to Google. Under GDPR and KVKK that is a transfer of personal data; a German court has fined a site for it. For public sites, self-host the files and keep the CDN for internal tools and the picker in your admin.

Self-hosted fonts also skip two connections. Turn the CDN off with load={false} on FontProvider and loadPreviews={false} on the pickers.

Self-hosting with next/font

next/font/google downloads the files at build time and serves them from your domain — no request reaches Google at runtime. Define the three families, expose them as CSS variables and point the tokens at them.

app/fonts.ts
import { Fraunces, Figtree, DM_Mono } from "next/font/google";

// latin-ext keeps ğ ş ı İ in the same face.
export const heading = Fraunces({ subsets: ["latin", "latin-ext"], style: ["normal", "italic"], axes: ["opsz"], variable: "--ff-heading" });
export const body = Figtree({ subsets: ["latin", "latin-ext"], variable: "--ff-body" });
export const mono = DM_Mono({ subsets: ["latin", "latin-ext"], weight: ["400", "500"], variable: "--ff-mono", preload: false });
app/layout.tsx
import { heading, body, mono } from "./fonts";
import { fontPairings, pairingVars } from "@vamos-ui/react/fonts";

const boutique = fontPairings.find((p) => p.id === "boutique")!;
// Keep the pairing's display tokens, swap the families for the self-hosted variables.
const vars = { ...pairingVars(boutique), "--font-heading": "var(--ff-heading)", "--font-ui": "var(--ff-body)", "--font-code": "var(--ff-mono)" };

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" data-font="boutique" style={vars as React.CSSProperties} className={[heading, body, mono].map((f) => f.variable).join(" ")}>
      <body className="font-sans">{children}</body>
    </html>
  );
}

Not on Next.js? Download the woff2 files (the URLs are in the CSS that googleFontsUrl returns), serve them from your own origin with @font-face rules, and set the same three tokens.