Fonts and assets
Name the fonts in the theme, then export an optional font stylesheet that `theme add --import` imports with the built theme.Name the font
A theme names its typefaces in typography: body, heading, and code, each with a family and fallbacks. These set the --font-family-* tokens every component reads. Always give real fallbacks so text stays readable before the font loads, or if it never does. heading inherits family and fallbacks from body when you omit them.
ts// themes/ocean/oceanTheme.tsexport const oceanTheme = defineTheme({name: 'ocean',typography: {body: {family: 'Acme Sans', fallbacks: 'system-ui, sans-serif'},code: {family: 'Acme Mono', fallbacks: 'ui-monospace, monospace'},},// ...your tokens});
The full type scale and font roles are in astryx docs author-a-theme.
Ship the font loader
Naming a family does not load it. When the theme uses non-system fonts, add <slug>.fonts.css beside the built module and production CSS, then export it as ./themes/<slug>.fonts.css. theme add --import imports that stylesheet with the built theme.
json"exports": {"./themes/ocean": "./themes/ocean/ocean.js","./themes/ocean.css": "./themes/ocean/ocean.css","./themes/ocean.fonts.css": "./themes/ocean/ocean.fonts.css"}
The stylesheet can contain self-hosted @font-face rules or import a hosted stylesheet. A hosted @import is simple, but it delays loading compared with a preconnected <link>; choose that trade-off deliberately. If the app loads the same family outside the generated module, Doctor warns because it cannot prove the loader, but the warning does not fail the app.
css/* themes/ocean/ocean.fonts.css */@font-face {font-family: 'Acme Sans';src: url('./fonts/acme-sans.woff2') format('woff2');font-weight: 100 900;font-style: normal;font-display: swap;unicode-range: U+0000-00FF, U+0131, U+0152-0153;}
- Load every weight and style the theme uses. Do not let the browser synthesize bold or italic.
- Include self-hosted font files and their licenses in the packed package.
- Keep CSS and font assets side-effectful so a bundler does not remove the loader.
- Run
integration verify; it fails when an exported font stylesheet or one of its packed files cannot resolve.
Verify in an app
Before publishing, run integration verify. Then install the package in a clean app, run theme add --import, apply the generated theme, and open it in a browser. The source, packed exports, and applied result form one chain; check the last step too.
Textrenders in the named families at every weight and style — the italic face resolves, not a synthesized slant.- Every font request succeeds; nothing silently falls back to a system font.
- Color pairs still meet contrast in both light and dark mode.