Early days: oxframe is under very active development. The API is not stable yet and may change from version to version. Pin the exact version in your Cargo.toml and check the changelog before upgrading.

Icons

Lucide, Tabler and Simple Icons by name, as nodes you color and animate. Only the icons a scene names end up in its binary.

Loading live preview…

Source of architecture — edit in the playground
//! An architecture diagram with icons: `icon!` names an icon from Lucide,
//! Tabler or Simple Icons, and only those end up in the binary.

use oxframe::prelude::*;

#[oxframe::scene]
pub async fn request(view: View) {
    view.add(
        Txt::new()
            .text("How a request is served")
            .h1()
            .to_edge(Edge::Top),
    );
    let flow = view.add(
        FlowDiagram::new()
            .edge("browser", "edge")
            .edge("edge", "api")
            .edge("api", "cache")
            .edge("api", "database")
            .icon("browser", icon!("lucide:globe"))
            .icon("edge", icon!("lucide:cloud"))
            .icon("api", icon!("lucide:server"))
            .icon("cache", icon!("tabler:bolt"))
            .icon("database", icon!("lucide:database"))
            .opacity(0.0),
    );
    flow.enter().await;
    for _ in 0..flow.len() {
        flow.next().await;
    }
    // A slow query: the database turns red and back.
    if let Some(db) = flow.icon("database") {
        db.color().to(Danger, 0.3).await;
        db.stroke_width().to(3.0, 0.3).await;
        wait_for(0.4).await;
        all((db.color().to(Text, 0.4), db.stroke_width().to(2.0, 0.4))).await;
    }

    let logos = view.add(Layout::new().gap(56.0).y(300.0).opacity(0.0));
    for name in [
        icon!("simple:github"),
        icon!("simple:rust"),
        icon!("simple:cloudflare"),
    ] {
        logos.add(name.size(56.0).color(Muted));
    }
    logos.fade_in().await;
    wait_for(0.6).await;
}

pub fn project() -> Project {
    Project::new("architecture").scene(request())
}

An icon by name

Turn on the icons feature of oxframe:

oxframe = { version = "…", features = ["icons"] }
let server = view.add(icon!("lucide:server").size(64.0).color(Accent));
view.add(icon!("tabler:heart-filled"));
view.add(icon!("simple:github").color(Muted));

icon! takes the set and the name. The name is the one on the set's site: lucide.dev/icons, tabler.io/icons (add -filled for the filled variant) and simpleicons.org for brand logos. A name that does not exist fails to compile and the error lists the closest names, so icon!("lucide:arow-right") suggests arrow-right.

PrefixSetIconsLicense
lucide:Lucide, outlines2,171ISC
tabler:Tabler Icons, outlines and -filled6,238MIT
simple:Simple Icons, brand logos3,466CC0; the brands are trademarks of their owners

Size, color and line width

An icon is a 24 by 24 grid scaled to .size(), 48 pixels by default. Its color is the theme's text color unless you set .color() with a color or a theme role. Outline icons take .stroke_width() on the 24-unit grid, 2 by default; filled icons and logos have no lines.

The handle, Icon, has color() and stroke_width() as signals, so a whole icon animates at once. It is a node like any other: fade_in, next_to, scale and layout work on it. Icon::paths gives its paths one by one, for draw_on, and Icon::style tells an IconStyle::Stroke icon from an IconStyle::Fill one.

server.color().to(Danger, 0.3).await;
server.stroke_width().to(3.0, 0.3).await;
for path in server.paths() {
    path.draw_on().await;
}

In a flow diagram

FlowDiagram puts an icon at the start of a node's card with .icon(name, icon!(..)). flow.icon(name) returns its handle.

let flow = view.add(
    FlowDiagram::new()
        .edge("api", "database")
        .icon("api", icon!("lucide:server"))
        .icon("database", icon!("lucide:database")),
);
flow.enter().await;
if let Some(db) = flow.icon("database") {
    db.color().to(Danger, 0.3).await;
}

Any other SVG

Icon::from_svg(bytes, IconStyle::Stroke) makes the same node from a 24 by 24 SVG of your own. For a logo or an illustration at its own size, use the Svg node.

The playground has the feature on, so icon! works in a scene there.