# Customize Application Fonts

> Configure custom fonts using Google Fonts, local fonts, or system fonts in your Makerkit application with Tanstack Start font optimization.

*Canonical: https://makerkit.dev/docs/tanstack-supabase/customization/fonts*

---

Customize your application's typography by editing the CSS in `apps/web/src/styles.css` and the font tokens in `apps/web/src/styles/shadcn-ui.css`. Fonts are self-hosted via [Fontsource](https://fontsource.org) packages — installed from npm and imported directly in CSS — for privacy and performance, with no runtime font-loading component.

By default, Makerkit uses Apple's system font on Apple devices (San Francisco) and falls back to Inter (via `@fontsource-variable/inter`) on other platforms.

## How Fonts Are Wired

There is no `fonts.ts` module. The font system is entirely CSS-based and lives in two files:

`apps/web/src/styles.css` imports the self-hosted font package and declares the fallback font stack that the design tokens map onto:

```css title="apps/web/src/styles.css"
/* Sans font (self-hosted via Fontsource, weights 300–700) */
@import '@fontsource-variable/inter';

/* The sans font stack the design tokens map onto. */
:root {
  --font-sans-fallback:
    'Inter Variable', system-ui, 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
```

`apps/web/src/styles/shadcn-ui.css` maps that fallback into the Tailwind `@theme inline` tokens:

```css title="apps/web/src/styles/shadcn-ui.css"
@theme inline {
  --font-sans: -apple-system, BlinkMacSystemFont, var(--font-sans-fallback);
  --font-mono:
    ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
  --font-heading: var(--font-sans);
}
```

## Quick Font Change

Replace the default Inter font with any Google Font using its Fontsource package.

1. Install the package (variable fonts are preferred — a single file supports every weight):

```bash
pnpm --filter web add @fontsource-variable/poppins
```

2. Swap the `@import` and update `--font-sans-fallback` in `apps/web/src/styles.css`:

```css title="apps/web/src/styles.css"
@import '@fontsource-variable/poppins';

:root {
  --font-sans-fallback:
    'Poppins Variable', system-ui, 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
```

That's it — every component that consumes `--font-sans` now renders in Poppins.

## Using Different Fonts for Headings and Body

Create visual hierarchy by using different fonts for headings and body text.

1. Install both packages:

```bash
pnpm --filter web add @fontsource-variable/inter @fontsource-variable/playfair-display
```

2. Import both and declare a heading fallback variable in `apps/web/src/styles.css`:

```css title="apps/web/src/styles.css"
@import '@fontsource-variable/inter';
@import '@fontsource-variable/playfair-display';

:root {
  --font-sans-fallback:
    'Inter Variable', system-ui, 'Helvetica Neue', Helvetica, Arial, sans-serif;
  --font-heading-fallback:
    'Playfair Display Variable', Georgia, 'Times New Roman', serif;
}
```

3. Point the `--font-heading` token at your heading stack in `apps/web/src/styles/shadcn-ui.css`:

```css title="apps/web/src/styles/shadcn-ui.css"
@theme inline {
  --font-sans: -apple-system, BlinkMacSystemFont, var(--font-sans-fallback);
  --font-heading: var(--font-heading-fallback);
}
```

## Using Local Fonts

For fonts not available on Fontsource, or for complete control over the font files, use a plain CSS `@font-face` declaration.

1. Place your font files under `apps/web/public/fonts/`. Supported formats: `.woff2` (recommended), `.woff`, `.ttf`, `.otf`.

2. Declare the faces in `apps/web/src/styles/custom.css` (or any CSS file imported from `styles.css`):

```css title="apps/web/src/styles/custom.css"
@font-face {
  font-family: 'CustomFont';
  src: url('/fonts/CustomFont-Regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'CustomFont';
  src: url('/fonts/CustomFont-Medium.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'CustomFont';
  src: url('/fonts/CustomFont-Bold.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
```

3. Reference it in `--font-sans-fallback` in `apps/web/src/styles.css`:

```css title="apps/web/src/styles.css"
:root {
  --font-sans-fallback:
    'CustomFont', system-ui, 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
```

## Removing Apple System Font Default

By default, Makerkit prioritizes Apple's system font on macOS and iOS for a native feel. To use your chosen font consistently across all platforms, edit the `@theme inline` block in `apps/web/src/styles/shadcn-ui.css`:

```css title="apps/web/src/styles/shadcn-ui.css"
@theme inline {
  /* Remove -apple-system and BlinkMacSystemFont to use your font everywhere */
  --font-sans: var(--font-sans-fallback);
  --font-heading: var(--font-sans);
}
```

This ensures your custom font displays on Apple devices instead of San Francisco.

## Popular Font Combinations

