topcoat 0.10.0

A modular, batteries-included Rust web framework for server-rendered apps.
Documentation
Topcoat renders icons as inline `<svg>` elements. Icons scale with the surrounding text and need no separate network request.

# Declaring icons

[`IconData`] holds an icon's view box and SVG body. Declare it as a constant, then render it with the [`icon`] component:

```rust,no_run
use topcoat::{
    Result,
    icon::{IconData, icon},
    router::{module_router, page},
    view::{View, svg::ViewBox, view},
};

// An icon is a view box plus a raw SVG body:
const TRASH: IconData = IconData::unescaped_unchecked(
    ViewBox::new(0.0, 0.0, 24.0, 24.0),
    r#"<path fill="currentColor" d="M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19V4M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z"/>"#,
);

#[tokio::main]
async fn main() {
    topcoat::start(module_router!().build())
        .await
        .unwrap();
}

#[page]
async fn home() -> Result<impl View> {
    Ok(view! {
        <!DOCTYPE html>
        <html>
            <body>
                <p>"Move to " icon(data: TRASH, label: "trash")</p>
            </body>
        </html>
    })
}
```

Icons are `1em` square by default. Their SVG bodies can use `currentColor` to inherit the text color. Pass `size` to set the dimensions and `attrs` to add attributes to the `<svg>` element.

An icon without a `label` is hidden from assistive technology. Provide a label when the icon conveys information that the surrounding text does not:

```rust
# use topcoat::{Result, icon::{IconData, icon}, view::*};
# const TRASH: IconData = IconData::unescaped_unchecked(svg::ViewBox::new(0.0, 0.0, 24.0, 24.0), "<g/>");
# #[component]
# async fn example() -> Result<impl View> {
Ok(view! {
    icon(data: TRASH, size: 48, label: "Delete")
})
# }
```

# Iconify

[Iconify] publishes open-source icon sets in a shared format. Topcoat can download these sets and check icon references at compile time.

Iconify support lives behind the `icon-iconify` feature, for both your runtime dependency and your build dependency:

```toml
[dependencies]
topcoat = { version = "0.10.0", features = ["icon-iconify"] }

[build-dependencies]
topcoat = { version = "0.10.0", default-features = false, features = ["icon-iconify"] }
```

Add a `build.rs` next to `Cargo.toml` to stage the sets you use. Each set is downloaded when it is missing from the cache:

```rust,no_run
# #[cfg(feature = "icon-iconify")]
# #[allow(clippy::needless_doctest_main)]
fn main() {
    topcoat::icon::iconify::BuildConfig::new()
        .icon_set("feather")
        .stage()
        .unwrap();
}
# #[cfg(not(feature = "icon-iconify"))]
# fn main() {}
```

[`include!`] creates `IconData` constants from a staged set. The constants use the icons' names in `SCREAMING_SNAKE_CASE`:

```rust,ignore
use topcoat::icon::{icon, iconify};

iconify::include!("feather");

view! {
    icon(data: feather::TRASH_2, label: "Delete")
}
```

## Single icons

Use [`iconify_icon!`] to refer to one icon as an expression. You can pass it directly to a view or assign it to a constant:

```rust,ignore
const TRASH: IconData = iconify::iconify_icon!("feather:trash-2");
```

## Caching and vendoring

By default the downloaded sets are cached in Topcoat's cache inside the Cargo target directory, shared across the workspace. Pass [`cache_dir`] to keep the cache in a directory of your own instead:

```rust,no_run
# #[cfg(feature = "icon-iconify")]
# #[allow(clippy::needless_doctest_main)]
fn main() {
    topcoat::icon::iconify::BuildConfig::new()
        .cache_dir("icons")
        .icon_set("feather")
        .icon_set_version("mdi", "1.30.0")
        .stage()
        .unwrap();
}
# #[cfg(not(feature = "icon-iconify"))]
# fn main() {}
```

Each set is cached at `<dir>/<set>.json` and downloaded only when its file is missing or pinned to a different version. Commit the directory for offline, reproducible builds, or gitignore it to keep a cache that survives `cargo clean`. Files you place there yourself are used as-is, so an icon set that is not on Iconify can be vendored the same way.

[Iconify]: https://iconify.design/
[`IconData`]: IconData
[`icon`]: icon
[`include!`]: iconify/macro.include.html
[`iconify_icon!`]: iconify/macro.iconify_icon.html
[`icon_set_version`]: iconify/struct.BuildConfig.html#method.icon_set_version
[`cache_dir`]: iconify/struct.BuildConfig.html#method.cache_dir