Fonts

#Default fonts

Every app starts with a UI font and a monospace font from the theme:

RoleFamilySize
UI text.SystemUIFont16px
Code / monospacemacOS: Menlo, Windows: Consolas, Linux: DejaVu Sans Mono13px

The editor paints its code in mono_font_family at mono_font_size. See Editor for details.

#System fonts

Desktop apps can use any font installed on the OS by name — no bundling, no config. GPUI resolves the family live against the system collection (CoreText on macOS, DirectWrite on Windows, fontconfig on Linux).

div().font_family("Segoe UI")

Editor::new(&editor).font_family("JetBrains Mono")

Common examples per platform:

  • macOS: SF Pro, Helvetica, Arial, Times New Roman, Menlo, Monaco
  • Windows: Segoe UI, Arial, Consolas, Courier New
  • Linux: Noto Sans, DejaVu Sans, Liberation Sans, DejaVu Sans Mono

If the name does not match an installed font, GPUI falls back silently — so verify the exact family name on each target platform.

#Changing fonts via Theme

Set the app-wide fonts on the Theme global, then sync to the base layer:

Theme::global_mut(cx).font_family = "Inter".into();
Theme::global_mut(cx).mono_font_family = "JetBrains Mono".into();
Theme::global_mut(cx).font_size = px(18.);
Theme::sync_base(cx);
window.refresh();

font_size doubles as the application zoom control — Root calls window.set_rem_size(cx.theme().font_size), so rem-based spacing scales with it. See Coding Guides for details.

#Per-element override

Any element accepts a font override without touching the theme:

div()
    .font_family("JetBrains Mono")
    .text_size(px(15.))
    .font_weight(FontWeight::BOLD)

These are ordinary Styled methods, so they compose with the rest of the style chain.

#Bundling custom fonts

Fonts that are not installed on the user’s system must be bundled and registered with the text system before the first frame:

cx.text_system()
    .add_fonts(vec![Cow::Borrowed(
        include_bytes!("../fonts/MyFont-Regular.ttf").as_slice(),
    )])
    .expect("Failed to load fonts");

Then reference them by family name as usual:

Theme::global_mut(cx).font_family = "MyFont".into();
Theme::sync_base(cx);

The gallery’s web build bundles Inter, JetBrains Mono, NotoSansSC and NotoEmoji this way — see crates/story-web/src/lib.rs.

#Theme JSON config

Font families and sizes can also come from a theme file:

{
    "font.family": "Inter",
    "font.size": 16,
    "mono_font.family": "JetBrains Mono",
    "mono_font.size": 13
}

Load it with ThemeRegistry:

ThemeRegistry::watch_dir(PathBuf::from("./themes"), cx, move |cx| {
    if let Some(theme) = ThemeRegistry::global(cx).themes().get(&theme_name).cloned() {
        Theme::global_mut(cx).apply_config(&theme);
    }
});

See Theme for the full config reference.

#WebAssembly note

Browsers expose no system fonts to WASM apps. The story-web gallery (which runs at gpui-kit.com/gallery/) must bundle every family it uses and re-assert them after Theme::change, or the text system panics. Desktop apps skip this entirely.