ScreenshotNeo

BlogHow-to

How to Use Custom Fonts in CSS

Load a custom font with CSS @font-face, choose a loading strategy, and keep text readable with reliable fallbacks.

By the ScreenshotNeo team4 October 20268 min read

To use a custom font in CSS, define it with @font-face, point src to a web font file, and use the declared family name in a font-family stack with fallback fonts. WOFF2 is a sensible modern default. Choose a font-display behavior deliberately, describe each file’s actual weight and style, and confirm that its license permits web use.

1. Add a custom font with @font-face

For a self-hosted font, place the licensed WOFF2 file in your site’s public assets, then define its face in a stylesheet:

/* CSS */
@font-face {
  font-family: "Example Sans";
  src: url("/fonts/example-sans-regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

body {
  font-family: "Example Sans", Arial, sans-serif;
}

The font-family name declared inside @font-face is the name you use in the regular font-family property. The URL is resolved relative to the CSS file for a relative URL; a root-relative URL such as /fonts/... starts at the site root. Adjust the path to match your deployment.

Load static weights and styles accurately

Each static font file describes a particular weight and style. Register separate faces for separate files, and keep the descriptors accurate so the browser can choose the intended face:

@font-face {
  font-family: "Example Sans";
  src: url("/fonts/example-sans-regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Example Sans";
  src: url("/fonts/example-sans-bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Example Sans";
  src: url("/fonts/example-sans-italic.woff2") format("woff2");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}

body {
  font-family: "Example Sans", Arial, sans-serif;
  font-weight: 400;
}

strong {
  font-weight: 700;
}

em {
  font-style: italic;
}

If you use a variable font, its file can represent a range of weights. Declare the range it supports, for example font-weight: 100 900, only when that range is accurate for the file. Do not label a single regular file as every weight and style: that can cause the browser to select or synthesize a face that does not match the intended design.

2. Choose a font format and delivery method

WOFF2 and compatibility

WOFF2 is efficiently compressed and broadly supported in modern browsers, making it a sensible starting point. Check the browser support your project actually needs. Add a legacy format only when you have a concrete compatibility requirement; avoid serving extra files without a reason. MDN’s web fonts guide explains font formats and browser delivery.

If you must offer a fallback format, list it after WOFF2 in src, with the appropriate format hint:

@font-face {
  font-family: "Example Sans";
  src:
    url("/fonts/example-sans-regular.woff2") format("woff2"),
    url("/fonts/example-sans-regular.woff") format("woff");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

Self-hosting or a hosted font service

With self-hosting, your stylesheet points to files on your own site or asset host. A hosted font service supplies CSS and font resources that the browser retrieves. Both approaches can work. Compare the control you need over files and updates, the network requests involved, caching behavior, compatibility requirements, and the provider’s terms. There is no universal performance or privacy winner; assess the actual setup and requirements.

For a hosted service, follow its current instructions for loading its CSS and using the provided family name. For self-hosted files, make sure your server returns the correct file at the URL in src. Google’s technical documentation describes the browser retrieving CSS and then an appropriate font resource.

3. Keep text visible while the font loads

The font-display descriptor controls how text is rendered before and after the web font downloads. MDN documents auto, block, swap, fallback, and optional; they make different tradeoffs in whether fallback text appears immediately and whether the browser swaps to the downloaded face.

  • swap makes the fallback available while the custom face loads, then swaps when the face is ready. It is a practical example, not a universal prescription.
  • block can leave text temporarily invisible while the browser waits for the font.
  • fallback and optional use different time limits and swap behavior; choose them based on the visual behavior you want under slow or failed downloads.
  • auto leaves the behavior to the browser.

Keep a fallback stack even when using a display strategy that normally shows the custom face quickly. The file can fail, arrive late, or lack a glyph used by the page. Use a generic family such as serif or sans-serif at the end of the stack.

4. Support languages and character coverage

A font may not contain every character your site uses. A fallback family allows missing glyphs to render from another available font. If you use split font subsets, ensure the set of files collectively covers the characters visitors need. The unicode-range descriptor can tell the browser which characters a face covers, so it can select the relevant subset.

@font-face {
  font-family: "Example Sans";
  src: url("/fonts/example-sans-latin.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF;
}

This range is only an example. Use ranges that match the contents of your actual subset files and the scripts used by the site. Incorrect ranges can leave some characters without the intended face.

5. Check font licensing before deployment

Confirm that the font’s specific license permits web use and your intended delivery method. A license for desktop use does not necessarily authorize embedding a font on a website or redistributing it. Terms may differ by file format or provider. Do not infer permission simply because a file is available to download. See MDN’s font licensing guidance and the font’s own license terms.

6. Troubleshoot fonts that do not appear

Symptom Likely cause What to check
The page always shows the fallback The file URL is wrong, the request fails, or the face declaration does not match the CSS usage. Open the font request in the browser’s Network panel. Check its status and URL, then compare the family, weight, and style in @font-face with the element’s computed styles.
The request returns not found The deployed file path differs from the stylesheet URL, often because a relative path was resolved from an unexpected location. Check the final request URL and deployed asset path. Use a root-relative path if the font is served from the site root.
A cross-origin font is blocked The font server has not allowed the page’s origin through its HTTP access-control response. Inspect the console and response headers. Confirm the file URL and configure the font server’s CORS response as required for your origin.
Bold or italic text uses an unexpected face The corresponding weight or style file is missing, or its descriptors are inaccurate. Register the actual bold or italic file with its correct font-weight and font-style.
Some letters use a different typeface The custom font or selected subset lacks those glyphs. Check the font’s character coverage and subset ranges. Add appropriate coverage and retain a fallback family.
Text changes appearance after loading The browser swaps from the fallback to the downloaded face according to font-display. Choose the display behavior that fits the desired loading experience and verify the fallback stack. The display choice affects when a face appears; it cannot make a missing font file load.

7. Performance, reliability, and cost

Font delivery performance depends on the files, requests, caching, network, and delivery setup. WOFF2’s efficient compression is a useful default, but this guide makes no universal speed claim for self-hosting versus hosted services. Avoid requesting unnecessary formats or weights, and ensure that visitors have a fallback if the font cannot be fetched. Consider the behavior of font-display on slow connections and check actual requests in the browser’s developer tools.

Reliability depends on the font URL remaining available and returning a usable file, as well as any required cross-origin access being configured. A hosted provider and a self-hosted setup have different operational dependencies; review the provider’s terms and the control you need over updates and delivery.

There is no single cost model established here. Check the font’s license and any hosted provider’s terms for the cost and permitted use that apply to your project. The font loading method does not change the need for a valid license.

8. Capture a screenshot to check typography

After adding the font, inspect the page in a browser with the expected viewport and check that the intended face, weights, styles, and fallback behavior appear. A screenshot can help you review a page’s visual layout; it does not replace checking the browser’s network and console for font-loading errors.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request can return a PNG, JPEG, WebP, or PDF. It can capture a page so you can review how its typography renders:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

See the ScreenshotNeo API documentation for request options. ScreenshotNeo accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, with response headers showing the page verdict and billing status. Its MCP server gives AI agents tools to take screenshots, get page info, and capture PDFs. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.

Sign up for 1,000 free screenshots a month, with no card required.

FAQ

Can I use a local font installed on my computer?

A visitor’s locally installed font may be used through a local font reference, but it is not a reliable way to provide a web font to every visitor. Use a properly licensed web font file or a hosted font service when the site needs to deliver the typeface consistently.

Does @font-face automatically make every weight available?

No. Register each static file with the weight and style it represents, or declare an accurate range for a variable font. A fallback font remains useful if the requested face is unavailable.

Should every project use font-display: swap?

No. swap is one documented option. Choose among the available values based on whether you prioritize immediately visible fallback text, a brief period before display, or limiting late swaps.

Is a downloadable font automatically free to embed?

No. Check the font’s license for web embedding, redistribution, and the delivery method you plan to use.

Sources