Skip to content

Web renderer treats system font tokens as literal families, causing serif fallback #140

Description

@clholgat

Summary

On web, Valdi's abstract system font names are emitted as literal CSS font-family values. For example, font: system 18 becomes font-family: system, and font: system-bold 18 becomes font-family: system-bold. Browsers do not recognize either family, so visible text can fall back to a serif face; bold text also reports normal weight.

The same TSX renders with the expected platform sans-serif font on native targets.

Minimal reproduction

import { Style } from 'valdi_core/src/Style';
import { systemBoldFont, systemFont } from 'valdi_core/src/SystemFont';
import { Label } from 'valdi_tsx/src/NativeTemplateElements';

const regular = new Style<Label>({ font: systemFont(18) });
const bold = new Style<Label>({ font: systemBoldFont(18) });

<label style={regular} value="Regular system text" />;
<label style={bold} value="Bold system text" />;

In Chromium, getComputedStyle() reports system and system-bold as the font families. Depending on installed fonts and browser fallback behavior, the rendered face is serif. The system-bold example also remains weight 400 because bold is encoded in the unresolved family name rather than CSS font weight.

Cause

web_renderer/src/styles/ValdiWebStyles.ts splits the Valdi font string and assigns the first token directly to fontFamily. Attributed text and text-field paths perform similar literal assignment. The default web label may start with sans-serif, but an explicit Valdi system font overwrites that safe default.

Polyglot/custom web views are a related boundary: browser controls created inside them do not necessarily inherit the normal Valdi label defaults. They need either inheritance from a resolved container font or an explicit system stack.

Expected behavior

  • system resolves to the browser's platform sans-serif stack.
  • system-bold resolves to the same stack with bold weight.
  • Normal labels, attributed text, text fields, and text views resolve these names consistently.
  • Custom views can inherit the resolved font where practical; framework widgets that create their own text controls should use the same mapping.

Proposed fix

Add one shared web font resolver used by every renderer path:

const WEB_SYSTEM_FONT = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif';

function resolveWebFont(name: string): { family: string; weight?: string } {
  if (name === 'system') return { family: WEB_SYSTEM_FONT };
  if (name === 'system-bold') return { family: WEB_SYSTEM_FONT, weight: 'bold' };
  return { family: name };
}

Use it in ValdiWebStyles, attributed-text rendering, WebValdiTextField, and WebValdiTextView. Add browser tests asserting both computed family and weight. This keeps the abstract Valdi font contract intact and avoids application-level global CSS, which would not cover native targets or isolated custom-view internals.

Compatibility impact

This changes web rendering only for the reserved Valdi names system and system-bold, bringing web behavior in line with native behavior. Explicit custom font families remain unchanged.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions