Browse documentation
Documentation

Fonts

Edit this guide on GitHub

Declare web fonts in Rust and serve their CSS through the router. Each @font-face rule tells the browser which font file to load and when to use it.

Declaring fonts

font! declares a font family and its @font-face rules. Write the family name once, followed by the rules. Register the font on the router and add its stylesheet to the page's <head>:

use hearth::{
    Result,
    font::{Font, font},
    router::{RouterBuilderDiscoverExt, module_router, page},
    view::{View, view},
};

const ORBITRON: Font = font! {
    "Orbitron",
    @font-face {
        src: url(
            "https://cdn.jsdelivr.net/fontsource/fonts/orbitron:vf@latest/latin-wght-normal.woff2"
        ) format("woff2") tech("variations");
        font-weight: 100 900;
        font-display: swap;
    }
};

#[tokio::main]
async fn main() {
    // Discover the font and page.
    let router = module_router!().discover().build();
    hearth::start(router).await.unwrap();
}

#[page]
async fn home() -> Result<impl View> {
    Ok(view! {
        <!DOCTYPE html>
        <html>
            <head>
                hearth::font::link(font: ORBITRON)
            </head>
            <body>
                <h1 style="font-family: 'Orbitron'">"Hello!"</h1>
            </body>
        </html>
    })
}

The link component loads the font's stylesheet. To register a font manually, call font on the router builder. Manual registration and .discover() can be combined in either order. Registering the same font stylesheet more than once has no effect.

Numeric font-weight values accept integers from 1 through 1000, inclusive. Use one value for a single weight or two space-separated values for a variable font's inclusive range, such as font-weight: 1 1000. The range must run from the lighter weight to the heavier weight. The keywords normal and bold mean 400 and 700.

Use the family name in CSS to apply the font. ORBITRON.family() returns that name when you need it in Rust.

Serving the files as assets

Pass an Asset to url(...) to bundle the font file and serve it from your application:

use hearth::{asset::asset, font::{Font, font}};

const INTER: Font = font! {
    "Inter",
    @font-face {
        src: url(asset!(
            "https://cdn.jsdelivr.net/fontsource/fonts/inter@latest/latin-400-normal.woff2"
        )) format("woff2");
        font-weight: 400;
    }
};

Use url(asset!("./fonts/inter-400.woff2")) for a local file. Enable the asset feature and load the bundle on the router as described in the asset guide.

Fontsource

Fontsource is a catalog of open-source web fonts. fontsource_font! declares a font from this catalog and checks the requested family, weights, styles, and character subsets at compile time.

Fontsource support lives behind the font-fontsource feature:

hearth = { version = "0.1.0", features = ["font-fontsource"] }

Choose a font from families. By default, the declaration includes all its weights and styles in its default character subset. The browser loads the files from jsDelivr:

const ROBOTO: Font = fontsource_font!(ROBOTO);

The resulting Font can be registered, served, and loaded exactly like a custom one.

Picking weights, styles, and subsets

Each combination of weight, style, and subset needs a separate font file. Include only the combinations you use. Each argument accepts one value or a bracketed list:

const ROBOTO: Font = fontsource_font!(
    ROBOTO,
    weight: [400, 700],
    style: Normal,
    subset: [Latin, Cyrillic],
);

See fontsource_font! for the details of each argument.

Self-hosting Fontsource fonts

Pass host: Asset to bundle the font files as Hearth assets and serve them from your application:

use hearth::{
    asset::{AssetBundle, RouterBuilderAssetExt},
    font::{Font, fontsource::fontsource_font},
    router::{RouterBuilderDiscoverExt, module_router},
};

const ROBOTO: Font = fontsource_font!(ROBOTO, host: Asset);

let router = module_router!()
    .assets(AssetBundle::load().unwrap())
    .discover()
    .build();