Skip to main content

topcoat_font/
component.rs

1use topcoat_core::error::Result;
2use topcoat_view::View;
3use topcoat_view_macro::{component, view};
4
5use crate::{Font, FontFormat, FontSource};
6
7/// Loads a [`Font`] into the page.
8///
9/// Renders the stylesheet `<link>` that pulls in the font's `@font-face` rules,
10/// and, by default, a `rel="preload"` `<link>` for the first source of each
11/// face so the browser can start fetching the files before the CSS is parsed.
12///
13/// ```rust
14/// # use topcoat::{font::{Font, fontsource::fontsource_font}, view::{View, view}};
15/// # const LAVISHLY_YOURS: Font = fontsource_font!(LAVISHLY_YOURS, host: Asset);
16/// # #[topcoat::view::component]
17/// # async fn example() -> topcoat::Result<impl View> {
18/// Ok(view! {
19///     topcoat::font::link(font: LAVISHLY_YOURS)
20/// })
21/// # }
22/// ```
23#[component]
24pub async fn link(
25    /// The font to load.
26    font: Font,
27    /// Whether to emit `rel="preload"` links for the font's sources ahead of
28    /// the stylesheet.
29    #[default(true)]
30    preload: bool,
31) -> Result<impl View> {
32    Ok(view! {
33        if preload {
34            for face in font.faces().iter() {
35                if let Some(source) = face.src().first() {
36                    preload_link(source: source)
37                }
38            }
39        }
40        <link rel="stylesheet" href=(font)>
41    })
42}
43
44/// Renders a `rel="preload"` `<link>` for a single font [`FontSource`].
45///
46/// Emits nothing for sources that are not URL-backed (such as local fonts). The
47/// `type` attribute is set from the source's format when known and omitted
48/// otherwise.
49#[component]
50pub async fn preload_link(
51    /// The font source to preload.
52    source: &FontSource,
53) -> Result<impl View> {
54    Ok(view! {
55        if let FontSource::Url { url, format, .. } = source {
56            <link
57                rel="preload"
58                href=(url.clone())
59                as="font"
60                type=(format.map(FontFormat::mime_type))
61                crossorigin="true"
62            >
63        }
64    })
65}