Font Families
Per-theme font families — body and heading — are defined as design tokens, bound as Figma Variables, and applied to every text node automatically.
Theme font matrix
| Theme | sans(body) | heading(h1–h6) |
|---|---|---|
| Primary | Open Sans | Open Sans |
| Secondary | Inter | Playfair Display |
If a theme does not define a heading font, sans is used as the fallback, which in turn falls back to the primary theme.
How it works
- 1
Font families are read from the plugin's configured token source mode (
auto,css,dtcg). Inauto, CSS wins and DTCG is fallback. - 2
Default CSS variables are
--font-sansand--font-heading. Legacy DTCG font tokens remain supported indtcgmode. - 3
Run
pnpm tokens:sync(orpnpm tokens:sync --from-css ...) when you want to refresh generated runtime artifacts / offline snapshot. - 4
When the plugin runs, it reads the token map, collects every distinct font family across all themes and roles, and pre-loads them via
figma.loadFontAsyncbefore any frames are drawn. - 5
Each text node resolves its font based on its HTML tag (
h1–h6→ heading role, everything else → sans) and the current theme being rendered. - 6
Font tokens are also written to the Figma Variables collection as STRING variables (
font/sans,font/heading), one per theme mode.
Token source format
In CSS-first mode, font tokens are plain CSS variables (default path src/app/tokens.css):
:root {
--font-sans: "Open Sans", ui-sans-serif, system-ui, sans-serif;
--font-heading: "Open Sans", ui-sans-serif, system-ui, sans-serif;
}
[data-theme="secondary"] {
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
--font-heading: "Playfair Display", Georgia, serif;
}DTCG remains supported in dtcg mode for teams that keep design-tokens/tokens.dtcg.json as part of their pipeline.
Figma Variables
Font tokens become STRING variables in the Design Tokens collection. On a paid Figma plan they are two modes (Primary / Secondary) in one collection. On a free plan they are split across two collections.
Note: Figma does not have a native font Variable type. STRING is used as the closest equivalent. The value stored is the first family name from the font stack (e.g. Open Sans) without fallbacks, since Figma resolves fonts by exact name rather than a CSS-style cascade.
Making fonts available in Figma
The plugin calls figma.loadFontAsync before rendering. A font loads successfully if any of these is true:
Figma has a built-in Google Fonts integration. Open the text tool in Figma, click the font picker, and search for your font — enabling it there makes it available to the plugin automatically.
Fonts installed on your machine are available in Figma desktop. Install the font via your OS font manager and restart Figma.
Paid Figma plans allow uploading custom font files directly to the organization. Fonts uploaded there are available to all members and plugins.
If a configured font cannot be loaded, the plugin shows a notification in Figma listing the missing families and falls back to Inter.
Known limitations
- Font must be available in Figma. If the font is not accessible via Google Fonts, system install, or Organization upload, loading fails and the renderer falls back to Inter. A notification lists any missing fonts.
- Only the first family in the stack is used. CSS font stacks like
"Open Sans", ui-sans-serif, system-uiare trimmed to the first entry — Figma resolves fonts by exact name, not as a cascade. - Font style names vary by family in Figma. The plugin resolves nearest weight/style per family, but if a family exposes only condensed/limited variants, Figma may still map to the closest available style.
- The
monorole is defined but not rendered. You can add amonofont token and it will appear as a Figma Variable, but the renderer does not currently apply it to<code>or<pre>elements. - Custom font tokens are not passed to CSS utility classes. The CSS output sets
--font-sansand--font-headingcustom properties, but Tailwind'sfont-sansutility uses its own config value — you need to referencevar(--font-sans)in yourtailwind.configto wire them together.