Attribute macros C!, style! and attrs! represent HTML element attributes in the Seed world, e.g.:

    C!["counter", IF!(selected => "active")],
        St::Display => "flex",
        St::Padding => px(10),
    attrs!{At::Title => "A Title"},
    "This is a counter.",


<div class="counter active" title="A Title" style="display:flex;padding:10px">
    This is a counter.


  • Macro C! accepts all items that implement trait ToClasses.
  • If you use multiple C! invocations in one element, then the classes will be merged. (This rule is valid for all attributes.)

ToClasses is implemented for String and &str, references and containers Option and Vec.

Example of some valid input type combinations:

let selected = false;
let optional_classes: Option<Vec<String>> = None;
    C!["counter", IF!(selected => "active")],
    C![IF!(true => vec!["class_a", "class_b"])],

Corresponding HTML:

<div class="counter class_a class_b"></div>
Why C!?

The name C breaks Rust naming conventions (macros should be written in snake_case!), but it's a trade-off for better scannability (you can distinguish element macros and attribute macros on the first glance). And it will be consistent with future names of other entities (e.g. A. for other attributes and E. for event handlers).

Note: If you want to use Tailwind CSS and typed classes, look at seed-quickstart-webpack.


style! expects key-value pairs, where:

  • Key is a CSS property name - e.g. St::Display

    • You can also use custom property names - e.g. St::from("custom_name")
  • Value can be everything that implements ToString and it can be wrapped in Option.

Example of some valid input type combinations:

let selected = true;
let apply_custom = true;
        St::Margin => px(50),
        St::MaxWidth => unit!(50, %),
        St::Top => 0,
        St::Padding => px(20) + " " + &px(15)
        St::BackgroundColor => if selected { "green" } else { "white" },
        St::from("custom_name") => IF!(apply_custom => "a_value"),

Corresponding HTML:

<div style="
    padding:20px 15px;


Macro style! will be replaced with Seed Style.

Seed Style is basically typed inline CSS on steroids. It allows you to write also pseudo-classes like :hover directly in your Rust elements so it eliminates the need to write standalone style files. And there are many more useful features.


attrs! expects key-value pairs, where:

  • Key is an attribute name - e.g. At::Title

    • You can also use custom attribute names - e.g. At::from("custom_name")
  • Value can be AtValue or everything that implements ToString. AtValue has 3 variants:

    • Ignored - The whole attribute is ignored (i.e. not rendered to HTML at all). It's useful for boolean HTML attributes and in your conditions.
    • None - The attribute value is not used (i.e. rendered as empty string). It's also useful for boolean HTML attributes.
    • Some(String) - If v in At::X => v, implements ToString, then it's automatically transformed to AtValue::Some(v).

Note: C! and style! are basically only attrs!'s specializations - you can write

attrs!{At::Class => "class_a", At::Style => "top:0"}

but it's not recommended.


Method as_at_value is automatically attached to all Rust bools. It helps with boolean attributes. It allows you to write:

let disabled = false;
    At::Disabled => disabled.as_at_value()

instead of:

    At::Disabled => if disabled { AtValue::None } else { AtValue::Ignored }

Note: Without that .as_at_value() call, variable disabled would be only casted into String and rendered in HTML as disabled="false".

attrs! example

let disabled = true;
    attrs! {
        At::Disabled => disabled.as_at_value(),
        At::Title => "a_title",
        At::AutoFocus => AtValue::None,
        At::from("custom_name") => 123,

Corresponding HTML:

<div disabled="" title="a_title" autofocus="" custom_name="123"></div>


Macro attrs! will be replaced with a safer and more readable API that consists of a struct A and associated methods. You can see drafts in this issue.