Loading custom fonts
Browsers can decide to render HTML in multiple passes when custom fonts are used. They do this to speed up the time-to-first-meaningful-paint.
This behavior can cause a test to render without the custom font or use different fonts across repeated runs, making the test unstable. The following techniques can prevent that instability.
Best practice: Fall back to web-safe fonts
Web font loading can vary between browsers, versions, and operating systems. Web-safe fonts are commonly installed by default on browsers and operating systems. We recommend you include a web-safe font in your font stack as a fallback in case your web font isn’t available or doesn’t load as expected.
/* Fallback to "Courier New" for monospace fonts */
code {
font-family: 'Source Code Pro', 'Courier New', monospace;
}
Which web-safe fonts do you recommend for Chromatic?
- Sans-serif: Arial, Verdana, Trebuchet MS
- Serif: Georgia, Times New Roman
- Monospace: Courier New, Courier
- Non-Latin scripts: use a web-safe font that covers your target language (e.g.,
Noto Sans JPfor Japanese)
Solution A: Preload fonts
We recommend that you always ensure fonts are loaded before your tests are executed. With Storybook, you can preload fonts by specifying them in ./storybook/preview-head.html.
<link rel="preload" href="path/to/font.woff2" as="font" type="font/woff2" crossorigin="anonymous" />
Solution B: Point font-face declarations at static files
If your CSS has global @font-face declarations that point to a CDN, you may need to override them to ensure that your snapshots always use assets loaded locally.
For example, you might reference a font CDN in your stylesheets like this:
@font-face {
font-display: optional;
font-family: 'YourFont';
font-style: normal;
font-weight: normal;
src: url('https://cdn.yoursite.com/yourfont.woff2') format('woff2');
}
To serve the fonts statically, you first need to put your fonts in a static directory for Storybook. We recommend the ../public directory.
Next, create a yourfontface.css file inside your Storybook configuration directory (that is, .storybook). Use it to reference the local path for your font in the ../public directory.
@font-face {
font-display: optional;
font-family: 'YourFont';
font-style: normal;
font-weight: normal;
/* 👇 Change this to point at the local font path */
src: url('/yourfont.woff2') format('woff2');
}
Reference the stylesheet in Storybook’s preview-head.html configuration file to load the font from the local path.
<link rel="stylesheet" type="text/css" href="/yourfontface.css">
This technique loads a local font file during development and testing in Storybook. Meanwhile, your users are still loading the font from the CDN in production.
Solution C: Check that fonts have loaded in a loader
This alternate solution uses the browser’s font load API and the isChromatic() helper function to verify that fonts load when in the Chromatic environment.
import isChromatic from 'chromatic/isChromatic';
// Use the document.fonts API to check if fonts have loaded
// https://developer.mozilla.org/en-US/docs/Web/API/Document/fonts
const fontLoader = async () => ({
fonts: await Promise.all([document.fonts.load('400 1em Font Name')]),
// or
// fonts: await document.fonts.ready,
});
/* 👇 It's configured as a global loader
* See https://storybook.js.org/docs/writing-stories/loaders
* to learn more about loaders
*/
export const loaders = isChromatic() && document.fonts ? [fontLoader] : [];
Solution D: Don’t load fonts
As a last resort, you can also disable custom fonts by setting font-display: optional in your CSS when running in Chromatic.