### Modern SaaS (Clean and Professional)
```bash
pnpm --filter web add @fontsource-variable/inter
# Headings and body: Inter
```

### Editorial (Content-Heavy Apps)
```bash
pnpm --filter web add @fontsource-variable/source-sans-3 @fontsource-variable/source-serif-4
# Body: Source Sans 3
# Headings: Source Serif 4
```

### Startup (Friendly and Approachable)
```bash
pnpm --filter web add @fontsource-variable/dm-sans
# Headings and body: DM Sans
```

### Technical (Developer Tools)
```bash
pnpm --filter web add @fontsource-variable/ibm-plex-sans @fontsource/ibm-plex-mono
# Body: IBM Plex Sans
# Code: IBM Plex Mono
```

### Premium (Luxury/Finance)
```bash
pnpm --filter web add @fontsource-variable/outfit
# Headings and body: Outfit
```

## Font Variable Reference

The font system uses CSS variables defined in two places:

| Variable | Defined In | Purpose |
|----------|------------|---------|
| `--font-sans-fallback` | `styles.css` | Self-hosted Fontsource (or local) font stack |
| `--font-heading` | `shadcn-ui.css` | Heading font (if different) |
| `--font-sans` | `shadcn-ui.css` | Final font stack with system fallbacks |

Tailwind consumes these through the `@theme inline` block:

```css title="apps/web/src/styles/shadcn-ui.css"
@theme inline {
  --font-sans: -apple-system, BlinkMacSystemFont, var(--font-sans-fallback);
  --font-heading: var(--font-sans);
}
```

## Optimizing Font Loading

### Prefer Variable Fonts

Fontsource ships variable font packages (`@fontsource-variable/<name>`) that pack every weight into a single file, usually smaller than multiple static weights combined:

```css
/* One import covers 300–700 and everything in between */
@import '@fontsource-variable/inter';
```

### Import Only the Weights You Use

For static (non-variable) Fontsource packages, import only the weights you actually need instead of the whole family:

```css
/* Avoid importing the entire family */
@import '@fontsource/roboto/400.css';
@import '@fontsource/roboto/500.css';
@import '@fontsource/roboto/700.css';
```

### Use `font-display: swap`

Fontsource packages already default to `font-display: swap`, showing a fallback immediately and swapping when the font loads. For local `@font-face` declarations, set `font-display: swap` yourself (as shown above) to avoid invisible text (FOIT).

## Common Mistakes

**Loading too many font weights**: Each static weight adds to bundle size. Prefer a single variable-font import, or only import the weights you use (typically 400, 500, 600, 700).

**Forgetting to update the token**: After changing the `@import` in `styles.css`, update `--font-sans-fallback` (and `--font-heading` in `shadcn-ui.css` if you use a separate heading font), otherwise the tokens still point at the old family.

**Using `font-display: block`**: This causes invisible text until fonts load (FOIT). Use `swap` for better perceived performance.

**Not testing on Windows**: Apple system fonts don't exist on Windows. Always test your fallback fonts on non-Apple devices.

## Verification

After updating fonts:

1. Check the Network tab in DevTools for font files loading
2. Verify fonts render on both Mac and Windows
3. Test with slow network throttling to see fallback behavior
4. Run Lighthouse to check for font-related performance issues

```bash
# Quick check for font loading
pnpm dev
# Open DevTools > Network > Filter: Font
# Verify your custom font files are loading
```

{% faq
   title="Frequently Asked Questions"
   items=[
     {"question": "Why does my custom font not appear on Mac?", "answer": "By default, Makerkit prioritizes Apple system fonts. Edit shadcn-ui.css and remove -apple-system and BlinkMacSystemFont from the --font-sans token to use your custom font on Apple devices."},
     {"question": "How do I add a monospace font for code blocks?", "answer": "Install a monospace Fontsource package (e.g. @fontsource-variable/jetbrains-mono), @import it in styles.css, and set the --font-mono token in shadcn-ui.css. Consider IBM Plex Mono, JetBrains Mono, or Fira Code."},
     {"question": "Can I use variable fonts?", "answer": "Yes. Prefer the @fontsource-variable/<name> packages — a single file supports every weight, often smaller than multiple static font files."},
     {"question": "How do I improve font loading performance?", "answer": "Prefer variable fonts, import only the weights you use for static packages, and rely on font-display: swap (the Fontsource default)."}
   ]
/%}

## Next Steps

- Back to [Customization Overview](/docs/tanstack-supabase/customization)
- Configure your [theme colors](/docs/tanstack-supabase/customization/theme) to complement your typography
- Set up your [layout style](/docs/tanstack-supabase/customization/layout-style) for navigation
- Update your [application logo](/docs/tanstack-supabase/customization/logo)
