preset-wind4: `font-*` utilities emit `var(--font-*)` without fallback — fonts silently break when the theme preflight is absent
Clear and concise description of the problem
As a developer using preset-wind4, font-sans / font-mono / font-serif compile to font-family: var(--font-sans) with no fallback, while the --font-* definitions are emitted separately by the on-demand theme preflight (:root, :host { --font-sans: ...; }). With default options this works fine. But any setup that suppresses preflights — preflights: { theme: false }, generate(code, { preflights: false }), or consuming split layers without the theme layer (e.g. bringing your own reset and only including the utilities layers) — produces declarations that are invalid at computed-value time, so the font silently falls back to the inherited one. No warning, no error — just a wrong font.
This is a regression-shaped footgun relative to preset-wind3, where .font-sans inlined the full family list and was safe under the same configurations. Verified on unocss 66.7.5 (and the behavior is present since wind4's introduction in 66.1.0).
Repro (66.7.5):
// uno.config.ts
import { defineConfig, presetWind4 } from 'unocss'
export default defineConfig({
presets: [presetWind4({ preflights: { theme: false } })],
})<div class="font-sans">hello</div>Output contains only:
.font-sans{font-family:var(--font-sans);}--font-sans is defined nowhere → invalid at computed-value time → inherited font. wind3 output for the same class: .font-sans{font-family:ui-sans-serif,system-ui,...}.
I know theme: false is marked "Not recommended ⚠️" in the option types — the concern here is the silent failure mode, and layer-splitting setups hit the same breakage without ever touching that option.
Suggested solution
Emit the theme value as a var() fallback in the utility, the same way the wind4 reset already guards the default font (font-family: var(--default-font-family, ui-sans-serif, system-ui, ...)):
.font-sans{font-family:var(--font-sans, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", ...);}Overriding via --font-sans keeps working identically; the fallback only kicks in when the var is absent. I'm happy to submit a PR for this if the direction is agreed.
Alternative
- Document loudly (preset-wind4 docs +
PreflightsThemeJSDoc) that thethemelayer is load-bearing forfont-*and other var-referencing utilities, and that dropping it silently breaks them. - Or emit a dev-time warning when a rule resolves a theme key to a CSS var while the theme preflight is disabled.
Additional context
Hit this in production: a project with its own reset/preflight setup shipped with every font-sans element silently rendering the browser default font.
Validations
- Read the Contributing Guidelines.
- Read the
README.mdof using the package. - Already used the Interactive Docs.
- Check that there isn't already an issue that request the same feature to avoid creating a duplicate.
Source: unocss/unocss