Compress Fonts

Font System

Complete guide to managing fonts in Speedwell.

_scripts/compress-fonts.js

Overview

Speedwell uses a streamlined font system that converts TTF fonts to optimized WOFF2 format and automatically configures them for Next.js.


Usage

npm run fonts <folder> [fontTypes...]

Examples

Compress fonts only:

npm run fonts Switzer

Compress and update font configuration:

npm run fonts Switzer heading heading2 heading3

This will:

  • Convert src/styles/fonts/Switzer/*.ttf to WOFF2
  • Update _data/_fonts/_heading.tsx
  • Update _data/_fonts/_heading2.tsx
  • Update _data/_fonts/_heading3.tsx

Adding Custom Fonts

1. Place TTF Files

Add your font files to src/styles/fonts/YourFontName/:

src/styles/fonts/
└── Switzer/
    ├── Switzer-Regular.ttf
    ├── Switzer-Bold.ttf
    ├── Switzer-Italic.ttf
    └── Switzer-BoldItalic.ttf

2. Run Compression

# Compress only
npm run fonts Switzer
# Compress and update font config files
npm run fonts Switzer heading heading2 heading3

3. Font is Automatically:

  • ✅ Converted to WOFF2 (90%+ smaller file size)
  • ✅ Weight and style detected from filename
  • ✅ Configuration updated in _data/_fonts/

Font File Naming

The system detects font properties from filenames:

Weights

Filename ContainsWeightCSS Value
Thin100font-weight: 100
ExtraLight200font-weight: 200
Light300font-weight: 300
Regular400font-weight: 400
Medium500font-weight: 500
Semibold600font-weight: 600
Bold700font-weight: 700
Extrabold800font-weight: 800
Black900font-weight: 900

Styles

Filename ContainsStyleCSS Value
Italicitalicfont-style: italic

Examples

FontName-Regular.ttf      → weight: 400, style: normal
FontName-Bold.ttf         → weight: 700, style: normal
FontName-BoldItalic.ttf   → weight: 700, style: italic
FontName-Light.ttf        → weight: 300, style: normal
FontName-Semibold.ttf     → weight: 600, style: normal

Variable Fonts

FontName-Variable.ttf     → weight: 100-900

Variable fonts support the full weight range in a single file.


Font Configuration Files

Fonts are organized by usage in _data/_fonts/:

FileUsageHTML Elements
_heading.tsxMain headings<h1>
_heading2.tsxSubheadings<h2>, <h3>
_heading3.tsxSmall headings<h4>, <h5>, <h6>
_body.tsxBody text<p>, <span>, etc.
_accent.tsxSpecial textCustom components

Example Configuration

// _data/_fonts/_heading.tsx
import localFont from 'next/font/local'
export const heading = localFont({
src: [
{
path: '../../src/styles/fonts/Switzer/Switzer-Regular.woff2',
weight: '400',
style: 'normal',
},
{
path: '../../src/styles/fonts/Switzer/Switzer-Bold.woff2',
weight: '700',
style: 'normal',
},
],
variable: '--font-heading',
})

Using Fonts in Components

Automatic Application

Once configured, fonts are applied automatically through CSS variables:

h1 {
font-family: var(--font-heading);
}
h2,
h3 {
font-family: var(--font-heading2);
}
h4,
h5,
h6 {
font-family: var(--font-heading3);
}
body {
font-family: var(--font-body);
}

Manual Application

import { heading } from '@/_data/_fonts/_heading'
export default function Hero() {
return <h1 className={heading.className}>Welcome</h1>
}

With Tailwind

<h1 className="font-heading font-bold">Welcome</h1>

Configure in tailwind.config.js:

module.exports = {
theme: {
extend: {
fontFamily: {
heading: ['var(--font-heading)'],
heading2: ['var(--font-heading2)'],
heading3: ['var(--font-heading3)'],
body: ['var(--font-body)'],
accent: ['var(--font-accent)'],
},
},
},
}

Font Formats

WOFF2

Why WOFF2?

  • Smaller - 30% smaller than WOFF, 90% smaller than TTF
  • Faster - Reduced download time
  • Compatible - Supported by all modern browsers
  • Optimized - Built-in compression

Browser Support

WOFF2 is supported in:

  • Chrome 36+
  • Firefox 39+
  • Safari 10+
  • Edge 14+

For older browsers, consider adding TTF fallback.


Complete Workflow Example

1. Download Font

Get your font files from:

  • Google Fonts
  • Adobe Fonts
  • Commercial font foundries
  • Custom design

2. Organize Files

mkdir -p src/styles/fonts/MyFont
mv MyFont-*.ttf src/styles/fonts/MyFont/

3. Compress & Configure

npm run fonts MyFont heading heading2 body

4. Verify Output

Check that files were created:

src/styles/fonts/MyFont/
├── MyFont-Regular.woff2
├── MyFont-Bold.woff2
└── ...

_data/_fonts/
├── _heading.tsx
├── _heading2.tsx
└── _body.tsx

5. Test in Browser

npm run dev

Visit your site and inspect elements to verify fonts loaded.


Font Loading Strategy

Next.js automatically optimizes font loading:

  • Preloads critical fonts
  • Subsetting - Only includes used characters
  • Self-hosted - No external requests
  • Cache-optimized - Fonts cached by browser

Loading Performance

export const heading = localFont({
src: './fonts/heading.woff2',
display: 'swap', // Show fallback while loading
preload: true, // Load immediately
variable: '--font-heading',
})

Directory Structure

src/styles/fonts/
  Switzer/              # Font family folder
    Switzer-Regular.ttf
    Switzer-Bold.ttf
    ...
  Geist/
    GeistVF.ttf
    ...

_data/_fonts/           # Generated configs
  _heading.tsx
  _heading2.tsx
  _body.tsx
  ...

Troubleshooting

Fonts not appearing

Check:

  • Font files are in correct directory
  • npm run fonts completed successfully
  • WOFF2 files were generated
  • Font config files updated
  • CSS variables defined in layout
  • Clear browser cache

Compression failed

Possible causes:

  • Invalid TTF file
  • File permissions issue
  • Missing ttf2woff2 dependency

Solution:

npm install ttf2woff2 --save-dev

Wrong weights detected

Fix filename:

# Wrong
mv Font-Reg.ttf Font-Regular.ttf
# Wrong
mv Font-B.ttf Font-Bold.ttf

Weight detection requires specific keywords in filename.


Best Practices

  • Use variable fonts when possible - Smaller file size for multiple weights
  • Limit font weights - Only include weights you actually use
  • Subset fonts - Remove unused characters for smaller files
  • Self-host - Don't rely on external CDNs
  • Preload critical fonts - Heading fonts should be preloaded

Advanced: Custom Font Configuration

Manual Configuration

Edit _data/_fonts/_heading.tsx directly:

import localFont from 'next/font/local'
export const heading = localFont({
src: [
{
path: '../../src/styles/fonts/Custom/Custom-Light.woff2',
weight: '300',
style: 'normal',
},
{
path: '../../src/styles/fonts/Custom/Custom-Regular.woff2',
weight: '400',
style: 'normal',
},
{
path: '../../src/styles/fonts/Custom/Custom-Bold.woff2',
weight: '700',
style: 'normal',
},
],
variable: '--font-heading',
display: 'swap',
preload: true,
})

Font Subsetting

For smaller file sizes, subset fonts to include only needed characters:

# Using pyftsubset (install fonttools)
pyftsubset font.ttf \
--output-file=font-subset.ttf \
--unicodes=U+0020-007F,U+00A0-00FF
# Then convert to WOFF2
npm run fonts SubsetFont heading

When to Run

  • After adding new font files
  • After updating font weights
  • When setting up fonts for the first time
  • Before committing font changes

← Back to README

Support

Talk to the developers of this project to learn more

We have been building professional websites for big clients for over 15 years. Gallop templates and blocks is our best foundation for SEO websites and web apps.

© 2025 Web Plant Media, LLC