Fonts
Swap the default system fonts for a local or Google font.
Typography in Appica UI is driven by two design tokens — --font-sans and
--font-mono — exactly like colors and radii. Switching fonts is a two-part job:
load the font in your app, then point the token at it. The first part is
framework-specific; the second is one line of CSS that's the same everywhere.
The default fonts
Out of the box, @appica/ui-react/styles.css sets both tokens to native system
font stacks:
System fonts download nothing, render instantly, and never cause layout shift — a
good default. They feed Tailwind's font-sans / font-mono utilities through the
@theme inline block, and Tailwind's preflight applies --font-sans as the base
family for the whole document. So overriding the raw --font-sans token re-fonts
your entire UI — every component included.
Override the raw --font-sans token at :root, not its alias inside the @theme inline block. This mirrors how
Theming treats colors and radii — the theme layer is derived, so the raw token is the one to
redefine.
The one line that always applies
Whatever loads the font, you finish by redefining the raw token after the import, keeping a fallback stack so text stays readable while the web font loads:
The font name on the left of the fallback list must match whatever the loading step
below makes available — a font-family from @font-face, an @fontsource package,
or the CSS variable next/font generates. The rest of this guide is just the
different ways to get that name onto the page.
Loading the font
How you load a font depends on your framework. Next.js has a first-class font
pipeline (next/font); everywhere else you self-host with Fontsource
or a plain @font-face. All of these self-host the files, which is faster and more
private than linking to a third-party CDN.
Next.js — next/font
next/font downloads the font at build time, self-hosts it, and hands you a CSS
variable with zero layout shift. Use next/font/google for a Google font:
…or next/font/local for font files you ship yourself:
Either way, map the generated variable onto the Appica token:
Apply the variable, not the className
next/font only exposes --font-geist on the element you put geist.variable on (and its
descendants), so apply it to <html>. Don't also spread geist.className onto <body> — that hardcodes
the font on the body and bypasses the token, so component-level font-mono and any later override stop
working. Pick the variable approach and let --font-sans flow through.
Vite, TanStack Start, React Router 7 / Remix — Fontsource
These bundlers have no built-in font helper, so self-host with Fontsource — one npm package per family, imported once:
pnpm add @fontsource-variable/geistThe font-family Fontsource registers is shown on each font's page — variable fonts
use the … Variable suffix as above; static weights use the bare name ('Geist').
Any framework — local files with @font-face
If you'd rather not add a dependency, declare the face yourself and drop the files in
your public/ (or static/) folder. This works in every framework, including Astro:
Prefer self-hosting over Google's link tag
You can load a font with Google's <link href="https://fonts.googleapis.com/…"> snippet and it works
anywhere, but it adds a third-party request, an extra DNS round-trip, and sends your visitors' IPs to
Google. next/font, Fontsource, and @font-face all serve the files from your own origin — faster and
privacy-friendly. Reach for the link tag only as a quick prototype.
The monospace font
--font-mono works identically — load the font, then redefine the token. It feeds
font-mono, code blocks, and anything that opts into monospace:
Framework notes
The CSS override is identical everywhere; only the loading mechanism and where it lives change:
Whatever the source, the contract is the same: make a font-family name resolvable
on the page, then put it first in --font-sans / --font-mono with the original
stack trailing as a fallback.