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}