Skip to content

Prevent ligature FOUC in self-hosted Material icon fonts by setting font-display: block#22

Closed
Copilot wants to merge 1 commit intomasterfrom
copilot/update-font-display-in-icon-css
Closed

Prevent ligature FOUC in self-hosted Material icon fonts by setting font-display: block#22
Copilot wants to merge 1 commit intomasterfrom
copilot/update-font-display-in-icon-css

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 12, 2026

MudBlazor font icons render ligature text in markup, so first paint can show raw text until the icon font is available. Our self-hosted Material Symbols/Icons CSS did not set font-display, allowing visible text→glyph swaps on initial load (especially with larger Symbols fonts).

For Material Symbols, Google’s guidance says to use display=block to prevent ligature FOUC.

  • What changed

    • Added font-display: block; to every @font-face in both icon packs:
      • src/MudBlazor.FontIcons.MaterialSymbols/wwwroot/css/font.css
      • src/MudBlazor.FontIcons.MaterialSymbols/wwwroot/css/font.min.css
      • src/MudBlazor.FontIcons.MaterialIcons/wwwroot/css/font.css
      • src/MudBlazor.FontIcons.MaterialIcons/wwwroot/css/font.min.css
  • Scope

    • Change is isolated to icon-pack CSS font-loading behavior.
    • No MudBlazor core styling changes (e.g., no global .mud-icon-root overflow rule).
  • Example (@font-face)

@font-face {
  font-family: 'Material Symbols Rounded';
  font-style: normal;
  font-weight: 400;
  font-display: block;
  src: url(../font/MaterialSymbolsRounded.woff2) format('woff2');
}

Closes #17, Closes MudBlazor/MudBlazor#13028

Agent-Logs-Url: https://github.com/MudBlazor/MudBlazor.Icons/sessions/5078d5bb-1efd-41fd-b67a-888a5c12bea3

Co-authored-by: danielchalmers <7112040+danielchalmers@users.noreply.github.com>
@ScarletKuro
Copy link
Copy Markdown
Member

Did not fix it for me in the repro, tho ig it makes sense to have it as block? Idk why google just mentions it in the alert block, rather than just include it by default

@danielchalmers
Copy link
Copy Markdown
Member

danielchalmers commented Apr 12, 2026

The repros ([1], [2]) both point to a general ligature-font loading issue and not primarily a MudBlazor bug:

  • The repro app loads the icon CSS from external stylesheets in index.html and immediately renders a single Material Symbols icon in IconsPage.razor.
  • The icons package generator is currently downloading the full Google variable Material Symbols fonts from google/material-design-icons/variablefont in IconHttpClientService.cs.
  • Those local font files are very large: MaterialSymbolsRounded.woff2 is 5,244,144 bytes and MaterialSymbolsOutlined.woff2 is 3,885,608 bytes. For comparison, MaterialIconsRound.woff2 is only 173,620 bytes.
  • The package CSS in font.css still discovers the font through an external stylesheet, which means the browser learns about the actual font file later than it could.

Google says &display=block is important to avoid ligature FOUC for Material Symbols, but font-display: block is only a temporary block period. web.dev notes Chromium/Firefox block for about 2–3 seconds, then fall back if the font still isn’t ready. With a multi-megabyte icon font that is discovered late, that timeout can still expire, so you still see the ligature text before the font swaps in. web.dev also warns that icon fonts are a bad fit for normal font-display strategies and recommends SVGs when possible.

For MauiAppMaterialSymbolsIconsFlicker:

Add a preload for the exact font you use:

<link rel="preload"
      href="_content/MudBlazor.FontIcons.MaterialSymbols/font/MaterialSymbolsRounded.woff2"
      as="font"
      type="font/woff2"
      crossorigin>

Or hide those icons until the font is confirmed loaded, using the CSS Font Loading API:

<style>
  html.ms-icons-loading .material-symbols-rounded { visibility: hidden; }
</style>
<script>
  document.documentElement.classList.add('ms-icons-loading');
  (async () => {
    try {
      await document.fonts.load("74px 'Material Symbols Rounded'", "weight");
      await document.fonts.ready;
    } finally {
      document.documentElement.classList.remove('ms-icons-loading');
    }
  })();
</script>

If the requirement is “never show readable ligature text”, the robust app-level answer is SVG for above-the-fold icons. Preload/gating can reduce the issue, but SVG removes the font-loading dependency.

For MudBlazor.Icons:

  • Keep font-display: block; it still helps.
  • Change the generator in IconHttpClientService.cs so it stops bundling the raw variable fonts by default. The package only uses fixed axes today, so the full [FILL,GRAD,opsz,wght] fonts are wasted payload.
  • Prefer downloading the default static family file from the Google Fonts CSS API instead of the raw variablefont asset, or generate a fixed-axis subset at package build time.
  • Add docs showing preload plus document.fonts.load(...) for self-hosted usage.
  • Optionally ship family-specific CSS files (rounded.css, outlined.css, sharp.css) so consumers can avoid the all-in-one stylesheet.

For MudBlazor:

  • I would not add a global .mud-icon-root { overflow: hidden; } fix. That only masks the symptom.
  • I would not change MudIcon rendering by default; the raw span repro shows the issue exists outside MudBlazor.
  • The only reasonable MudBlazor change is documentation: warn that ligature font icons can flash on first load, recommend preload/gating for self-hosted fonts, and recommend SVG icons when zero-text-flash is required.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Material Symbols icons flicker / show ligature text briefly on first render

3 participants