Skip to main content

El

Struct El 

Source
#[non_exhaustive]
pub struct El {
Show 90 fields pub kind: Kind, pub style_profile: StyleProfile, pub key: Option<String>, pub block_pointer: bool, pub hit_overflow: Sides, pub focusable: bool, pub focus_ring_placement: FocusRingPlacement, pub always_show_focus_ring: bool, pub selectable: bool, pub selection_source: Option<SelectionSource>, pub capture_keys: bool, pub alpha_follows_focused_ancestor: bool, pub blink_when_focused: bool, pub state_follows_interactive_ancestor: bool, pub hover_alpha: Option<HoverAlpha>, pub source: Source, pub axis: Axis, pub gap: f32, pub padding: Sides, pub align: Align, pub justify: Justify, pub width: Size, pub height: Size, pub component_size: Option<ComponentSize>, pub metrics_role: Option<MetricsRole>, pub explicit_width: bool, pub explicit_height: bool, pub explicit_padding: bool, pub explicit_gap: bool, pub explicit_radius: bool, pub explicit_font_family: bool, pub explicit_mono_font_family: bool, pub explicit_mono: bool, pub fill: Option<Color>, pub dim_fill: Option<Color>, pub stroke: Option<Color>, pub stroke_width: f32, pub radius: Corners, pub shadow: f32, pub surface_role: SurfaceRole, pub paint_overflow: Sides, pub clip: bool, pub scrollable: bool, pub pin_end: bool, pub arrow_nav_siblings: bool, pub tooltip: Option<String>, pub cursor: Option<Cursor>, pub cursor_pressed: Option<Cursor>, pub shader_override: Option<ShaderBinding>, pub layout_override: Option<LayoutFn>, pub virtual_items: Option<VirtualItems>, pub scrollbar: bool, pub text: Option<String>, pub text_color: Option<Color>, pub text_align: TextAlign, pub text_wrap: TextWrap, pub text_overflow: TextOverflow, pub text_role: TextRole, pub text_max_lines: Option<usize>, pub font_size: f32, pub line_height: f32, pub font_family: FontFamily, pub mono_font_family: FontFamily, pub font_weight: FontWeight, pub font_mono: bool, pub text_italic: bool, pub text_underline: bool, pub text_strikethrough: bool, pub text_link: Option<String>, pub text_bg: Option<Color>, pub math: Option<Arc<MathExpr>>, pub math_display: MathDisplay, pub icon: Option<IconSource>, pub icon_stroke_width: f32, pub image: Option<Image>, pub image_tint: Option<Color>, pub image_fit: ImageFit, pub surface_source: Option<SurfaceSource>, pub surface_alpha: SurfaceAlpha, pub surface_fit: ImageFit, pub surface_transform: Affine2, pub vector_source: Option<Arc<VectorAsset>>, pub vector_render_mode: VectorRenderMode, pub children: Vec<El>, pub opacity: f32, pub translate: (f32, f32), pub scale: f32, pub animate: Option<Timing>, pub redraw_within: Option<Duration>, pub computed_id: String,
}
Expand description

The core tree node.

Construct via the component builders (text, button, card, column, …) and chain modifiers (.padding, .gap, .fill, …). Avoid building El directly — the builders set polished defaults.

#[non_exhaustive]El is meant to be built through the component constructors, not by struct-literal syntax. Direct construction from outside this crate is intentionally disabled so adding new layout/style fields stays a non-breaking change.

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§kind: Kind§style_profile: StyleProfile§key: Option<String>§block_pointer: bool§hit_overflow: Sides

Expand this element’s pointer hit target beyond its transformed layout rect. Layout-neutral and paint-neutral: siblings don’t move, the element doesn’t draw larger, and focus rings / shadows still use Self::paint_overflow.

Use sparingly for controls with deliberately small visuals but larger intended targets (resize handles, compact icon affordances, row chrome). Hover, press, cursor, tooltip, and click routing all share this expanded target, so the invisible area behaves like the visible control. Ancestor clips still bound hit-testing.

§focusable: bool§focus_ring_placement: FocusRingPlacement§always_show_focus_ring: bool

Show the focus ring on this node even when focus arrived via pointer (i.e. the runtime’s focus_visible is false). Default behavior matches the web platform’s :focus-visible heuristic — ring on Tab, no ring on click. Widgets like text inputs and text areas opt in here because the ring is a meaningful “this surface is now the active editing target” affordance even when activated by mouse, beyond what the caret alone shows.

§selectable: bool

When true, this node is a pointer target for the library’s text-selection manager: pointer-down inside its rect starts (or extends) the global crate::selection::Selection anchored at this node’s key. The leaf must also carry an explicit .key(...) — same convention as focusable widgets — so the selection survives tree rebuilds.

Set via Self::selectable. Coordinates with focus on a per-pointer-event basis: pointer-down on a focusable widget transfers focus and clears selection; pointer-down on a selectable-only leaf moves selection without disturbing focus.

§selection_source: Option<SelectionSource>

Optional source-backed selection payload. Plain text leaves select/copy their rendered Self::text. Rich text systems can attach a crate::selection::SelectionSource so pointer positions resolve through rendered text but copy returns the original driving syntax (for example Markdown or TeX).

§capture_keys: bool

When true, all key events (other than registered hotkeys) route to this node as raw KeyDown instead of being interpreted by the library’s defaults (Tab traversal, Enter/Space activation, Escape escape). Used by text-input widgets that need to consume Tab/Enter/etc. as text or editing actions. Implies focusable at the runner — the flag only takes effect when the node is also the focused target.

§alpha_follows_focused_ancestor: bool

When true, this node’s paint opacity is multiplied by the nearest focusable ancestor’s focus envelope (0..1). The library already animates that envelope on focus / blur; flagged nodes fade in and out with the same easing without any app-side focus tracking.

Used by text_input’s caret bar — the caret only paints when the input is focused, fading via the standard focus animation. Documented in widget_kit.md as part of the public surface.

§blink_when_focused: bool

When true, this node’s paint opacity is also multiplied by the runtime’s caret blink alpha. Combine with alpha_follows_focused_ancestor (the caret should blink only while the input is focused) — the two compose multiplicatively. Used by text_input / text_area’s caret bar.

§state_follows_interactive_ancestor: bool

When true, this node’s hover and press visual envelopes are borrowed from its nearest focusable ancestor instead of being driven by its own (always-zero) envelope.

The hit-test only ever resolves to a focusable target, so a child of an interactive container — a slider thumb, a select trigger’s chevron, the dot inside a radio — never receives hover or press envelopes of its own. Flagged children pick up the ancestor’s envelopes so they can lighten / darken / ring out alongside the surface that captured the input.

Used by slider’s thumb so grabbing the slider visibly reacts on the thumb itself, mirroring shadcn’s hover:ring-4 hover:ring-ring/50.

§hover_alpha: Option<HoverAlpha>

When Some, this node’s paint opacity is bound to the subtree interaction envelopemax of the hover, focus, and press envelopes for the subtree rooted here. The drawn alpha interpolates from rest (no interaction anywhere in the subtree) to peak (full interaction), then composes multiplicatively with the existing Self::opacity / inherited opacity stack.

“Interaction” includes hovering, pressing, or keyboard-focusing any descendant — so a hover-revealed close icon stays visible when its tab is keyboard-focused, and an action pill stays visible when the cursor moves to one of its focusable buttons. Mirrors CSS’s “this element OR any descendant is hot.”

Layout-neutral — the element’s geometry stays fixed regardless of interaction state. Use for hover-revealed close buttons, secondary actions on list rows, hover-only validation icons, and other “show on interaction” patterns whose visibility shouldn’t shift the surrounding layout.

§source: Source§axis: Axis§gap: f32§padding: Sides§align: Align§justify: Justify§width: Size§height: Size§component_size: Option<ComponentSize>

Optional t-shirt size for stock widgets. None means the active theme supplies the component-class default.

§metrics_role: Option<MetricsRole>

Optional theme-facing metrics role. Stock widgets set this so the theme can resolve default height/padding/radius before layout; app-defined widgets can set the same role to opt into identical sizing behavior.

§explicit_width: bool

Author-overrode layout metrics. Stock constructors set defaults without these flags; public modifiers flip them so theme metrics do not clobber explicit app choices.

§explicit_height: bool§explicit_padding: bool§explicit_gap: bool§explicit_radius: bool§explicit_font_family: bool§explicit_mono_font_family: bool

Author overrode the monospace font face for this node — theme application leaves Self::mono_font_family alone when set.

§explicit_mono: bool

Author opted this node into the monospace family via Self::mono. Role modifiers (Self::caption, Self::label, Self::body, Self::title, Self::heading, Self::display) leave Self::font_mono alone when this flag is set, so the natural reading order text(s).mono().caption() keeps the mono family. Without this guard, role application silently resets font_mono = false. The Self::code role always forces font_mono = true regardless.

§fill: Option<Color>§dim_fill: Option<Color>

Alternate fill used when the nearest focusable ancestor’s focus envelope is below 1.0; the painter linearly interpolates from dim_fill toward fill as the envelope approaches 1.0. Used by text_input / text_area selection bands so the highlight remains visible (in a muted color) even when the input loses focus, matching the macOS convention.

§stroke: Option<Color>§stroke_width: f32§radius: Corners

Corner radii in logical pixels. Authored as a scalar in the common case (.radius(tokens::RADIUS_MD) works via super::geometry::Corners::from); per-corner shapes use super::geometry::Corners::top, super::geometry::Corners::bottom, etc. The painter clamps each corner to half the shorter side.

§shadow: f32§surface_role: SurfaceRole§paint_overflow: Sides

Permit this element to paint outside its layout bounds. The outset enlarges the quad geometry handed to the shader (and any focus / shadow / glow visuals are positioned in the overflow band) while leaving the layout rect — and therefore sibling positions and hit-testing — unchanged. Subject to ancestor clip rects: a focused widget inside a clip()ped parent has its overflow clipped, same as any other paint.

§clip: bool

Clip this element’s own paint and descendants to its computed rect. Used by scroll panes, host-painted regions, overlays, and any region where overflow should not leak visually or receive events.

§scrollable: bool

This element is a vertical scroll viewport. The layout pass reads the offset from UiState’s scroll-offset side map keyed by computed_id, clamps it to [0, content_h - viewport_h], and writes the clamped value back. Set automatically by crate::scroll().

§pin_end: bool

When set on a Kind::Scroll container, the runtime tracks the “stick to bottom” pin used by chat logs and activity feeds: the scroll offset stays glued to the tail across content growth, the user can scroll up to break the pin, and scrolling back to the bottom re-engages it. No effect on non-scrollable nodes. Defaults to false; opt in with Self::pin_end.

Mirrors egui’s ScrollArea::stick_to_bottom(true). The “is the pin currently engaged” bit lives in crate::state::UiState’s scroll subsystem, keyed by computed_id; layout reads it each frame to decide whether to snap the stored offset to max_offset before clamping.

§arrow_nav_siblings: bool

Treat this element’s focusable children as a single arrow-navigable group: while a focused element is one of the direct children, Up / Down / Home / End move focus among the group’s focusable siblings instead of being routed as a KeyDown. Tab traversal is unchanged.

Used by popover_panel so menu items in a dropdown are keyboard-navigable; available to any user widget that wants the same semantics.

§tooltip: Option<String>

Tooltip text. When set, the runtime synthesizes a hover-driven tooltip layer anchored to this node — appearing after the hover delay elapses, fading in with the standard envelope, and dismissed when the pointer leaves or presses the node. The trigger doesn’t have to be focusable or keyed; the runtime anchors the tooltip via the trigger’s computed_id.

§cursor: Option<Cursor>

Pointer cursor declared for this element. None falls through to whatever an ancestor declared, else crate::cursor::Cursor::Default. Resolution lives in crate::state::UiState::cursor: if a press is captured, the cursor follows the press target; otherwise the hovered node is walked root-ward for the first explicit declaration. Disabled state is not auto-mapped — widgets that want crate::cursor::Cursor::NotAllowed when disabled set it explicitly in their build closure.

§cursor_pressed: Option<Cursor>

Cursor to show only while a press is captured at this exact node. Powers the natural Grab → Grabbing transition: the slider sets cursor=Grab + cursor_pressed=Grabbing, and the resolver picks the latter while the press anchors here. Unlike Self::cursor, this does not walk up: an ancestor’s cursor_pressed doesn’t apply to a descendant press target. The press target’s own cursor is the fallback when this is None.

§shader_override: Option<ShaderBinding>

Override the implicit stock::rounded_rect binding for this node’s surface. The escape hatch a user crate uses to bind a custom shader (e.g. liquid_glass).

§layout_override: Option<LayoutFn>

Second escape hatch: author-supplied layout function that positions this node’s direct children. When set, the layout pass calls the function instead of running its column/row/ overlay distribution. The library still recurses into each child and still drives hit-test / focus / animation / scroll off the rects the function returns. See LayoutFn for the contract.

§virtual_items: Option<VirtualItems>

Virtualized list state. Set by crate::virtual_list (and only on Kind::VirtualList nodes). The layout pass uses this to realize only the rows whose rect intersects the viewport. The node is automatically scrollable + clip.

§scrollbar: bool

Show a draggable vertical scrollbar thumb when this node is scrollable and its content overflows the viewport. The thumb overlays the right edge of the viewport — it does not reflow children. No effect on non-scrollable nodes. Defaults to false; the crate::scroll() and crate::virtual_list() constructors flip it on by default. Authors disable with Self::no_scrollbar.

§text: Option<String>§text_color: Option<Color>§text_align: TextAlign§text_wrap: TextWrap§text_overflow: TextOverflow§text_role: TextRole§text_max_lines: Option<usize>§font_size: f32§line_height: f32§font_family: FontFamily§mono_font_family: FontFamily

Monospace face used when Self::font_mono is set (or when the node carries TextRole::Code). Stamped by theme application from crate::Theme::mono_font_family unless the author set it explicitly via Self::mono_font_family.

§font_weight: FontWeight§font_mono: bool§text_italic: bool

Italic styling. Author-set via Self::italic; honoured when this El is a styled text leaf inside an Kind::Inlines parent and (best-effort) on standalone text Els.

§text_underline: bool

Underline styling. Author-set via Self::underline.

§text_strikethrough: bool

Strikethrough styling. Author-set via Self::strikethrough.

§text_link: Option<String>

Link target URL. When set on a text leaf inside Kind::Inlines, the run renders as a link (themed) and runs sharing a URL group together for hit-test. Author-set via Self::link.

§text_bg: Option<Color>

Inline-run background. When set on a text leaf inside Kind::Inlines, the shaped span paints a solid quad behind its glyphs (one rect per line if the span wraps). No effect on standalone text Els — author wraps in a styled row() for chip-shaped surfaces. Author-set via Self::background.

§math: Option<Arc<MathExpr>>

Native math expression rendered through Aetna’s math box layout. Set by crate::tree::math, crate::tree::math_inline, and crate::tree::math_block.

§math_display: MathDisplay§icon: Option<IconSource>§icon_stroke_width: f32§image: Option<Image>

Raster image. When set together with Kind::Image (or any kind, though crate::image is the idiomatic builder) the draw_ops pass emits a crate::ir::DrawOp::Image projected per Self::image_fit and tinted by Self::image_tint. Layout intrinsic is the image’s natural pixel size when both width and height are Hug.

§image_tint: Option<Color>

Multiply each sampled pixel by this colour (RGBA [0..1]). Most raster art wants None (no tint); set it for monochrome assets (icon-style PNGs) the app wants to recolour.

§image_fit: ImageFit

How the image projects into the resolved rect. Defaults to ImageFit::Contain — preserves aspect ratio and letterboxes.

§surface_source: Option<SurfaceSource>

App-owned GPU texture source for Kind::Surface elements. Set via Self::surface_source (typically through the crate::tree::surface builder).

§surface_alpha: SurfaceAlpha

How the surface texture composes with widgets painted below it. Defaults to crate::surface::SurfaceAlpha::Premultiplied.

§surface_fit: ImageFit

How the surface texture projects into the resolved rect. Defaults to ImageFit::Fill — stretch to the rect, ignoring aspect ratio. Contain / Cover / None mirror the corresponding modes on crate::tree::image.

§surface_transform: Affine2

Affine applied to the texture quad in destination space, around the centre of the post-fit rect. Defaults to identity. Composes after Self::surface_fit: the fit projection picks the destination rect, then this matrix transforms it (rotate, scale, translate, shear). The auto-clip scissor still clamps to the El’s content rect, so transforms that move the texture outside that rect are cropped.

§vector_source: Option<Arc<VectorAsset>>

Vector asset for Kind::Vector elements. Set via Self::vector_source (typically through the crate::tree::vector builder). The asset’s view box determines the natural aspect ratio.

§vector_render_mode: VectorRenderMode

Render policy for Self::vector_source. Defaults to crate::vector::VectorRenderMode::Painted so authored vector paint is preserved unless the caller explicitly opts into mask rendering.

§children: Vec<El>§opacity: f32

Paint-time alpha multiplier in [0, 1]. Default 1.0. Multiplies the alpha channel of fill, stroke, and text colour at draw time. Layout-neutral. App-driven changes are eased when Self::animate is set.

§translate: (f32, f32)

Paint-time offset in logical pixels. Default (0.0, 0.0). Subtree-inheriting: descendants paint at their computed rect plus all ancestor translate accumulated through the paint recursion. Use this to slide a sidebar / drawer / list-item without re-running layout. App-driven changes are eased when Self::animate is set.

§scale: f32

Per-node uniform scale around the computed-rect centre. Default 1.0. Scales this node’s surface quad and (if it carries text) its glyph run together. Not subtree-inheriting — descendants keep their own scale. Use this for tap-bounce on a button. App- driven changes are eased when Self::animate is set.

§animate: Option<Timing>

Opt-in app-driven prop interpolation. When Some(timing), the animation tracker eases fill / text_color / stroke / opacity / translate / scale between rebuilds — the value the build closure produces becomes the spring/tween target; current carries over from last frame. State visuals (hover / press / focus ring) keep their own library defaults regardless.

§redraw_within: Option<Duration>

Inside-out redraw deadline: when Some(d) and this El is visible (rect intersects the viewport), Aetna asks the host to schedule the next frame within d. Aggregated across the tree via min and surfaced as [crate::runtime::PrepareResult::next_redraw_in]; the host drives the loop, Aetna mediates by visibility.

Use this for any widget whose paint depends on time (animated images, video frames written via surface(), custom shaders that don’t go through the samples_time registration path, hover-and-fade effects implemented outside the built-in animation tracker). Duration::ZERO means “next frame ASAP”; non-zero values let the host pace at lower-than-display cadence.

§computed_id: String

Stable path-based ID, filled by the layout pass. Used as the key for every side map that holds per-node bookkeeping in crate::state::UiState — computed rects, interaction state, state-envelope amounts, scroll offsets, in-flight animations.

Implementations§

Source§

impl El

Source

pub fn primary(self) -> Self

Examples found in repository?
examples/settings_calibration.rs (line 104)
97fn settings_header() -> El {
98    row([
99        icon_button("menu").ghost(),
100        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
101        h3("Settings").key("metric:page.title"),
102        spacer(),
103        button("Reset").secondary(),
104        button("Save changes").primary(),
105    ])
106    .key("metric:header")
107    .gap(tokens::SPACE_3)
108    .height(Size::Fixed(56.0))
109    .padding(Sides::xy(tokens::SPACE_4, 0.0))
110    .align(Align::Center)
111    .stroke(tokens::BORDER)
112}
More examples
Hide additional examples
examples/polish_calibration.rs (line 134)
119fn toolbar() -> El {
120    row([
121        column([
122            h1("Polish calibration").key("metric:page.title"),
123            text("A representative app surface for default tuning.")
124                .muted()
125                .key("metric:page.subtitle"),
126        ])
127        .gap(tokens::SPACE_2)
128        .height(Size::Hug),
129        spacer(),
130        button_with_icon("search", "Preview")
131            .secondary()
132            .key("metric:action.secondary"),
133        button_with_icon("upload", "Publish")
134            .primary()
135            .key("metric:action.primary"),
136    ])
137    .key("metric:header")
138    .gap(tokens::SPACE_4)
139    .height(Size::Hug)
140    .align(Align::Start)
141}
142
143fn kpi_card(label: &'static str, value: &'static str, delta: &'static str, positive: bool) -> El {
144    let delta_badge = if positive {
145        badge(delta).success()
146    } else {
147        badge(delta).destructive()
148    };
149    let delta_badge = if label == "Latency" {
150        delta_badge.key("metric:kpi.badge")
151    } else {
152        delta_badge
153    };
154    let value_text = h2(value).display();
155    let value_text = if label == "Latency" {
156        value_text.key("metric:kpi.value")
157    } else {
158        value_text
159    };
160    card([
161        card_header([card_title(label)]),
162        card_content([
163            row([value_text, spacer(), delta_badge]).align(Align::Center),
164            text(if positive {
165                "Moving in the expected direction"
166            } else {
167                "Needs visual attention"
168            })
169            .muted(),
170        ])
171        .gap(tokens::SPACE_6),
172    ])
173    .key(if label == "Latency" {
174        "metric:kpi.card"
175    } else {
176        label
177    })
178    .width(Size::Fill(1.0))
179}
180
181fn table_card() -> El {
182    card([
183        card_header([card_title("Reference rows")]),
184        card_content([table([
185            table_header([table_row([
186                table_head("Status").width(Size::Fixed(86.0)),
187                table_head("Surface").width(Size::Fill(1.0)),
188                table_head("Owner").width(Size::Fixed(110.0)),
189                table_head("State").width(Size::Fixed(86.0)),
190            ])
191            .key("metric:table.header")]),
192            divider(),
193            table_body([
194                data_row("OK", "Settings card", "core", "selected", true, "success"),
195                data_row(
196                    "WARN",
197                    "Command palette density",
198                    "widgets",
199                    "needs work",
200                    false,
201                    "warning",
202                ),
203                data_row(
204                    "ERR",
205                    "Disabled and invalid states",
206                    "style",
207                    "missing",
208                    false,
209                    "destructive",
210                ),
211                data_row(
212                    "INFO",
213                    "Token resolution",
214                    "theme",
215                    "planned",
216                    false,
217                    "info",
218                ),
219                data_row(
220                    "OK",
221                    "Popover elevation",
222                    "shader",
223                    "queued",
224                    false,
225                    "success",
226                ),
227            ])
228            .gap(tokens::SPACE_1)
229            .width(Size::Fill(1.0)),
230        ])]),
231    ])
232    .key("metric:table.card")
233    .width(Size::Fill(1.2))
234    .height(Size::Fill(1.0))
235}
236
237fn data_row(
238    status: &'static str,
239    title: &'static str,
240    owner: &'static str,
241    state: &'static str,
242    selected: bool,
243    tone: &'static str,
244) -> El {
245    let status_badge = match tone {
246        "success" => badge(status).success(),
247        "warning" => badge(status).warning(),
248        "destructive" => badge(status).destructive(),
249        _ => badge(status).info(),
250    };
251    let status_badge = if selected {
252        status_badge.key("metric:table.badge")
253    } else {
254        status_badge
255    };
256
257    let mut row = table_row([
258        table_cell(status_badge).width(Size::Fixed(70.0)),
259        column([
260            text(title)
261                .font_weight(FontWeight::Medium)
262                .ellipsis()
263                .width(Size::Fill(1.0)),
264            text("Default styling probe.")
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0)),
271        table_cell(text(owner).muted()).width(Size::Fixed(110.0)),
272        table_cell(text(state).label().small()).width(Size::Fixed(86.0)),
273    ])
274    .key(if selected {
275        "metric:table.row".to_string()
276    } else {
277        format!("row-{title}")
278    })
279    .focusable();
280
281    if selected {
282        row = row.selected();
283    }
284
285    row
286}
287
288fn command_card() -> El {
289    card([
290        card_header([card_title("Command surface")]),
291        card_content([
292            text_input(
293                "Search commands...",
294                &Selection::default(),
295                "command-search",
296            )
297            .key("metric:command.input")
298            .width(Size::Fill(1.0)),
299            popover_panel([
300                command_row("git-branch", "New branch", "Ctrl+B").key("metric:command.row"),
301                command_row("git-commit", "Commit staged files", "Ctrl+Enter")
302                    .key("command-row-commit"),
303                command_row("refresh-cw", "Refresh repository", "Ctrl+R")
304                    .key("command-row-refresh"),
305                command_row("alert-circle", "Force push", "Danger").key("command-row-force"),
306            ])
307            .width(Size::Fill(1.0)),
308            scroll([form_probe()]).key("form-probe-scroll"),
309        ])
310        .height(Size::Fill(1.0)),
311    ])
312    .key("metric:command.card")
313    .width(Size::Fill(0.8))
314    .height(Size::Fill(1.0))
315}
316
317fn form_probe() -> El {
318    form([
319        form_item([
320            form_label("Valid input"),
321            form_control(
322                text_input(
323                    "Valid input",
324                    &Selection::caret("valid-input", 11),
325                    "valid-input",
326                )
327                .key("metric:form.input"),
328            ),
329            form_description("Default field spacing and helper text."),
330        ]),
331        form_item([
332            form_label("Invalid input"),
333            form_control(
334                text_input(
335                    "Invalid input",
336                    &Selection::caret("invalid-input", 13),
337                    "invalid-input",
338                )
339                .invalid(),
340            ),
341            form_message("This field needs attention."),
342        ]),
343        row([
344            button("Disabled").secondary().disabled(),
345            button("Loading").primary().loading(),
346            spacer(),
347        ]),
348    ])
349    .padding(tokens::SPACE_3)
350    .fill(tokens::MUTED)
351    .stroke(tokens::BORDER)
352    .radius(tokens::RADIUS_MD)
353}
examples/circular_layout.rs (line 63)
48fn fixture() -> El {
49    let centre = h2("Compass").center_text();
50    let dirs = [
51        ("North", "n"),
52        ("NE", "ne"),
53        ("East", "e"),
54        ("SE", "se"),
55        ("South", "s"),
56        ("SW", "sw"),
57        ("West", "w"),
58        ("NW", "nw"),
59    ];
60
61    let mut children: Vec<El> = vec![centre];
62    for (label, k) in dirs {
63        children.push(button(label).key(k).primary());
64    }
65
66    column([
67        h1("Custom layout — circular"),
68        paragraph(
69            "Eight buttons positioned on a circle by an author-supplied \
70             LayoutFn. Stock paint, automatic hover/press, and hit-test \
71             all keep working — only the rect distribution changed.",
72        )
73        .muted(),
74        stack(children)
75            .key("compass")
76            .layout(circular)
77            .width(Size::Fill(1.0))
78            .height(Size::Fixed(360.0)),
79    ])
80    .gap(tokens::SPACE_4)
81    .padding(tokens::SPACE_7)
82}
examples/palette_demo.rs (line 322)
314fn component_section() -> El {
315    card([
316        card_header([
317            card_title("Stock widgets"),
318            card_description("The same palette applied to regular component constructors."),
319        ]),
320        card_content([
321            row([
322                button("Primary").primary(),
323                button("Secondary").secondary(),
324                button("Outline").outline(),
325                button("Ghost").ghost(),
326            ])
327            .gap(tokens::SPACE_2)
328            .align(Align::Center),
329            row([
330                badge("success").success(),
331                badge("warning").warning(),
332                badge("destructive").destructive(),
333                badge("info").info(),
334                badge("muted").muted(),
335            ])
336            .gap(tokens::SPACE_2)
337            .align(Align::Center),
338            row([
339                text_input("palette search", &Selection::default(), "palette:search")
340                    .width(Size::Fill(1.0)),
341                button_with_icon("settings", "Tune").secondary(),
342            ])
343            .gap(tokens::SPACE_2)
344            .align(Align::Center),
345            row([
346                surface_sample("Card", tokens::CARD),
347                surface_sample("Muted", tokens::MUTED),
348                surface_sample("Popover", tokens::POPOVER),
349            ])
350            .gap(tokens::SPACE_3)
351            .align(Align::Stretch),
352        ])
353        .gap(tokens::SPACE_4),
354    ])
355    .height(Size::Hug)
356}
examples/settings.rs (line 59)
16fn settings() -> El {
17    column([
18        h1("Settings"),
19        titled_card(
20            "Account",
21            [
22                row([text("Email"), spacer(), text("user@example.com").muted()]),
23                row([
24                    text("Two-factor authentication"),
25                    spacer(),
26                    badge("Enabled").success(),
27                ]),
28                row([
29                    text("Recovery codes"),
30                    spacer(),
31                    button("Generate").secondary(),
32                ]),
33            ],
34        ),
35        titled_card(
36            "Appearance",
37            [
38                row([text("Theme"), spacer(), button("Dark").secondary()]),
39                row([text("Compact mode"), spacer(), badge("Off").muted()]),
40                row([text("Font size"), spacer(), text("14")]),
41            ],
42        ),
43        titled_card(
44            "Danger zone",
45            [row([
46                column([
47                    text("Delete account").bold(),
48                    text("Permanently remove your account and all data.")
49                        .muted()
50                        .small(),
51                ])
52                .gap(tokens::SPACE_1)
53                .align(Align::Start)
54                .width(Size::Hug),
55                spacer(),
56                button("Delete").destructive(),
57            ])],
58        ),
59        row([spacer(), button("Cancel").ghost(), button("Save").primary()]),
60    ])
61    .gap(tokens::SPACE_4)
62    .padding(tokens::SPACE_7)
63}
Source

pub fn success(self) -> Self

Examples found in repository?
examples/settings_calibration.rs (line 299)
292fn security_card() -> El {
293    card([
294        card_header([
295            card_title("Security"),
296            card_description("Two-factor authentication is enabled for all privileged users."),
297        ]),
298        card_content([
299            compact_stat("Passkeys", "2 registered", badge("On").success()),
300            compact_stat("Sessions", "3 active", button("Review").secondary()),
301        ]),
302    ])
303    .width(Size::Fill(1.0))
304}
More examples
Hide additional examples
examples/virtual_list.rs (line 22)
18fn build_row(i: usize) -> El {
19    let badge_el = match i % 5 {
20        0 => badge("info").muted(),
21        1 => badge("warn").warning(),
22        2 => badge("ok").success(),
23        3 => badge("err").destructive(),
24        _ => spacer(),
25    };
26    row([
27        text(format!("#{i:05}")).mono(),
28        spacer(),
29        text(format!("entry {i}")),
30        spacer(),
31        badge_el,
32    ])
33    .key(format!("row-{i}"))
34    .gap(tokens::SPACE_3)
35    .padding(Sides::xy(tokens::SPACE_3, tokens::SPACE_2))
36    .height(Size::Fixed(ROW_HEIGHT))
37}
examples/polish_calibration.rs (line 145)
143fn kpi_card(label: &'static str, value: &'static str, delta: &'static str, positive: bool) -> El {
144    let delta_badge = if positive {
145        badge(delta).success()
146    } else {
147        badge(delta).destructive()
148    };
149    let delta_badge = if label == "Latency" {
150        delta_badge.key("metric:kpi.badge")
151    } else {
152        delta_badge
153    };
154    let value_text = h2(value).display();
155    let value_text = if label == "Latency" {
156        value_text.key("metric:kpi.value")
157    } else {
158        value_text
159    };
160    card([
161        card_header([card_title(label)]),
162        card_content([
163            row([value_text, spacer(), delta_badge]).align(Align::Center),
164            text(if positive {
165                "Moving in the expected direction"
166            } else {
167                "Needs visual attention"
168            })
169            .muted(),
170        ])
171        .gap(tokens::SPACE_6),
172    ])
173    .key(if label == "Latency" {
174        "metric:kpi.card"
175    } else {
176        label
177    })
178    .width(Size::Fill(1.0))
179}
180
181fn table_card() -> El {
182    card([
183        card_header([card_title("Reference rows")]),
184        card_content([table([
185            table_header([table_row([
186                table_head("Status").width(Size::Fixed(86.0)),
187                table_head("Surface").width(Size::Fill(1.0)),
188                table_head("Owner").width(Size::Fixed(110.0)),
189                table_head("State").width(Size::Fixed(86.0)),
190            ])
191            .key("metric:table.header")]),
192            divider(),
193            table_body([
194                data_row("OK", "Settings card", "core", "selected", true, "success"),
195                data_row(
196                    "WARN",
197                    "Command palette density",
198                    "widgets",
199                    "needs work",
200                    false,
201                    "warning",
202                ),
203                data_row(
204                    "ERR",
205                    "Disabled and invalid states",
206                    "style",
207                    "missing",
208                    false,
209                    "destructive",
210                ),
211                data_row(
212                    "INFO",
213                    "Token resolution",
214                    "theme",
215                    "planned",
216                    false,
217                    "info",
218                ),
219                data_row(
220                    "OK",
221                    "Popover elevation",
222                    "shader",
223                    "queued",
224                    false,
225                    "success",
226                ),
227            ])
228            .gap(tokens::SPACE_1)
229            .width(Size::Fill(1.0)),
230        ])]),
231    ])
232    .key("metric:table.card")
233    .width(Size::Fill(1.2))
234    .height(Size::Fill(1.0))
235}
236
237fn data_row(
238    status: &'static str,
239    title: &'static str,
240    owner: &'static str,
241    state: &'static str,
242    selected: bool,
243    tone: &'static str,
244) -> El {
245    let status_badge = match tone {
246        "success" => badge(status).success(),
247        "warning" => badge(status).warning(),
248        "destructive" => badge(status).destructive(),
249        _ => badge(status).info(),
250    };
251    let status_badge = if selected {
252        status_badge.key("metric:table.badge")
253    } else {
254        status_badge
255    };
256
257    let mut row = table_row([
258        table_cell(status_badge).width(Size::Fixed(70.0)),
259        column([
260            text(title)
261                .font_weight(FontWeight::Medium)
262                .ellipsis()
263                .width(Size::Fill(1.0)),
264            text("Default styling probe.")
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0)),
271        table_cell(text(owner).muted()).width(Size::Fixed(110.0)),
272        table_cell(text(state).label().small()).width(Size::Fixed(86.0)),
273    ])
274    .key(if selected {
275        "metric:table.row".to_string()
276    } else {
277        format!("row-{title}")
278    })
279    .focusable();
280
281    if selected {
282        row = row.selected();
283    }
284
285    row
286}
examples/dashboard_01_calibration.rs (line 223)
214fn metric_card(
215    icon_name: &'static str,
216    title: &'static str,
217    value: &'static str,
218    delta: &'static str,
219    note: &'static str,
220    positive: bool,
221) -> El {
222    let badge = if positive {
223        badge(delta).success()
224    } else {
225        badge(delta).warning()
226    };
227    let badge = if title == "Total Revenue" {
228        badge.key("metric:kpi.badge")
229    } else {
230        badge
231    };
232    let value = if title == "Total Revenue" {
233        h2(value).ellipsis().key("metric:kpi.value")
234    } else {
235        h2(value).ellipsis()
236    };
237    card([card_content([
238        row([
239            row([
240                icon(icon_name)
241                    .color(tokens::MUTED_FOREGROUND)
242                    .icon_size(tokens::ICON_XS),
243                text(title).muted().ellipsis().width(Size::Fill(1.0)),
244            ])
245            .gap(tokens::SPACE_1)
246            .width(Size::Fill(1.0))
247            .align(Align::Center),
248            badge,
249        ])
250        .gap(tokens::SPACE_2)
251        .align(Align::Center),
252        value,
253        text(note).caption().ellipsis().width(Size::Fill(1.0)),
254    ])
255    .padding(tokens::SPACE_4)
256    .gap(tokens::SPACE_2)])
257    .key(if title == "Total Revenue" {
258        "metric:kpi.card"
259    } else {
260        title
261    })
262    .width(Size::Fill(1.0))
263}
264
265fn chart_card() -> El {
266    card([
267        card_header([
268            card_title("Visitors for the last 6 months"),
269            card_description("Total visitors by channel."),
270        ])
271        .padding(tokens::SPACE_4),
272        card_content([row(chart_bars())
273            .gap(2.0)
274            .height(Size::Fill(1.0))
275            .align(Align::End)])
276        .padding(Sides {
277            left: tokens::SPACE_4,
278            right: tokens::SPACE_4,
279            top: 0.0,
280            bottom: tokens::SPACE_4,
281        })
282        .height(Size::Fill(1.0)),
283    ])
284    .key("metric:chart.card")
285    .width(Size::Fill(1.0))
286    .height(Size::Fill(1.0))
287}
288
289fn chart_bars() -> Vec<El> {
290    [
291        48.0, 72.0, 56.0, 90.0, 64.0, 80.0, 108.0, 84.0, 122.0, 96.0, 136.0, 118.0,
292    ]
293    .into_iter()
294    .flat_map(|height| {
295        [
296            bar(height, tokens::MUTED_FOREGROUND),
297            bar((height - 28.0_f32).max(24.0), tokens::INPUT),
298        ]
299    })
300    .collect()
301}
302
303fn bar(height: f32, color: Color) -> El {
304    El::new(Kind::Custom("chart_bar"))
305        .fill(color)
306        .radius(tokens::RADIUS_SM)
307        .width(Size::Fill(1.0))
308        .height(Size::Fixed(height))
309}
310
311fn sales_card() -> El {
312    card([
313        card_header([
314            card_title("Recent Sales"),
315            card_description("You made 265 sales this month."),
316        ])
317        .padding(tokens::SPACE_4),
318        card_content([
319            sale_row("OM", "Olivia Martin", "olivia@example.com", "+$1,999.00"),
320            sale_row("JL", "Jackson Lee", "jackson@example.com", "+$39.00"),
321            sale_row("IN", "Isabella Nguyen", "isabella@example.com", "+$299.00"),
322            sale_row("WK", "William Kim", "will@example.com", "+$99.00"),
323        ])
324        .gap(tokens::SPACE_2)
325        .padding(Sides {
326            left: tokens::SPACE_4,
327            right: tokens::SPACE_4,
328            top: 0.0,
329            bottom: tokens::SPACE_4,
330        }),
331    ])
332    .key("metric:sales.card")
333    .width(Size::Fixed(330.0))
334    .height(Size::Fill(1.0))
335}
336
337fn sale_row(
338    initials: &'static str,
339    name: &'static str,
340    email: &'static str,
341    amount: &'static str,
342) -> El {
343    row([
344        icon_cell(initials),
345        column([
346            text(name).semibold().ellipsis().width(Size::Fill(1.0)),
347            text(email).caption().ellipsis().width(Size::Fill(1.0)),
348        ])
349        .gap(2.0)
350        .height(Size::Hug)
351        .width(Size::Fill(1.0)),
352        text(amount).label().small(),
353    ])
354    .gap(tokens::SPACE_2)
355    .height(Size::Fixed(42.0))
356    .align(Align::Center)
357}
358
359fn documents_card() -> El {
360    card([
361        card_header([card_title("Documents")]).padding(tokens::SPACE_4),
362        card_content([scroll([table([
363            table_header([table_row([
364                table_head("").width(Size::Fixed(35.0)),
365                table_head("Header").width(Size::Fill(1.8)),
366                table_head("Section Type").width(Size::Fill(1.0)),
367                table_head("Status").width(Size::Fixed(104.0)),
368                table_head("Target").width(Size::Fixed(64.0)),
369                table_head("Limit").width(Size::Fixed(64.0)),
370                table_head("Reviewer").width(Size::Fixed(128.0)),
371                table_head("").width(Size::Fixed(32.0)),
372            ])
373            .padding(Sides::xy(tokens::SPACE_4, 0.0))
374            .key("metric:table.header")]),
375            divider(),
376            table_body([
377                document_row(
378                    "Cover page",
379                    "Cover page",
380                    "In Process",
381                    "18",
382                    "5",
383                    "Eddie Lake",
384                    "info",
385                ),
386                document_row(
387                    "Table of contents",
388                    "Table of contents",
389                    "Done",
390                    "29",
391                    "24",
392                    "Eddie Lake",
393                    "success",
394                ),
395            ]),
396        ])])
397        .height(Size::Fill(1.0))])
398        .gap(0.0)
399        .padding(0.0)
400        .height(Size::Fill(1.0)),
401    ])
402    .key("metric:table.card")
403    .height(Size::Fill(1.0))
404}
405
406fn document_row(
407    header: &'static str,
408    section: &'static str,
409    status: &'static str,
410    target: &'static str,
411    limit: &'static str,
412    reviewer: &'static str,
413    tone: &'static str,
414) -> El {
415    let status_badge = match tone {
416        "success" => badge(status).success(),
417        _ => badge(status).info(),
418    };
419    table_row([
420        table_utility_cell("::"),
421        table_cell(text(header).label().small()).width(Size::Fill(1.8)),
422        table_cell(text(section).muted()).width(Size::Fill(1.0)),
423        table_cell(status_badge).width(Size::Fixed(104.0)),
424        table_cell(text(target).label().small()).width(Size::Fixed(64.0)),
425        table_cell(text(limit).label().small()).width(Size::Fixed(64.0)),
426        table_cell(text(reviewer).muted()).width(Size::Fixed(128.0)),
427        table_action_cell(),
428    ])
429    .padding(Sides::xy(tokens::SPACE_4, 0.0))
430    .key(if header == "Cover page" {
431        "metric:table.row"
432    } else {
433        header
434    })
435}
examples/palette_demo.rs (line 330)
314fn component_section() -> El {
315    card([
316        card_header([
317            card_title("Stock widgets"),
318            card_description("The same palette applied to regular component constructors."),
319        ]),
320        card_content([
321            row([
322                button("Primary").primary(),
323                button("Secondary").secondary(),
324                button("Outline").outline(),
325                button("Ghost").ghost(),
326            ])
327            .gap(tokens::SPACE_2)
328            .align(Align::Center),
329            row([
330                badge("success").success(),
331                badge("warning").warning(),
332                badge("destructive").destructive(),
333                badge("info").info(),
334                badge("muted").muted(),
335            ])
336            .gap(tokens::SPACE_2)
337            .align(Align::Center),
338            row([
339                text_input("palette search", &Selection::default(), "palette:search")
340                    .width(Size::Fill(1.0)),
341                button_with_icon("settings", "Tune").secondary(),
342            ])
343            .gap(tokens::SPACE_2)
344            .align(Align::Center),
345            row([
346                surface_sample("Card", tokens::CARD),
347                surface_sample("Muted", tokens::MUTED),
348                surface_sample("Popover", tokens::POPOVER),
349            ])
350            .gap(tokens::SPACE_3)
351            .align(Align::Stretch),
352        ])
353        .gap(tokens::SPACE_4),
354    ])
355    .height(Size::Hug)
356}
examples/settings.rs (line 26)
16fn settings() -> El {
17    column([
18        h1("Settings"),
19        titled_card(
20            "Account",
21            [
22                row([text("Email"), spacer(), text("user@example.com").muted()]),
23                row([
24                    text("Two-factor authentication"),
25                    spacer(),
26                    badge("Enabled").success(),
27                ]),
28                row([
29                    text("Recovery codes"),
30                    spacer(),
31                    button("Generate").secondary(),
32                ]),
33            ],
34        ),
35        titled_card(
36            "Appearance",
37            [
38                row([text("Theme"), spacer(), button("Dark").secondary()]),
39                row([text("Compact mode"), spacer(), badge("Off").muted()]),
40                row([text("Font size"), spacer(), text("14")]),
41            ],
42        ),
43        titled_card(
44            "Danger zone",
45            [row([
46                column([
47                    text("Delete account").bold(),
48                    text("Permanently remove your account and all data.")
49                        .muted()
50                        .small(),
51                ])
52                .gap(tokens::SPACE_1)
53                .align(Align::Start)
54                .width(Size::Hug),
55                spacer(),
56                button("Delete").destructive(),
57            ])],
58        ),
59        row([spacer(), button("Cancel").ghost(), button("Save").primary()]),
60    ])
61    .gap(tokens::SPACE_4)
62    .padding(tokens::SPACE_7)
63}
Source

pub fn warning(self) -> Self

Examples found in repository?
examples/virtual_list.rs (line 21)
18fn build_row(i: usize) -> El {
19    let badge_el = match i % 5 {
20        0 => badge("info").muted(),
21        1 => badge("warn").warning(),
22        2 => badge("ok").success(),
23        3 => badge("err").destructive(),
24        _ => spacer(),
25    };
26    row([
27        text(format!("#{i:05}")).mono(),
28        spacer(),
29        text(format!("entry {i}")),
30        spacer(),
31        badge_el,
32    ])
33    .key(format!("row-{i}"))
34    .gap(tokens::SPACE_3)
35    .padding(Sides::xy(tokens::SPACE_3, tokens::SPACE_2))
36    .height(Size::Fixed(ROW_HEIGHT))
37}
More examples
Hide additional examples
examples/dashboard_01_calibration.rs (line 225)
214fn metric_card(
215    icon_name: &'static str,
216    title: &'static str,
217    value: &'static str,
218    delta: &'static str,
219    note: &'static str,
220    positive: bool,
221) -> El {
222    let badge = if positive {
223        badge(delta).success()
224    } else {
225        badge(delta).warning()
226    };
227    let badge = if title == "Total Revenue" {
228        badge.key("metric:kpi.badge")
229    } else {
230        badge
231    };
232    let value = if title == "Total Revenue" {
233        h2(value).ellipsis().key("metric:kpi.value")
234    } else {
235        h2(value).ellipsis()
236    };
237    card([card_content([
238        row([
239            row([
240                icon(icon_name)
241                    .color(tokens::MUTED_FOREGROUND)
242                    .icon_size(tokens::ICON_XS),
243                text(title).muted().ellipsis().width(Size::Fill(1.0)),
244            ])
245            .gap(tokens::SPACE_1)
246            .width(Size::Fill(1.0))
247            .align(Align::Center),
248            badge,
249        ])
250        .gap(tokens::SPACE_2)
251        .align(Align::Center),
252        value,
253        text(note).caption().ellipsis().width(Size::Fill(1.0)),
254    ])
255    .padding(tokens::SPACE_4)
256    .gap(tokens::SPACE_2)])
257    .key(if title == "Total Revenue" {
258        "metric:kpi.card"
259    } else {
260        title
261    })
262    .width(Size::Fill(1.0))
263}
examples/polish_calibration.rs (line 247)
237fn data_row(
238    status: &'static str,
239    title: &'static str,
240    owner: &'static str,
241    state: &'static str,
242    selected: bool,
243    tone: &'static str,
244) -> El {
245    let status_badge = match tone {
246        "success" => badge(status).success(),
247        "warning" => badge(status).warning(),
248        "destructive" => badge(status).destructive(),
249        _ => badge(status).info(),
250    };
251    let status_badge = if selected {
252        status_badge.key("metric:table.badge")
253    } else {
254        status_badge
255    };
256
257    let mut row = table_row([
258        table_cell(status_badge).width(Size::Fixed(70.0)),
259        column([
260            text(title)
261                .font_weight(FontWeight::Medium)
262                .ellipsis()
263                .width(Size::Fill(1.0)),
264            text("Default styling probe.")
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0)),
271        table_cell(text(owner).muted()).width(Size::Fixed(110.0)),
272        table_cell(text(state).label().small()).width(Size::Fixed(86.0)),
273    ])
274    .key(if selected {
275        "metric:table.row".to_string()
276    } else {
277        format!("row-{title}")
278    })
279    .focusable();
280
281    if selected {
282        row = row.selected();
283    }
284
285    row
286}
examples/palette_demo.rs (line 331)
314fn component_section() -> El {
315    card([
316        card_header([
317            card_title("Stock widgets"),
318            card_description("The same palette applied to regular component constructors."),
319        ]),
320        card_content([
321            row([
322                button("Primary").primary(),
323                button("Secondary").secondary(),
324                button("Outline").outline(),
325                button("Ghost").ghost(),
326            ])
327            .gap(tokens::SPACE_2)
328            .align(Align::Center),
329            row([
330                badge("success").success(),
331                badge("warning").warning(),
332                badge("destructive").destructive(),
333                badge("info").info(),
334                badge("muted").muted(),
335            ])
336            .gap(tokens::SPACE_2)
337            .align(Align::Center),
338            row([
339                text_input("palette search", &Selection::default(), "palette:search")
340                    .width(Size::Fill(1.0)),
341                button_with_icon("settings", "Tune").secondary(),
342            ])
343            .gap(tokens::SPACE_2)
344            .align(Align::Center),
345            row([
346                surface_sample("Card", tokens::CARD),
347                surface_sample("Muted", tokens::MUTED),
348                surface_sample("Popover", tokens::POPOVER),
349            ])
350            .gap(tokens::SPACE_3)
351            .align(Align::Stretch),
352        ])
353        .gap(tokens::SPACE_4),
354    ])
355    .height(Size::Hug)
356}
Source

pub fn destructive(self) -> Self

Examples found in repository?
examples/virtual_list.rs (line 23)
18fn build_row(i: usize) -> El {
19    let badge_el = match i % 5 {
20        0 => badge("info").muted(),
21        1 => badge("warn").warning(),
22        2 => badge("ok").success(),
23        3 => badge("err").destructive(),
24        _ => spacer(),
25    };
26    row([
27        text(format!("#{i:05}")).mono(),
28        spacer(),
29        text(format!("entry {i}")),
30        spacer(),
31        badge_el,
32    ])
33    .key(format!("row-{i}"))
34    .gap(tokens::SPACE_3)
35    .padding(Sides::xy(tokens::SPACE_3, tokens::SPACE_2))
36    .height(Size::Fixed(ROW_HEIGHT))
37}
More examples
Hide additional examples
examples/polish_calibration.rs (line 147)
143fn kpi_card(label: &'static str, value: &'static str, delta: &'static str, positive: bool) -> El {
144    let delta_badge = if positive {
145        badge(delta).success()
146    } else {
147        badge(delta).destructive()
148    };
149    let delta_badge = if label == "Latency" {
150        delta_badge.key("metric:kpi.badge")
151    } else {
152        delta_badge
153    };
154    let value_text = h2(value).display();
155    let value_text = if label == "Latency" {
156        value_text.key("metric:kpi.value")
157    } else {
158        value_text
159    };
160    card([
161        card_header([card_title(label)]),
162        card_content([
163            row([value_text, spacer(), delta_badge]).align(Align::Center),
164            text(if positive {
165                "Moving in the expected direction"
166            } else {
167                "Needs visual attention"
168            })
169            .muted(),
170        ])
171        .gap(tokens::SPACE_6),
172    ])
173    .key(if label == "Latency" {
174        "metric:kpi.card"
175    } else {
176        label
177    })
178    .width(Size::Fill(1.0))
179}
180
181fn table_card() -> El {
182    card([
183        card_header([card_title("Reference rows")]),
184        card_content([table([
185            table_header([table_row([
186                table_head("Status").width(Size::Fixed(86.0)),
187                table_head("Surface").width(Size::Fill(1.0)),
188                table_head("Owner").width(Size::Fixed(110.0)),
189                table_head("State").width(Size::Fixed(86.0)),
190            ])
191            .key("metric:table.header")]),
192            divider(),
193            table_body([
194                data_row("OK", "Settings card", "core", "selected", true, "success"),
195                data_row(
196                    "WARN",
197                    "Command palette density",
198                    "widgets",
199                    "needs work",
200                    false,
201                    "warning",
202                ),
203                data_row(
204                    "ERR",
205                    "Disabled and invalid states",
206                    "style",
207                    "missing",
208                    false,
209                    "destructive",
210                ),
211                data_row(
212                    "INFO",
213                    "Token resolution",
214                    "theme",
215                    "planned",
216                    false,
217                    "info",
218                ),
219                data_row(
220                    "OK",
221                    "Popover elevation",
222                    "shader",
223                    "queued",
224                    false,
225                    "success",
226                ),
227            ])
228            .gap(tokens::SPACE_1)
229            .width(Size::Fill(1.0)),
230        ])]),
231    ])
232    .key("metric:table.card")
233    .width(Size::Fill(1.2))
234    .height(Size::Fill(1.0))
235}
236
237fn data_row(
238    status: &'static str,
239    title: &'static str,
240    owner: &'static str,
241    state: &'static str,
242    selected: bool,
243    tone: &'static str,
244) -> El {
245    let status_badge = match tone {
246        "success" => badge(status).success(),
247        "warning" => badge(status).warning(),
248        "destructive" => badge(status).destructive(),
249        _ => badge(status).info(),
250    };
251    let status_badge = if selected {
252        status_badge.key("metric:table.badge")
253    } else {
254        status_badge
255    };
256
257    let mut row = table_row([
258        table_cell(status_badge).width(Size::Fixed(70.0)),
259        column([
260            text(title)
261                .font_weight(FontWeight::Medium)
262                .ellipsis()
263                .width(Size::Fill(1.0)),
264            text("Default styling probe.")
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0)),
271        table_cell(text(owner).muted()).width(Size::Fixed(110.0)),
272        table_cell(text(state).label().small()).width(Size::Fixed(86.0)),
273    ])
274    .key(if selected {
275        "metric:table.row".to_string()
276    } else {
277        format!("row-{title}")
278    })
279    .focusable();
280
281    if selected {
282        row = row.selected();
283    }
284
285    row
286}
examples/modal.rs (line 33)
9fn modal_fixture() -> El {
10    stack([
11        column([
12            h1("Account"),
13            titled_card(
14                "Profile",
15                [
16                    row([text("Email"), spacer(), text("user@example.com").muted()]),
17                    row([text("Plan"), spacer(), badge("Pro").info()]),
18                ],
19            ),
20            titled_card(
21                "Danger zone",
22                [row([
23                    column([
24                        text("Delete account").bold(),
25                        text("Remove this account and all associated data.")
26                            .muted()
27                            .small(),
28                    ])
29                    .gap(tokens::SPACE_1)
30                    .align(Align::Start)
31                    .width(Size::Hug),
32                    spacer(),
33                    button("Delete").destructive().key("open-delete"),
34                ])],
35            ),
36        ])
37        .gap(tokens::SPACE_4)
38        .padding(tokens::SPACE_7),
39        modal(
40            "delete-account",
41            "Delete account?",
42            [
43                text("Permanent action. Export data first.").muted(),
44                row([
45                    spacer(),
46                    button("Cancel").ghost().key("cancel-delete"),
47                    button("Delete").destructive().key("confirm-delete"),
48                ])
49                .gap(tokens::SPACE_2),
50            ],
51        ),
52    ])
53}
examples/palette_demo.rs (line 332)
314fn component_section() -> El {
315    card([
316        card_header([
317            card_title("Stock widgets"),
318            card_description("The same palette applied to regular component constructors."),
319        ]),
320        card_content([
321            row([
322                button("Primary").primary(),
323                button("Secondary").secondary(),
324                button("Outline").outline(),
325                button("Ghost").ghost(),
326            ])
327            .gap(tokens::SPACE_2)
328            .align(Align::Center),
329            row([
330                badge("success").success(),
331                badge("warning").warning(),
332                badge("destructive").destructive(),
333                badge("info").info(),
334                badge("muted").muted(),
335            ])
336            .gap(tokens::SPACE_2)
337            .align(Align::Center),
338            row([
339                text_input("palette search", &Selection::default(), "palette:search")
340                    .width(Size::Fill(1.0)),
341                button_with_icon("settings", "Tune").secondary(),
342            ])
343            .gap(tokens::SPACE_2)
344            .align(Align::Center),
345            row([
346                surface_sample("Card", tokens::CARD),
347                surface_sample("Muted", tokens::MUTED),
348                surface_sample("Popover", tokens::POPOVER),
349            ])
350            .gap(tokens::SPACE_3)
351            .align(Align::Stretch),
352        ])
353        .gap(tokens::SPACE_4),
354    ])
355    .height(Size::Hug)
356}
examples/settings.rs (line 56)
16fn settings() -> El {
17    column([
18        h1("Settings"),
19        titled_card(
20            "Account",
21            [
22                row([text("Email"), spacer(), text("user@example.com").muted()]),
23                row([
24                    text("Two-factor authentication"),
25                    spacer(),
26                    badge("Enabled").success(),
27                ]),
28                row([
29                    text("Recovery codes"),
30                    spacer(),
31                    button("Generate").secondary(),
32                ]),
33            ],
34        ),
35        titled_card(
36            "Appearance",
37            [
38                row([text("Theme"), spacer(), button("Dark").secondary()]),
39                row([text("Compact mode"), spacer(), badge("Off").muted()]),
40                row([text("Font size"), spacer(), text("14")]),
41            ],
42        ),
43        titled_card(
44            "Danger zone",
45            [row([
46                column([
47                    text("Delete account").bold(),
48                    text("Permanently remove your account and all data.")
49                        .muted()
50                        .small(),
51                ])
52                .gap(tokens::SPACE_1)
53                .align(Align::Start)
54                .width(Size::Hug),
55                spacer(),
56                button("Delete").destructive(),
57            ])],
58        ),
59        row([spacer(), button("Cancel").ghost(), button("Save").primary()]),
60    ])
61    .gap(tokens::SPACE_4)
62    .padding(tokens::SPACE_7)
63}
Source

pub fn info(self) -> Self

Examples found in repository?
examples/scroll_list.rs (line 19)
15fn scroll_list_fixture() -> El {
16    let rows: Vec<El> = (0..20)
17        .map(|i| {
18            row([
19                badge(format!("#{i}")).info(),
20                text(format!("Notification {i}")).bold(),
21                spacer(),
22                text(format!("{}m ago", i + 1)).muted(),
23            ])
24            .gap(tokens::SPACE_2)
25            .height(Size::Fixed(44.0))
26            .padding(Sides::xy(tokens::SPACE_3, tokens::SPACE_2))
27        })
28        .collect();
29
30    let list = scroll(rows)
31        .key("notifications")
32        .height(Size::Fixed(420.0))
33        .padding(tokens::SPACE_2);
34
35    column([
36        h2("Notifications"),
37        text("Roll the wheel inside the panel to scroll. The content is taller than the viewport.")
38            .muted(),
39        list,
40    ])
41    .gap(tokens::SPACE_4)
42    .padding(tokens::SPACE_7)
43}
More examples
Hide additional examples
examples/dashboard_01_calibration.rs (line 417)
406fn document_row(
407    header: &'static str,
408    section: &'static str,
409    status: &'static str,
410    target: &'static str,
411    limit: &'static str,
412    reviewer: &'static str,
413    tone: &'static str,
414) -> El {
415    let status_badge = match tone {
416        "success" => badge(status).success(),
417        _ => badge(status).info(),
418    };
419    table_row([
420        table_utility_cell("::"),
421        table_cell(text(header).label().small()).width(Size::Fill(1.8)),
422        table_cell(text(section).muted()).width(Size::Fill(1.0)),
423        table_cell(status_badge).width(Size::Fixed(104.0)),
424        table_cell(text(target).label().small()).width(Size::Fixed(64.0)),
425        table_cell(text(limit).label().small()).width(Size::Fixed(64.0)),
426        table_cell(text(reviewer).muted()).width(Size::Fixed(128.0)),
427        table_action_cell(),
428    ])
429    .padding(Sides::xy(tokens::SPACE_4, 0.0))
430    .key(if header == "Cover page" {
431        "metric:table.row"
432    } else {
433        header
434    })
435}
examples/polish_calibration.rs (line 249)
237fn data_row(
238    status: &'static str,
239    title: &'static str,
240    owner: &'static str,
241    state: &'static str,
242    selected: bool,
243    tone: &'static str,
244) -> El {
245    let status_badge = match tone {
246        "success" => badge(status).success(),
247        "warning" => badge(status).warning(),
248        "destructive" => badge(status).destructive(),
249        _ => badge(status).info(),
250    };
251    let status_badge = if selected {
252        status_badge.key("metric:table.badge")
253    } else {
254        status_badge
255    };
256
257    let mut row = table_row([
258        table_cell(status_badge).width(Size::Fixed(70.0)),
259        column([
260            text(title)
261                .font_weight(FontWeight::Medium)
262                .ellipsis()
263                .width(Size::Fill(1.0)),
264            text("Default styling probe.")
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0)),
271        table_cell(text(owner).muted()).width(Size::Fixed(110.0)),
272        table_cell(text(state).label().small()).width(Size::Fixed(86.0)),
273    ])
274    .key(if selected {
275        "metric:table.row".to_string()
276    } else {
277        format!("row-{title}")
278    })
279    .focusable();
280
281    if selected {
282        row = row.selected();
283    }
284
285    row
286}
examples/modal.rs (line 17)
9fn modal_fixture() -> El {
10    stack([
11        column([
12            h1("Account"),
13            titled_card(
14                "Profile",
15                [
16                    row([text("Email"), spacer(), text("user@example.com").muted()]),
17                    row([text("Plan"), spacer(), badge("Pro").info()]),
18                ],
19            ),
20            titled_card(
21                "Danger zone",
22                [row([
23                    column([
24                        text("Delete account").bold(),
25                        text("Remove this account and all associated data.")
26                            .muted()
27                            .small(),
28                    ])
29                    .gap(tokens::SPACE_1)
30                    .align(Align::Start)
31                    .width(Size::Hug),
32                    spacer(),
33                    button("Delete").destructive().key("open-delete"),
34                ])],
35            ),
36        ])
37        .gap(tokens::SPACE_4)
38        .padding(tokens::SPACE_7),
39        modal(
40            "delete-account",
41            "Delete account?",
42            [
43                text("Permanent action. Export data first.").muted(),
44                row([
45                    spacer(),
46                    button("Cancel").ghost().key("cancel-delete"),
47                    button("Delete").destructive().key("confirm-delete"),
48                ])
49                .gap(tokens::SPACE_2),
50            ],
51        ),
52    ])
53}
examples/palette_demo.rs (line 333)
314fn component_section() -> El {
315    card([
316        card_header([
317            card_title("Stock widgets"),
318            card_description("The same palette applied to regular component constructors."),
319        ]),
320        card_content([
321            row([
322                button("Primary").primary(),
323                button("Secondary").secondary(),
324                button("Outline").outline(),
325                button("Ghost").ghost(),
326            ])
327            .gap(tokens::SPACE_2)
328            .align(Align::Center),
329            row([
330                badge("success").success(),
331                badge("warning").warning(),
332                badge("destructive").destructive(),
333                badge("info").info(),
334                badge("muted").muted(),
335            ])
336            .gap(tokens::SPACE_2)
337            .align(Align::Center),
338            row([
339                text_input("palette search", &Selection::default(), "palette:search")
340                    .width(Size::Fill(1.0)),
341                button_with_icon("settings", "Tune").secondary(),
342            ])
343            .gap(tokens::SPACE_2)
344            .align(Align::Center),
345            row([
346                surface_sample("Card", tokens::CARD),
347                surface_sample("Muted", tokens::MUTED),
348                surface_sample("Popover", tokens::POPOVER),
349            ])
350            .gap(tokens::SPACE_3)
351            .align(Align::Stretch),
352        ])
353        .gap(tokens::SPACE_4),
354    ])
355    .height(Size::Hug)
356}
Source

pub fn secondary(self) -> Self

Default-styled secondary surface. This is the default look for button(...); calling .secondary() makes intent explicit.

Examples found in repository?
examples/settings_calibration.rs (line 103)
97fn settings_header() -> El {
98    row([
99        icon_button("menu").ghost(),
100        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
101        h3("Settings").key("metric:page.title"),
102        spacer(),
103        button("Reset").secondary(),
104        button("Save changes").primary(),
105    ])
106    .key("metric:header")
107    .gap(tokens::SPACE_3)
108    .height(Size::Fixed(56.0))
109    .padding(Sides::xy(tokens::SPACE_4, 0.0))
110    .align(Align::Center)
111    .stroke(tokens::BORDER)
112}
113
114fn settings_nav_card() -> El {
115    column([
116        settings_nav_item("Account", true),
117        settings_nav_item("Security", false),
118        settings_nav_item("Notifications", false),
119        settings_nav_item("Appearance", false),
120        settings_nav_item("Billing", false),
121    ])
122    .gap(tokens::SPACE_1)
123    .padding(tokens::SPACE_1)
124    .width(Size::Fixed(220.0))
125    .height(Size::Fill(1.0))
126    .style_profile(StyleProfile::Surface)
127    .surface_role(SurfaceRole::Panel)
128    .fill(tokens::CARD)
129    .stroke(tokens::BORDER)
130    .radius(tokens::RADIUS_MD)
131    .shadow(tokens::SHADOW_MD)
132}
133
134fn settings_nav_item(label: &'static str, selected: bool) -> El {
135    let mut item = row([
136        El::new(Kind::Custom("nav-dot"))
137            .fill(tokens::MUTED_FOREGROUND)
138            .radius(tokens::RADIUS_PILL)
139            .width(Size::Fixed(6.0))
140            .height(Size::Fixed(6.0)),
141        text(label)
142            .font_weight(FontWeight::Medium)
143            .ellipsis()
144            .width(Size::Fill(1.0)),
145    ])
146    .key(if selected {
147        "metric:settings.nav.row".to_string()
148    } else {
149        format!("settings-nav-{label}")
150    })
151    .metrics_role(MetricsRole::ListItem)
152    .align(Align::Center)
153    .focusable();
154
155    if selected {
156        item = item.current();
157    } else {
158        item = item.color(tokens::MUTED_FOREGROUND);
159    }
160
161    item
162}
163
164fn settings_body() -> El {
165    column([
166        column([
167            h1("Account").heading().key("metric:section.title"),
168            text("Manage identity, workspace defaults, and security preferences.")
169                .muted()
170                .wrap_text()
171                .key("metric:page.subtitle"),
172        ])
173        .gap(tokens::SPACE_1)
174        .height(Size::Hug),
175        scroll([profile_card(), preferences_card()])
176            .key("settings-body-scroll")
177            .gap(tokens::SPACE_4)
178            .width(Size::Fill(1.0))
179            .height(Size::Fill(1.0)),
180    ])
181    .gap(tokens::SPACE_4)
182    .width(Size::Fill(1.0))
183    .height(Size::Fill(1.0))
184}
185
186fn profile_card() -> El {
187    card([
188        card_header([
189            card_title("Profile"),
190            card_description("This information appears in audit logs and shared documents."),
191        ]),
192        card_content([form([
193            row([
194                setting_field("Display name", "Alicia Koch", "display-name"),
195                setting_field("Email", "alicia@acme.co", "email"),
196            ])
197            .gap(tokens::SPACE_3),
198            row([
199                setting_select("Role", "Workspace admin", "role"),
200                setting_select("Region", "US East", "region"),
201            ])
202            .gap(tokens::SPACE_3),
203        ])]),
204    ])
205    .key("metric:profile.card")
206}
207
208fn setting_field(label: &'static str, value: &'static str, key: &'static str) -> El {
209    form_item([
210        form_label(label),
211        form_control(
212            text_input(value, &Selection::caret(key, value.len()), key).key(
213                if key == "display-name" {
214                    "metric:form.input"
215                } else {
216                    key
217                },
218            ),
219        ),
220    ])
221    .width(Size::Fill(1.0))
222}
223
224fn setting_select(label: &'static str, value: &'static str, key: &'static str) -> El {
225    form_item([form_label(label), form_control(select_trigger(key, value))]).width(Size::Fill(1.0))
226}
227
228fn preferences_card() -> El {
229    card([
230        card_header([
231            card_title("Preferences"),
232            card_description("Defaults used when creating new dashboards and exports."),
233        ]),
234        card_content([column([
235            preference_row(
236                "Compact navigation",
237                "Use tighter rows in the sidebar and command menus.",
238                switch(true).key("compact-navigation"),
239            ),
240            divider(),
241            preference_row(
242                "Email summaries",
243                "Send a daily digest when documents change.",
244                switch(false).key("email-summaries"),
245            ),
246            divider(),
247            preference_row(
248                "Require approval",
249                "Route external sharing through an owner review.",
250                checkbox(true).key("approval-required"),
251            ),
252        ])
253        .gap(0.0)
254        .width(Size::Fill(1.0))])
255        .padding(0.0),
256    ])
257    .key("metric:preferences.card")
258}
259
260fn preference_row(title: &'static str, description: &'static str, control: El) -> El {
261    row([
262        column([
263            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
264            text(description)
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0))
271        .height(Size::Hug),
272        control,
273    ])
274    .key(if title == "Compact navigation" {
275        "metric:preference.row".to_string()
276    } else {
277        format!("preference-{title}")
278    })
279    .metrics_role(MetricsRole::PreferenceRow)
280    .gap(tokens::SPACE_4)
281    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_3))
282    .align(Align::Center)
283}
284
285fn settings_aside() -> El {
286    column([security_card(), scale_card()])
287        .gap(tokens::SPACE_4)
288        .width(Size::Fixed(300.0))
289        .height(Size::Fill(1.0))
290}
291
292fn security_card() -> El {
293    card([
294        card_header([
295            card_title("Security"),
296            card_description("Two-factor authentication is enabled for all privileged users."),
297        ]),
298        card_content([
299            compact_stat("Passkeys", "2 registered", badge("On").success()),
300            compact_stat("Sessions", "3 active", button("Review").secondary()),
301        ]),
302    ])
303    .width(Size::Fill(1.0))
304}
More examples
Hide additional examples
examples/polish_calibration.rs (line 131)
119fn toolbar() -> El {
120    row([
121        column([
122            h1("Polish calibration").key("metric:page.title"),
123            text("A representative app surface for default tuning.")
124                .muted()
125                .key("metric:page.subtitle"),
126        ])
127        .gap(tokens::SPACE_2)
128        .height(Size::Hug),
129        spacer(),
130        button_with_icon("search", "Preview")
131            .secondary()
132            .key("metric:action.secondary"),
133        button_with_icon("upload", "Publish")
134            .primary()
135            .key("metric:action.primary"),
136    ])
137    .key("metric:header")
138    .gap(tokens::SPACE_4)
139    .height(Size::Hug)
140    .align(Align::Start)
141}
142
143fn kpi_card(label: &'static str, value: &'static str, delta: &'static str, positive: bool) -> El {
144    let delta_badge = if positive {
145        badge(delta).success()
146    } else {
147        badge(delta).destructive()
148    };
149    let delta_badge = if label == "Latency" {
150        delta_badge.key("metric:kpi.badge")
151    } else {
152        delta_badge
153    };
154    let value_text = h2(value).display();
155    let value_text = if label == "Latency" {
156        value_text.key("metric:kpi.value")
157    } else {
158        value_text
159    };
160    card([
161        card_header([card_title(label)]),
162        card_content([
163            row([value_text, spacer(), delta_badge]).align(Align::Center),
164            text(if positive {
165                "Moving in the expected direction"
166            } else {
167                "Needs visual attention"
168            })
169            .muted(),
170        ])
171        .gap(tokens::SPACE_6),
172    ])
173    .key(if label == "Latency" {
174        "metric:kpi.card"
175    } else {
176        label
177    })
178    .width(Size::Fill(1.0))
179}
180
181fn table_card() -> El {
182    card([
183        card_header([card_title("Reference rows")]),
184        card_content([table([
185            table_header([table_row([
186                table_head("Status").width(Size::Fixed(86.0)),
187                table_head("Surface").width(Size::Fill(1.0)),
188                table_head("Owner").width(Size::Fixed(110.0)),
189                table_head("State").width(Size::Fixed(86.0)),
190            ])
191            .key("metric:table.header")]),
192            divider(),
193            table_body([
194                data_row("OK", "Settings card", "core", "selected", true, "success"),
195                data_row(
196                    "WARN",
197                    "Command palette density",
198                    "widgets",
199                    "needs work",
200                    false,
201                    "warning",
202                ),
203                data_row(
204                    "ERR",
205                    "Disabled and invalid states",
206                    "style",
207                    "missing",
208                    false,
209                    "destructive",
210                ),
211                data_row(
212                    "INFO",
213                    "Token resolution",
214                    "theme",
215                    "planned",
216                    false,
217                    "info",
218                ),
219                data_row(
220                    "OK",
221                    "Popover elevation",
222                    "shader",
223                    "queued",
224                    false,
225                    "success",
226                ),
227            ])
228            .gap(tokens::SPACE_1)
229            .width(Size::Fill(1.0)),
230        ])]),
231    ])
232    .key("metric:table.card")
233    .width(Size::Fill(1.2))
234    .height(Size::Fill(1.0))
235}
236
237fn data_row(
238    status: &'static str,
239    title: &'static str,
240    owner: &'static str,
241    state: &'static str,
242    selected: bool,
243    tone: &'static str,
244) -> El {
245    let status_badge = match tone {
246        "success" => badge(status).success(),
247        "warning" => badge(status).warning(),
248        "destructive" => badge(status).destructive(),
249        _ => badge(status).info(),
250    };
251    let status_badge = if selected {
252        status_badge.key("metric:table.badge")
253    } else {
254        status_badge
255    };
256
257    let mut row = table_row([
258        table_cell(status_badge).width(Size::Fixed(70.0)),
259        column([
260            text(title)
261                .font_weight(FontWeight::Medium)
262                .ellipsis()
263                .width(Size::Fill(1.0)),
264            text("Default styling probe.")
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0)),
271        table_cell(text(owner).muted()).width(Size::Fixed(110.0)),
272        table_cell(text(state).label().small()).width(Size::Fixed(86.0)),
273    ])
274    .key(if selected {
275        "metric:table.row".to_string()
276    } else {
277        format!("row-{title}")
278    })
279    .focusable();
280
281    if selected {
282        row = row.selected();
283    }
284
285    row
286}
287
288fn command_card() -> El {
289    card([
290        card_header([card_title("Command surface")]),
291        card_content([
292            text_input(
293                "Search commands...",
294                &Selection::default(),
295                "command-search",
296            )
297            .key("metric:command.input")
298            .width(Size::Fill(1.0)),
299            popover_panel([
300                command_row("git-branch", "New branch", "Ctrl+B").key("metric:command.row"),
301                command_row("git-commit", "Commit staged files", "Ctrl+Enter")
302                    .key("command-row-commit"),
303                command_row("refresh-cw", "Refresh repository", "Ctrl+R")
304                    .key("command-row-refresh"),
305                command_row("alert-circle", "Force push", "Danger").key("command-row-force"),
306            ])
307            .width(Size::Fill(1.0)),
308            scroll([form_probe()]).key("form-probe-scroll"),
309        ])
310        .height(Size::Fill(1.0)),
311    ])
312    .key("metric:command.card")
313    .width(Size::Fill(0.8))
314    .height(Size::Fill(1.0))
315}
316
317fn form_probe() -> El {
318    form([
319        form_item([
320            form_label("Valid input"),
321            form_control(
322                text_input(
323                    "Valid input",
324                    &Selection::caret("valid-input", 11),
325                    "valid-input",
326                )
327                .key("metric:form.input"),
328            ),
329            form_description("Default field spacing and helper text."),
330        ]),
331        form_item([
332            form_label("Invalid input"),
333            form_control(
334                text_input(
335                    "Invalid input",
336                    &Selection::caret("invalid-input", 13),
337                    "invalid-input",
338                )
339                .invalid(),
340            ),
341            form_message("This field needs attention."),
342        ]),
343        row([
344            button("Disabled").secondary().disabled(),
345            button("Loading").primary().loading(),
346            spacer(),
347        ]),
348    ])
349    .padding(tokens::SPACE_3)
350    .fill(tokens::MUTED)
351    .stroke(tokens::BORDER)
352    .radius(tokens::RADIUS_MD)
353}
examples/custom_shader.rs (line 62)
31fn fixture() -> El {
32    column([
33        h1("Custom shader demo"),
34        paragraph(
35            "Three buttons below paint via a registered custom shader \
36             (gradient.wgsl). The right-hand button is a stock rounded_rect \
37             for contrast.",
38        )
39        .muted(),
40        titled_card(
41            "gradient.wgsl — vertical linear gradient",
42            [row([
43                gradient_button(
44                    "Sunrise",
45                    Color::rgb(255, 200, 90),
46                    Color::rgb(245, 95, 110),
47                    tokens::RADIUS_MD,
48                ),
49                gradient_button(
50                    "Ocean",
51                    Color::rgb(120, 200, 255),
52                    Color::rgb(40, 90, 200),
53                    tokens::RADIUS_MD,
54                ),
55                gradient_button(
56                    "Forest",
57                    Color::rgb(180, 230, 140),
58                    Color::rgb(40, 110, 80),
59                    tokens::RADIUS_MD,
60                ),
61                spacer(),
62                button("Stock").secondary(),
63            ])
64            .gap(tokens::SPACE_3)],
65        ),
66    ])
67    .gap(tokens::SPACE_4)
68    .padding(tokens::SPACE_7)
69}
examples/palette_demo.rs (line 323)
314fn component_section() -> El {
315    card([
316        card_header([
317            card_title("Stock widgets"),
318            card_description("The same palette applied to regular component constructors."),
319        ]),
320        card_content([
321            row([
322                button("Primary").primary(),
323                button("Secondary").secondary(),
324                button("Outline").outline(),
325                button("Ghost").ghost(),
326            ])
327            .gap(tokens::SPACE_2)
328            .align(Align::Center),
329            row([
330                badge("success").success(),
331                badge("warning").warning(),
332                badge("destructive").destructive(),
333                badge("info").info(),
334                badge("muted").muted(),
335            ])
336            .gap(tokens::SPACE_2)
337            .align(Align::Center),
338            row([
339                text_input("palette search", &Selection::default(), "palette:search")
340                    .width(Size::Fill(1.0)),
341                button_with_icon("settings", "Tune").secondary(),
342            ])
343            .gap(tokens::SPACE_2)
344            .align(Align::Center),
345            row([
346                surface_sample("Card", tokens::CARD),
347                surface_sample("Muted", tokens::MUTED),
348                surface_sample("Popover", tokens::POPOVER),
349            ])
350            .gap(tokens::SPACE_3)
351            .align(Align::Stretch),
352        ])
353        .gap(tokens::SPACE_4),
354    ])
355    .height(Size::Hug)
356}
examples/settings.rs (line 31)
16fn settings() -> El {
17    column([
18        h1("Settings"),
19        titled_card(
20            "Account",
21            [
22                row([text("Email"), spacer(), text("user@example.com").muted()]),
23                row([
24                    text("Two-factor authentication"),
25                    spacer(),
26                    badge("Enabled").success(),
27                ]),
28                row([
29                    text("Recovery codes"),
30                    spacer(),
31                    button("Generate").secondary(),
32                ]),
33            ],
34        ),
35        titled_card(
36            "Appearance",
37            [
38                row([text("Theme"), spacer(), button("Dark").secondary()]),
39                row([text("Compact mode"), spacer(), badge("Off").muted()]),
40                row([text("Font size"), spacer(), text("14")]),
41            ],
42        ),
43        titled_card(
44            "Danger zone",
45            [row([
46                column([
47                    text("Delete account").bold(),
48                    text("Permanently remove your account and all data.")
49                        .muted()
50                        .small(),
51                ])
52                .gap(tokens::SPACE_1)
53                .align(Align::Start)
54                .width(Size::Hug),
55                spacer(),
56                button("Delete").destructive(),
57            ])],
58        ),
59        row([spacer(), button("Cancel").ghost(), button("Save").primary()]),
60    ])
61    .gap(tokens::SPACE_4)
62    .padding(tokens::SPACE_7)
63}
Source

pub fn ghost(self) -> Self

No fill, no border. Low-emphasis actions like “Cancel” alongside a primary “Save”.

Examples found in repository?
examples/settings_calibration.rs (line 99)
97fn settings_header() -> El {
98    row([
99        icon_button("menu").ghost(),
100        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
101        h3("Settings").key("metric:page.title"),
102        spacer(),
103        button("Reset").secondary(),
104        button("Save changes").primary(),
105    ])
106    .key("metric:header")
107    .gap(tokens::SPACE_3)
108    .height(Size::Fixed(56.0))
109    .padding(Sides::xy(tokens::SPACE_4, 0.0))
110    .align(Align::Center)
111    .stroke(tokens::BORDER)
112}
More examples
Hide additional examples
examples/dashboard_01_calibration.rs (line 196)
194fn dashboard_header() -> El {
195    row([
196        icon_button("menu").ghost(),
197        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
198        h3("Documents").key("metric:page.title"),
199        spacer(),
200        text_input("Search...", &Selection::default(), "dashboard-search")
201            .key("metric:command.input")
202            .width(Size::Fixed(260.0)),
203        icon_button("plus").ghost(),
204        icon_button("bell").ghost(),
205    ])
206    .key("metric:header")
207    .gap(tokens::SPACE_3)
208    .height(Size::Fixed(56.0))
209    .padding(Sides::xy(tokens::SPACE_4, 0.0))
210    .align(Align::Center)
211    .stroke(tokens::BORDER)
212}
examples/modal.rs (line 46)
9fn modal_fixture() -> El {
10    stack([
11        column([
12            h1("Account"),
13            titled_card(
14                "Profile",
15                [
16                    row([text("Email"), spacer(), text("user@example.com").muted()]),
17                    row([text("Plan"), spacer(), badge("Pro").info()]),
18                ],
19            ),
20            titled_card(
21                "Danger zone",
22                [row([
23                    column([
24                        text("Delete account").bold(),
25                        text("Remove this account and all associated data.")
26                            .muted()
27                            .small(),
28                    ])
29                    .gap(tokens::SPACE_1)
30                    .align(Align::Start)
31                    .width(Size::Hug),
32                    spacer(),
33                    button("Delete").destructive().key("open-delete"),
34                ])],
35            ),
36        ])
37        .gap(tokens::SPACE_4)
38        .padding(tokens::SPACE_7),
39        modal(
40            "delete-account",
41            "Delete account?",
42            [
43                text("Permanent action. Export data first.").muted(),
44                row([
45                    spacer(),
46                    button("Cancel").ghost().key("cancel-delete"),
47                    button("Delete").destructive().key("confirm-delete"),
48                ])
49                .gap(tokens::SPACE_2),
50            ],
51        ),
52    ])
53}
examples/palette_demo.rs (line 325)
314fn component_section() -> El {
315    card([
316        card_header([
317            card_title("Stock widgets"),
318            card_description("The same palette applied to regular component constructors."),
319        ]),
320        card_content([
321            row([
322                button("Primary").primary(),
323                button("Secondary").secondary(),
324                button("Outline").outline(),
325                button("Ghost").ghost(),
326            ])
327            .gap(tokens::SPACE_2)
328            .align(Align::Center),
329            row([
330                badge("success").success(),
331                badge("warning").warning(),
332                badge("destructive").destructive(),
333                badge("info").info(),
334                badge("muted").muted(),
335            ])
336            .gap(tokens::SPACE_2)
337            .align(Align::Center),
338            row([
339                text_input("palette search", &Selection::default(), "palette:search")
340                    .width(Size::Fill(1.0)),
341                button_with_icon("settings", "Tune").secondary(),
342            ])
343            .gap(tokens::SPACE_2)
344            .align(Align::Center),
345            row([
346                surface_sample("Card", tokens::CARD),
347                surface_sample("Muted", tokens::MUTED),
348                surface_sample("Popover", tokens::POPOVER),
349            ])
350            .gap(tokens::SPACE_3)
351            .align(Align::Stretch),
352        ])
353        .gap(tokens::SPACE_4),
354    ])
355    .height(Size::Hug)
356}
examples/settings.rs (line 59)
16fn settings() -> El {
17    column([
18        h1("Settings"),
19        titled_card(
20            "Account",
21            [
22                row([text("Email"), spacer(), text("user@example.com").muted()]),
23                row([
24                    text("Two-factor authentication"),
25                    spacer(),
26                    badge("Enabled").success(),
27                ]),
28                row([
29                    text("Recovery codes"),
30                    spacer(),
31                    button("Generate").secondary(),
32                ]),
33            ],
34        ),
35        titled_card(
36            "Appearance",
37            [
38                row([text("Theme"), spacer(), button("Dark").secondary()]),
39                row([text("Compact mode"), spacer(), badge("Off").muted()]),
40                row([text("Font size"), spacer(), text("14")]),
41            ],
42        ),
43        titled_card(
44            "Danger zone",
45            [row([
46                column([
47                    text("Delete account").bold(),
48                    text("Permanently remove your account and all data.")
49                        .muted()
50                        .small(),
51                ])
52                .gap(tokens::SPACE_1)
53                .align(Align::Start)
54                .width(Size::Hug),
55                spacer(),
56                button("Delete").destructive(),
57            ])],
58        ),
59        row([spacer(), button("Cancel").ghost(), button("Save").primary()]),
60    ])
61    .gap(tokens::SPACE_4)
62    .padding(tokens::SPACE_7)
63}
Source

pub fn outline(self) -> Self

Outline-only style: no fill, prominent border.

Examples found in repository?
examples/palette_demo.rs (line 324)
314fn component_section() -> El {
315    card([
316        card_header([
317            card_title("Stock widgets"),
318            card_description("The same palette applied to regular component constructors."),
319        ]),
320        card_content([
321            row([
322                button("Primary").primary(),
323                button("Secondary").secondary(),
324                button("Outline").outline(),
325                button("Ghost").ghost(),
326            ])
327            .gap(tokens::SPACE_2)
328            .align(Align::Center),
329            row([
330                badge("success").success(),
331                badge("warning").warning(),
332                badge("destructive").destructive(),
333                badge("info").info(),
334                badge("muted").muted(),
335            ])
336            .gap(tokens::SPACE_2)
337            .align(Align::Center),
338            row([
339                text_input("palette search", &Selection::default(), "palette:search")
340                    .width(Size::Fill(1.0)),
341                button_with_icon("settings", "Tune").secondary(),
342            ])
343            .gap(tokens::SPACE_2)
344            .align(Align::Center),
345            row([
346                surface_sample("Card", tokens::CARD),
347                surface_sample("Muted", tokens::MUTED),
348                surface_sample("Popover", tokens::POPOVER),
349            ])
350            .gap(tokens::SPACE_3)
351            .align(Align::Stretch),
352        ])
353        .gap(tokens::SPACE_4),
354    ])
355    .height(Size::Hug)
356}
Source

pub fn muted(self) -> Self

Muted/neutral emphasis. On surface profiles this swaps to a neutral background; on text-only profiles it switches the text color to muted-foreground.

Examples found in repository?
examples/scrollbar.rs (line 24)
20fn list_rows() -> Vec<El> {
21    (0..40)
22        .map(|i| {
23            row([
24                text(format!("{i:02}.")).mono().muted(),
25                text(format!("scrollable list item {i}")),
26            ])
27            .gap(tokens::SPACE_2)
28            .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_1))
29            .height(Size::Fixed(28.0))
30            .align(Align::Center)
31        })
32        .collect()
33}
34
35fn fixture() -> El {
36    column([
37        h2("Scrollbar"),
38        text("scroll() and virtual_list() show a draggable thumb by default.").muted(),
39        row([
40            // 1) scroll() — default-on scrollbar.
41            column([
42                text("scroll() — default").bold(),
43                scroll(list_rows())
44                    .height(Size::Fixed(240.0))
45                    .padding(tokens::SPACE_2)
46                    .stroke(tokens::BORDER)
47                    .stroke_width(1.0)
48                    .radius(tokens::RADIUS_MD),
49            ])
50            .gap(tokens::SPACE_2)
51            .width(Size::Fill(1.0))
52            .height(Size::Hug),
53            // 2) virtual_list — thumb scales to content size.
54            column([
55                text("virtual_list(200, 28)").bold(),
56                virtual_list(200, 28.0, |i| {
57                    row([
58                        text(format!("{i:03}")).mono().muted(),
59                        text(format!("row {i}")),
60                    ])
61                    .gap(tokens::SPACE_2)
62                    .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_1))
63                    .height(Size::Fixed(28.0))
64                    .align(Align::Center)
65                })
66                .height(Size::Fixed(240.0))
67                .padding(tokens::SPACE_2)
68                .stroke(tokens::BORDER)
69                .stroke_width(1.0)
70                .radius(tokens::RADIUS_MD),
71            ])
72            .gap(tokens::SPACE_2)
73            .width(Size::Fill(1.0))
74            .height(Size::Hug),
75            // 3) Opt-out: same content, no thumb.
76            column([
77                text("scroll().no_scrollbar()").bold(),
78                scroll(list_rows())
79                    .no_scrollbar()
80                    .height(Size::Fixed(240.0))
81                    .padding(tokens::SPACE_2)
82                    .stroke(tokens::BORDER)
83                    .stroke_width(1.0)
84                    .radius(tokens::RADIUS_MD),
85            ])
86            .gap(tokens::SPACE_2)
87            .width(Size::Fill(1.0))
88            .height(Size::Hug),
89        ])
90        .gap(tokens::SPACE_4)
91        .width(Size::Fill(1.0)),
92    ])
93    .gap(tokens::SPACE_4)
94    .padding(tokens::SPACE_7)
95}
More examples
Hide additional examples
examples/custom_paint.rs (line 71)
68fn build_row(c: &FakeCommit, idx: usize, selected: bool) -> El {
69    row([
70        graph_cell(c.lane, selected),
71        text(c.sha).mono().muted(),
72        text(c.subject),
73        spacer(),
74        text(format!("{} · {}", c.author, c.when)).muted(),
75    ])
76    .key(format!("commit-{idx}"))
77    .gap(tokens::SPACE_3)
78    .padding(Sides::xy(tokens::SPACE_2, 0.0))
79    .height(Size::Fixed(ROW_HEIGHT))
80    .align(Align::Center)
81}
82
83fn fixture() -> El {
84    #[rustfmt::skip]
85    let commits = [
86        FakeCommit { sha: "8a3f1c9", subject: "fix race condition in scheduler", author: "ada",     when: "12m", lane: 0 },
87        FakeCommit { sha: "1b07d4e", subject: "tweak token tooltip wording",     author: "linus",   when: "1h",  lane: 0 },
88        FakeCommit { sha: "9f2e4a1", subject: "wire avatar fallback identicon",  author: "joelle",  when: "3h",  lane: 1 },
89        FakeCommit { sha: "44ab8d2", subject: "diff: word-level highlight pass", author: "raphael", when: "5h",  lane: 1 },
90        FakeCommit { sha: "61c0fe7", subject: "ci: bump rust toolchain to 1.85", author: "mei",     when: "7h",  lane: 2 },
91        FakeCommit { sha: "a90215b", subject: "switch logging to env_logger",    author: "isabel",  when: "1d",  lane: 2 },
92        FakeCommit { sha: "0d7e3c4", subject: "drop unused commit_detail cache", author: "noor",    when: "1d",  lane: 1 },
93        FakeCommit { sha: "33b2118", subject: "context-menu spacing pass",       author: "kira",    when: "2d",  lane: 3 },
94    ];
95    let selected_idx = 3;
96    let rows = commits
97        .iter()
98        .enumerate()
99        .map(|(i, c)| build_row(c, i, i == selected_idx))
100        .collect::<Vec<_>>();
101
102    column([
103        h2("Custom-painted commit graph"),
104        text("8 commits · custom shader paints lane line + circle node").muted(),
105        column(rows).gap(0.0),
106    ])
107    .padding(tokens::SPACE_4)
108    .gap(tokens::SPACE_2)
109}
examples/virtual_list.rs (line 20)
18fn build_row(i: usize) -> El {
19    let badge_el = match i % 5 {
20        0 => badge("info").muted(),
21        1 => badge("warn").warning(),
22        2 => badge("ok").success(),
23        3 => badge("err").destructive(),
24        _ => spacer(),
25    };
26    row([
27        text(format!("#{i:05}")).mono(),
28        spacer(),
29        text(format!("entry {i}")),
30        spacer(),
31        badge_el,
32    ])
33    .key(format!("row-{i}"))
34    .gap(tokens::SPACE_3)
35    .padding(Sides::xy(tokens::SPACE_3, tokens::SPACE_2))
36    .height(Size::Fixed(ROW_HEIGHT))
37}
38
39fn fixture() -> El {
40    column([
41        h1("Virtualized list"),
42        paragraph(format!(
43            "{ROW_COUNT} rows × {ROW_HEIGHT}px in a windowed viewport. \
44             Only the rows intersecting the viewport are realized — see \
45             `tree.txt` for proof."
46        ))
47        .muted(),
48        virtual_list(ROW_COUNT, ROW_HEIGHT, build_row)
49            .key("entries")
50            .height(Size::Fill(1.0)),
51    ])
52    .gap(tokens::SPACE_4)
53    .padding(tokens::SPACE_7)
54}
examples/settings_calibration.rs (line 169)
164fn settings_body() -> El {
165    column([
166        column([
167            h1("Account").heading().key("metric:section.title"),
168            text("Manage identity, workspace defaults, and security preferences.")
169                .muted()
170                .wrap_text()
171                .key("metric:page.subtitle"),
172        ])
173        .gap(tokens::SPACE_1)
174        .height(Size::Hug),
175        scroll([profile_card(), preferences_card()])
176            .key("settings-body-scroll")
177            .gap(tokens::SPACE_4)
178            .width(Size::Fill(1.0))
179            .height(Size::Fill(1.0)),
180    ])
181    .gap(tokens::SPACE_4)
182    .width(Size::Fill(1.0))
183    .height(Size::Fill(1.0))
184}
examples/polish_calibration.rs (line 46)
44fn sidebar() -> El {
45    column([
46        column([h2("Aetna"), text("calibration").muted()])
47            .key("metric:sidebar.brand")
48            .gap(tokens::SPACE_1)
49            .height(Size::Hug),
50        spacer().height(Size::Fixed(tokens::SPACE_4)),
51        nav_item("01", "Overview", true),
52        nav_item("02", "Commands", false),
53        nav_item("03", "Tables", false),
54        nav_item("04", "Forms", false),
55        spacer(),
56        badge("dark theme").muted(),
57    ])
58    .gap(tokens::SPACE_2)
59    .padding(tokens::SPACE_5)
60    .key("metric:sidebar")
61    .width(Size::Fixed(220.0))
62    .height(Size::Fill(1.0))
63    .fill(tokens::CARD)
64    .stroke(tokens::BORDER)
65}
66
67fn nav_item(icon: &'static str, label: &'static str, selected: bool) -> El {
68    let mut item = row([
69        icon_cell(icon),
70        text(label)
71            .font_weight(FontWeight::Medium)
72            .ellipsis()
73            .width(Size::Fill(1.0)),
74    ])
75    .key(if selected {
76        "metric:sidebar.nav.row".to_string()
77    } else {
78        format!("nav-{label}")
79    })
80    .metrics_role(MetricsRole::ListItem)
81    .gap(tokens::SPACE_3)
82    .padding(Sides::xy(tokens::SPACE_2, 0.0))
83    .height(Size::Fixed(40.0))
84    .align(Align::Center)
85    .focusable();
86
87    if selected {
88        item = item.current();
89    }
90
91    item
92}
93
94fn main_panel() -> El {
95    column([
96        toolbar(),
97        column([
98            row([
99                kpi_card("Latency", "42 ms", "-18%", true),
100                kpi_card("Runs", "1,284", "+12%", true),
101                kpi_card("Errors", "7", "+2", false),
102            ])
103            .gap(tokens::SPACE_4),
104            row([table_card(), command_card()])
105                .gap(tokens::SPACE_4)
106                .height(Size::Fill(1.0))
107                .align(Align::Stretch),
108        ])
109        .gap(tokens::SPACE_4)
110        .height(Size::Fill(1.0))
111        .align(Align::Stretch),
112    ])
113    .padding(tokens::SPACE_7)
114    .gap(tokens::SPACE_2)
115    .width(Size::Fill(1.0))
116    .height(Size::Fill(1.0))
117}
118
119fn toolbar() -> El {
120    row([
121        column([
122            h1("Polish calibration").key("metric:page.title"),
123            text("A representative app surface for default tuning.")
124                .muted()
125                .key("metric:page.subtitle"),
126        ])
127        .gap(tokens::SPACE_2)
128        .height(Size::Hug),
129        spacer(),
130        button_with_icon("search", "Preview")
131            .secondary()
132            .key("metric:action.secondary"),
133        button_with_icon("upload", "Publish")
134            .primary()
135            .key("metric:action.primary"),
136    ])
137    .key("metric:header")
138    .gap(tokens::SPACE_4)
139    .height(Size::Hug)
140    .align(Align::Start)
141}
142
143fn kpi_card(label: &'static str, value: &'static str, delta: &'static str, positive: bool) -> El {
144    let delta_badge = if positive {
145        badge(delta).success()
146    } else {
147        badge(delta).destructive()
148    };
149    let delta_badge = if label == "Latency" {
150        delta_badge.key("metric:kpi.badge")
151    } else {
152        delta_badge
153    };
154    let value_text = h2(value).display();
155    let value_text = if label == "Latency" {
156        value_text.key("metric:kpi.value")
157    } else {
158        value_text
159    };
160    card([
161        card_header([card_title(label)]),
162        card_content([
163            row([value_text, spacer(), delta_badge]).align(Align::Center),
164            text(if positive {
165                "Moving in the expected direction"
166            } else {
167                "Needs visual attention"
168            })
169            .muted(),
170        ])
171        .gap(tokens::SPACE_6),
172    ])
173    .key(if label == "Latency" {
174        "metric:kpi.card"
175    } else {
176        label
177    })
178    .width(Size::Fill(1.0))
179}
180
181fn table_card() -> El {
182    card([
183        card_header([card_title("Reference rows")]),
184        card_content([table([
185            table_header([table_row([
186                table_head("Status").width(Size::Fixed(86.0)),
187                table_head("Surface").width(Size::Fill(1.0)),
188                table_head("Owner").width(Size::Fixed(110.0)),
189                table_head("State").width(Size::Fixed(86.0)),
190            ])
191            .key("metric:table.header")]),
192            divider(),
193            table_body([
194                data_row("OK", "Settings card", "core", "selected", true, "success"),
195                data_row(
196                    "WARN",
197                    "Command palette density",
198                    "widgets",
199                    "needs work",
200                    false,
201                    "warning",
202                ),
203                data_row(
204                    "ERR",
205                    "Disabled and invalid states",
206                    "style",
207                    "missing",
208                    false,
209                    "destructive",
210                ),
211                data_row(
212                    "INFO",
213                    "Token resolution",
214                    "theme",
215                    "planned",
216                    false,
217                    "info",
218                ),
219                data_row(
220                    "OK",
221                    "Popover elevation",
222                    "shader",
223                    "queued",
224                    false,
225                    "success",
226                ),
227            ])
228            .gap(tokens::SPACE_1)
229            .width(Size::Fill(1.0)),
230        ])]),
231    ])
232    .key("metric:table.card")
233    .width(Size::Fill(1.2))
234    .height(Size::Fill(1.0))
235}
236
237fn data_row(
238    status: &'static str,
239    title: &'static str,
240    owner: &'static str,
241    state: &'static str,
242    selected: bool,
243    tone: &'static str,
244) -> El {
245    let status_badge = match tone {
246        "success" => badge(status).success(),
247        "warning" => badge(status).warning(),
248        "destructive" => badge(status).destructive(),
249        _ => badge(status).info(),
250    };
251    let status_badge = if selected {
252        status_badge.key("metric:table.badge")
253    } else {
254        status_badge
255    };
256
257    let mut row = table_row([
258        table_cell(status_badge).width(Size::Fixed(70.0)),
259        column([
260            text(title)
261                .font_weight(FontWeight::Medium)
262                .ellipsis()
263                .width(Size::Fill(1.0)),
264            text("Default styling probe.")
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0)),
271        table_cell(text(owner).muted()).width(Size::Fixed(110.0)),
272        table_cell(text(state).label().small()).width(Size::Fixed(86.0)),
273    ])
274    .key(if selected {
275        "metric:table.row".to_string()
276    } else {
277        format!("row-{title}")
278    })
279    .focusable();
280
281    if selected {
282        row = row.selected();
283    }
284
285    row
286}
examples/scroll_list.rs (line 22)
15fn scroll_list_fixture() -> El {
16    let rows: Vec<El> = (0..20)
17        .map(|i| {
18            row([
19                badge(format!("#{i}")).info(),
20                text(format!("Notification {i}")).bold(),
21                spacer(),
22                text(format!("{}m ago", i + 1)).muted(),
23            ])
24            .gap(tokens::SPACE_2)
25            .height(Size::Fixed(44.0))
26            .padding(Sides::xy(tokens::SPACE_3, tokens::SPACE_2))
27        })
28        .collect();
29
30    let list = scroll(rows)
31        .key("notifications")
32        .height(Size::Fixed(420.0))
33        .padding(tokens::SPACE_2);
34
35    column([
36        h2("Notifications"),
37        text("Roll the wheel inside the panel to scroll. The content is taller than the viewport.")
38            .muted(),
39        list,
40    ])
41    .gap(tokens::SPACE_4)
42    .padding(tokens::SPACE_7)
43}
Source

pub fn selected(self) -> Self

Selected row/item treatment. Use for the item that is selected inside a collection, not for transient keyboard focus.

Examples found in repository?
examples/polish_calibration.rs (line 282)
237fn data_row(
238    status: &'static str,
239    title: &'static str,
240    owner: &'static str,
241    state: &'static str,
242    selected: bool,
243    tone: &'static str,
244) -> El {
245    let status_badge = match tone {
246        "success" => badge(status).success(),
247        "warning" => badge(status).warning(),
248        "destructive" => badge(status).destructive(),
249        _ => badge(status).info(),
250    };
251    let status_badge = if selected {
252        status_badge.key("metric:table.badge")
253    } else {
254        status_badge
255    };
256
257    let mut row = table_row([
258        table_cell(status_badge).width(Size::Fixed(70.0)),
259        column([
260            text(title)
261                .font_weight(FontWeight::Medium)
262                .ellipsis()
263                .width(Size::Fill(1.0)),
264            text("Default styling probe.")
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0)),
271        table_cell(text(owner).muted()).width(Size::Fixed(110.0)),
272        table_cell(text(state).label().small()).width(Size::Fixed(86.0)),
273    ])
274    .key(if selected {
275        "metric:table.row".to_string()
276    } else {
277        format!("row-{title}")
278    })
279    .focusable();
280
281    if selected {
282        row = row.selected();
283    }
284
285    row
286}
Source

pub fn current(self) -> Self

Current navigation/page treatment. Slightly quieter than Self::selected so nav chrome does not compete with content.

Examples found in repository?
examples/polish_calibration.rs (line 88)
67fn nav_item(icon: &'static str, label: &'static str, selected: bool) -> El {
68    let mut item = row([
69        icon_cell(icon),
70        text(label)
71            .font_weight(FontWeight::Medium)
72            .ellipsis()
73            .width(Size::Fill(1.0)),
74    ])
75    .key(if selected {
76        "metric:sidebar.nav.row".to_string()
77    } else {
78        format!("nav-{label}")
79    })
80    .metrics_role(MetricsRole::ListItem)
81    .gap(tokens::SPACE_3)
82    .padding(Sides::xy(tokens::SPACE_2, 0.0))
83    .height(Size::Fixed(40.0))
84    .align(Align::Center)
85    .focusable();
86
87    if selected {
88        item = item.current();
89    }
90
91    item
92}
More examples
Hide additional examples
examples/settings_calibration.rs (line 156)
134fn settings_nav_item(label: &'static str, selected: bool) -> El {
135    let mut item = row([
136        El::new(Kind::Custom("nav-dot"))
137            .fill(tokens::MUTED_FOREGROUND)
138            .radius(tokens::RADIUS_PILL)
139            .width(Size::Fixed(6.0))
140            .height(Size::Fixed(6.0)),
141        text(label)
142            .font_weight(FontWeight::Medium)
143            .ellipsis()
144            .width(Size::Fill(1.0)),
145    ])
146    .key(if selected {
147        "metric:settings.nav.row".to_string()
148    } else {
149        format!("settings-nav-{label}")
150    })
151    .metrics_role(MetricsRole::ListItem)
152    .align(Align::Center)
153    .focusable();
154
155    if selected {
156        item = item.current();
157    } else {
158        item = item.color(tokens::MUTED_FOREGROUND);
159    }
160
161    item
162}
163
164fn settings_body() -> El {
165    column([
166        column([
167            h1("Account").heading().key("metric:section.title"),
168            text("Manage identity, workspace defaults, and security preferences.")
169                .muted()
170                .wrap_text()
171                .key("metric:page.subtitle"),
172        ])
173        .gap(tokens::SPACE_1)
174        .height(Size::Hug),
175        scroll([profile_card(), preferences_card()])
176            .key("settings-body-scroll")
177            .gap(tokens::SPACE_4)
178            .width(Size::Fill(1.0))
179            .height(Size::Fill(1.0)),
180    ])
181    .gap(tokens::SPACE_4)
182    .width(Size::Fill(1.0))
183    .height(Size::Fill(1.0))
184}
185
186fn profile_card() -> El {
187    card([
188        card_header([
189            card_title("Profile"),
190            card_description("This information appears in audit logs and shared documents."),
191        ]),
192        card_content([form([
193            row([
194                setting_field("Display name", "Alicia Koch", "display-name"),
195                setting_field("Email", "alicia@acme.co", "email"),
196            ])
197            .gap(tokens::SPACE_3),
198            row([
199                setting_select("Role", "Workspace admin", "role"),
200                setting_select("Region", "US East", "region"),
201            ])
202            .gap(tokens::SPACE_3),
203        ])]),
204    ])
205    .key("metric:profile.card")
206}
207
208fn setting_field(label: &'static str, value: &'static str, key: &'static str) -> El {
209    form_item([
210        form_label(label),
211        form_control(
212            text_input(value, &Selection::caret(key, value.len()), key).key(
213                if key == "display-name" {
214                    "metric:form.input"
215                } else {
216                    key
217                },
218            ),
219        ),
220    ])
221    .width(Size::Fill(1.0))
222}
223
224fn setting_select(label: &'static str, value: &'static str, key: &'static str) -> El {
225    form_item([form_label(label), form_control(select_trigger(key, value))]).width(Size::Fill(1.0))
226}
227
228fn preferences_card() -> El {
229    card([
230        card_header([
231            card_title("Preferences"),
232            card_description("Defaults used when creating new dashboards and exports."),
233        ]),
234        card_content([column([
235            preference_row(
236                "Compact navigation",
237                "Use tighter rows in the sidebar and command menus.",
238                switch(true).key("compact-navigation"),
239            ),
240            divider(),
241            preference_row(
242                "Email summaries",
243                "Send a daily digest when documents change.",
244                switch(false).key("email-summaries"),
245            ),
246            divider(),
247            preference_row(
248                "Require approval",
249                "Route external sharing through an owner review.",
250                checkbox(true).key("approval-required"),
251            ),
252        ])
253        .gap(0.0)
254        .width(Size::Fill(1.0))])
255        .padding(0.0),
256    ])
257    .key("metric:preferences.card")
258}
259
260fn preference_row(title: &'static str, description: &'static str, control: El) -> El {
261    row([
262        column([
263            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
264            text(description)
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0))
271        .height(Size::Hug),
272        control,
273    ])
274    .key(if title == "Compact navigation" {
275        "metric:preference.row".to_string()
276    } else {
277        format!("preference-{title}")
278    })
279    .metrics_role(MetricsRole::PreferenceRow)
280    .gap(tokens::SPACE_4)
281    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_3))
282    .align(Align::Center)
283}
284
285fn settings_aside() -> El {
286    column([security_card(), scale_card()])
287        .gap(tokens::SPACE_4)
288        .width(Size::Fixed(300.0))
289        .height(Size::Fill(1.0))
290}
291
292fn security_card() -> El {
293    card([
294        card_header([
295            card_title("Security"),
296            card_description("Two-factor authentication is enabled for all privileged users."),
297        ]),
298        card_content([
299            compact_stat("Passkeys", "2 registered", badge("On").success()),
300            compact_stat("Sessions", "3 active", button("Review").secondary()),
301        ]),
302    ])
303    .width(Size::Fill(1.0))
304}
305
306fn scale_card() -> El {
307    card([
308        card_header([
309            card_title("Interface scale"),
310            card_description("Reference captures keep browser zoom fixed and vary root UI scale."),
311        ]),
312        card_content([
313            row([text("Dense").caption(), spacer(), text("Default").caption()]),
314            slider(0.66, tokens::PRIMARY)
315                .key("interface-scale")
316                .width(Size::Fill(1.0)),
317        ]),
318    ])
319    .width(Size::Fill(1.0))
320}
321
322fn compact_stat(title: &'static str, detail: &'static str, control: El) -> El {
323    row([
324        column([
325            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
326            text(detail).caption().ellipsis().width(Size::Fill(1.0)),
327        ])
328        .gap(2.0)
329        .width(Size::Fill(1.0))
330        .height(Size::Hug),
331        control,
332    ])
333    .gap(tokens::SPACE_2)
334    .height(Size::Fixed(44.0))
335    .align(Align::Center)
336}
337
338fn section_label(label: &'static str) -> El {
339    text(label)
340        .caption()
341        .height(Size::Fixed(22.0))
342        .padding(Sides::xy(tokens::SPACE_2, 0.0))
343}
344
345fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
346    let mut item = row([
347        icon(icon_name)
348            .color(tokens::MUTED_FOREGROUND)
349            .icon_size(tokens::ICON_SM)
350            .width(Size::Fixed(tokens::ICON_SM)),
351        text(label)
352            .font_weight(FontWeight::Medium)
353            .ellipsis()
354            .width(Size::Fill(1.0)),
355    ])
356    .key(if selected {
357        "metric:sidebar.nav.row".to_string()
358    } else {
359        format!("side-item-{label}")
360    })
361    .metrics_role(MetricsRole::ListItem)
362    .gap(tokens::SPACE_2)
363    .padding(Sides::xy(tokens::SPACE_2, 0.0))
364    .height(Size::Fixed(32.0))
365    .align(Align::Center)
366    .focusable();
367
368    if selected {
369        item = item.current();
370    } else {
371        item = item.color(tokens::MUTED_FOREGROUND);
372    }
373
374    item
375}
examples/dashboard_01_calibration.rs (line 133)
109fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
110    let mut item = row([
111        icon(icon_name)
112            .color(tokens::MUTED_FOREGROUND)
113            .icon_size(tokens::ICON_SM)
114            .width(Size::Fixed(tokens::ICON_SM)),
115        text(label)
116            .font_weight(FontWeight::Medium)
117            .ellipsis()
118            .width(Size::Fill(1.0)),
119    ])
120    .key(if selected {
121        "metric:sidebar.nav.row".to_string()
122    } else {
123        format!("side-item-{label}")
124    })
125    .metrics_role(MetricsRole::ListItem)
126    .gap(tokens::SPACE_2)
127    .padding(Sides::xy(tokens::SPACE_2, 0.0))
128    .height(Size::Fixed(32.0))
129    .align(Align::Center)
130    .focusable();
131
132    if selected {
133        item = item.current();
134    } else {
135        item = item.color(tokens::MUTED_FOREGROUND);
136    }
137
138    item
139}
Source

pub fn disabled(self) -> Self

Disabled treatment for controls and rows. Also removes the node from focus order and blocks pointer hits on this element.

Examples found in repository?
examples/polish_calibration.rs (line 344)
317fn form_probe() -> El {
318    form([
319        form_item([
320            form_label("Valid input"),
321            form_control(
322                text_input(
323                    "Valid input",
324                    &Selection::caret("valid-input", 11),
325                    "valid-input",
326                )
327                .key("metric:form.input"),
328            ),
329            form_description("Default field spacing and helper text."),
330        ]),
331        form_item([
332            form_label("Invalid input"),
333            form_control(
334                text_input(
335                    "Invalid input",
336                    &Selection::caret("invalid-input", 13),
337                    "invalid-input",
338                )
339                .invalid(),
340            ),
341            form_message("This field needs attention."),
342        ]),
343        row([
344            button("Disabled").secondary().disabled(),
345            button("Loading").primary().loading(),
346            spacer(),
347        ]),
348    ])
349    .padding(tokens::SPACE_3)
350    .fill(tokens::MUTED)
351    .stroke(tokens::BORDER)
352    .radius(tokens::RADIUS_MD)
353}
Source

pub fn invalid(self) -> Self

Invalid/error treatment for inputs, rows, and validation badges.

Examples found in repository?
examples/polish_calibration.rs (line 339)
317fn form_probe() -> El {
318    form([
319        form_item([
320            form_label("Valid input"),
321            form_control(
322                text_input(
323                    "Valid input",
324                    &Selection::caret("valid-input", 11),
325                    "valid-input",
326                )
327                .key("metric:form.input"),
328            ),
329            form_description("Default field spacing and helper text."),
330        ]),
331        form_item([
332            form_label("Invalid input"),
333            form_control(
334                text_input(
335                    "Invalid input",
336                    &Selection::caret("invalid-input", 13),
337                    "invalid-input",
338                )
339                .invalid(),
340            ),
341            form_message("This field needs attention."),
342        ]),
343        row([
344            button("Disabled").secondary().disabled(),
345            button("Loading").primary().loading(),
346            spacer(),
347        ]),
348    ])
349    .padding(tokens::SPACE_3)
350    .fill(tokens::MUTED)
351    .stroke(tokens::BORDER)
352    .radius(tokens::RADIUS_MD)
353}
Source

pub fn loading(self) -> Self

Loading treatment for a direct text-bearing node. Container widgets can still use this for opacity even when they do not have their own label text.

Examples found in repository?
examples/polish_calibration.rs (line 345)
317fn form_probe() -> El {
318    form([
319        form_item([
320            form_label("Valid input"),
321            form_control(
322                text_input(
323                    "Valid input",
324                    &Selection::caret("valid-input", 11),
325                    "valid-input",
326                )
327                .key("metric:form.input"),
328            ),
329            form_description("Default field spacing and helper text."),
330        ]),
331        form_item([
332            form_label("Invalid input"),
333            form_control(
334                text_input(
335                    "Invalid input",
336                    &Selection::caret("invalid-input", 13),
337                    "invalid-input",
338                )
339                .invalid(),
340            ),
341            form_message("This field needs attention."),
342        ]),
343        row([
344            button("Disabled").secondary().disabled(),
345            button("Loading").primary().loading(),
346            spacer(),
347        ]),
348    ])
349    .padding(tokens::SPACE_3)
350    .fill(tokens::MUTED)
351    .stroke(tokens::BORDER)
352    .radius(tokens::RADIUS_MD)
353}
Source

pub fn text_role(self, role: TextRole) -> Self

Source

pub fn caption(self) -> Self

Examples found in repository?
examples/palette_demo.rs (line 298)
282fn token_chip(token: TokenDef, palette: &Palette) -> El {
283    let resolved = palette.resolve(token.color);
284    row([
285        El::new(Kind::Custom("palette-swatch"))
286            .at(file!(), line!())
287            .fill(token.color)
288            .stroke(tokens::BORDER)
289            .radius(tokens::RADIUS_SM)
290            .width(Size::Fixed(42.0))
291            .height(Size::Fixed(34.0)),
292        column([
293            text(token.name)
294                .label()
295                .ellipsis()
296                .nowrap_text()
297                .width(Size::Fill(1.0)),
298            mono(rgba_label(resolved)).caption().muted(),
299        ])
300        .gap(0.0)
301        .width(Size::Fill(1.0))
302        .height(Size::Hug),
303    ])
304    .gap(tokens::SPACE_2)
305    .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_2))
306    .align(Align::Center)
307    .fill(tokens::CARD)
308    .stroke(tokens::BORDER)
309    .radius(tokens::RADIUS_MD)
310    .width(Size::Fill(1.0))
311    .height(Size::Fixed(54.0))
312}
313
314fn component_section() -> El {
315    card([
316        card_header([
317            card_title("Stock widgets"),
318            card_description("The same palette applied to regular component constructors."),
319        ]),
320        card_content([
321            row([
322                button("Primary").primary(),
323                button("Secondary").secondary(),
324                button("Outline").outline(),
325                button("Ghost").ghost(),
326            ])
327            .gap(tokens::SPACE_2)
328            .align(Align::Center),
329            row([
330                badge("success").success(),
331                badge("warning").warning(),
332                badge("destructive").destructive(),
333                badge("info").info(),
334                badge("muted").muted(),
335            ])
336            .gap(tokens::SPACE_2)
337            .align(Align::Center),
338            row([
339                text_input("palette search", &Selection::default(), "palette:search")
340                    .width(Size::Fill(1.0)),
341                button_with_icon("settings", "Tune").secondary(),
342            ])
343            .gap(tokens::SPACE_2)
344            .align(Align::Center),
345            row([
346                surface_sample("Card", tokens::CARD),
347                surface_sample("Muted", tokens::MUTED),
348                surface_sample("Popover", tokens::POPOVER),
349            ])
350            .gap(tokens::SPACE_3)
351            .align(Align::Stretch),
352        ])
353        .gap(tokens::SPACE_4),
354    ])
355    .height(Size::Hug)
356}
357
358fn surface_sample(title: &'static str, fill: Color) -> El {
359    column([
360        text(title).label(),
361        text("surface sample").caption().muted(),
362    ])
363    .gap(tokens::SPACE_1)
364    .padding(tokens::SPACE_3)
365    .fill(fill)
366    .stroke(tokens::BORDER)
367    .radius(tokens::RADIUS_MD)
368    .width(Size::Fill(1.0))
369    .height(Size::Fixed(76.0))
370}
More examples
Hide additional examples
examples/polish_calibration.rs (line 265)
237fn data_row(
238    status: &'static str,
239    title: &'static str,
240    owner: &'static str,
241    state: &'static str,
242    selected: bool,
243    tone: &'static str,
244) -> El {
245    let status_badge = match tone {
246        "success" => badge(status).success(),
247        "warning" => badge(status).warning(),
248        "destructive" => badge(status).destructive(),
249        _ => badge(status).info(),
250    };
251    let status_badge = if selected {
252        status_badge.key("metric:table.badge")
253    } else {
254        status_badge
255    };
256
257    let mut row = table_row([
258        table_cell(status_badge).width(Size::Fixed(70.0)),
259        column([
260            text(title)
261                .font_weight(FontWeight::Medium)
262                .ellipsis()
263                .width(Size::Fill(1.0)),
264            text("Default styling probe.")
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0)),
271        table_cell(text(owner).muted()).width(Size::Fixed(110.0)),
272        table_cell(text(state).label().small()).width(Size::Fixed(86.0)),
273    ])
274    .key(if selected {
275        "metric:table.row".to_string()
276    } else {
277        format!("row-{title}")
278    })
279    .focusable();
280
281    if selected {
282        row = row.selected();
283    }
284
285    row
286}
287
288fn command_card() -> El {
289    card([
290        card_header([card_title("Command surface")]),
291        card_content([
292            text_input(
293                "Search commands...",
294                &Selection::default(),
295                "command-search",
296            )
297            .key("metric:command.input")
298            .width(Size::Fill(1.0)),
299            popover_panel([
300                command_row("git-branch", "New branch", "Ctrl+B").key("metric:command.row"),
301                command_row("git-commit", "Commit staged files", "Ctrl+Enter")
302                    .key("command-row-commit"),
303                command_row("refresh-cw", "Refresh repository", "Ctrl+R")
304                    .key("command-row-refresh"),
305                command_row("alert-circle", "Force push", "Danger").key("command-row-force"),
306            ])
307            .width(Size::Fill(1.0)),
308            scroll([form_probe()]).key("form-probe-scroll"),
309        ])
310        .height(Size::Fill(1.0)),
311    ])
312    .key("metric:command.card")
313    .width(Size::Fill(0.8))
314    .height(Size::Fill(1.0))
315}
316
317fn form_probe() -> El {
318    form([
319        form_item([
320            form_label("Valid input"),
321            form_control(
322                text_input(
323                    "Valid input",
324                    &Selection::caret("valid-input", 11),
325                    "valid-input",
326                )
327                .key("metric:form.input"),
328            ),
329            form_description("Default field spacing and helper text."),
330        ]),
331        form_item([
332            form_label("Invalid input"),
333            form_control(
334                text_input(
335                    "Invalid input",
336                    &Selection::caret("invalid-input", 13),
337                    "invalid-input",
338                )
339                .invalid(),
340            ),
341            form_message("This field needs attention."),
342        ]),
343        row([
344            button("Disabled").secondary().disabled(),
345            button("Loading").primary().loading(),
346            spacer(),
347        ]),
348    ])
349    .padding(tokens::SPACE_3)
350    .fill(tokens::MUTED)
351    .stroke(tokens::BORDER)
352    .radius(tokens::RADIUS_MD)
353}
354
355fn icon_cell(label: &'static str) -> El {
356    El::new(Kind::Custom("icon_cell"))
357        .style_profile(StyleProfile::Surface)
358        .text(label)
359        .text_align(TextAlign::Center)
360        .caption()
361        .font_weight(FontWeight::Semibold)
362        .fill(tokens::MUTED)
363        .stroke(tokens::BORDER)
364        .radius(tokens::RADIUS_SM)
365        .width(Size::Fixed(26.0))
366        .height(Size::Fixed(26.0))
367}
examples/settings_calibration.rs (line 50)
41fn settings_sidebar() -> El {
42    column([
43        row([
44            icon_slot("settings"),
45            column([
46                text("Workspace")
47                    .semibold()
48                    .ellipsis()
49                    .width(Size::Fill(1.0)),
50                text("Settings").caption().ellipsis().width(Size::Fill(1.0)),
51            ])
52            .gap(2.0)
53            .width(Size::Fill(1.0))
54            .height(Size::Hug),
55        ])
56        .gap(tokens::SPACE_2)
57        .height(Size::Fixed(44.0))
58        .align(Align::Center),
59        section_label("Personal"),
60        side_item("users", "Profile", false),
61        side_item("settings", "Account", true),
62        side_item("alert-circle", "Security", false),
63        side_item("bell", "Notifications", false),
64        spacer().height(Size::Fixed(tokens::SPACE_4)),
65        section_label("Workspace"),
66        side_item("file-text", "Billing", false),
67        side_item("bar-chart", "Appearance", false),
68        side_item("activity", "Integrations", false),
69        spacer(),
70        column([text("Changes sync after save.").caption().wrap_text()])
71            .padding(tokens::SPACE_2)
72            .fill(tokens::MUTED)
73            .radius(tokens::RADIUS_MD),
74    ])
75    .gap(tokens::SPACE_2)
76    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_2))
77    .key("metric:sidebar")
78    .width(Size::Fixed(244.0))
79    .height(Size::Fill(1.0))
80    .fill(tokens::CARD)
81    .stroke(tokens::BORDER)
82}
83
84fn settings_main() -> El {
85    column([
86        settings_header(),
87        row([settings_nav_card(), settings_body(), settings_aside()])
88            .gap(tokens::SPACE_4)
89            .padding(tokens::SPACE_4)
90            .height(Size::Fill(1.0))
91            .align(Align::Stretch),
92    ])
93    .width(Size::Fill(1.0))
94    .height(Size::Fill(1.0))
95}
96
97fn settings_header() -> El {
98    row([
99        icon_button("menu").ghost(),
100        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
101        h3("Settings").key("metric:page.title"),
102        spacer(),
103        button("Reset").secondary(),
104        button("Save changes").primary(),
105    ])
106    .key("metric:header")
107    .gap(tokens::SPACE_3)
108    .height(Size::Fixed(56.0))
109    .padding(Sides::xy(tokens::SPACE_4, 0.0))
110    .align(Align::Center)
111    .stroke(tokens::BORDER)
112}
113
114fn settings_nav_card() -> El {
115    column([
116        settings_nav_item("Account", true),
117        settings_nav_item("Security", false),
118        settings_nav_item("Notifications", false),
119        settings_nav_item("Appearance", false),
120        settings_nav_item("Billing", false),
121    ])
122    .gap(tokens::SPACE_1)
123    .padding(tokens::SPACE_1)
124    .width(Size::Fixed(220.0))
125    .height(Size::Fill(1.0))
126    .style_profile(StyleProfile::Surface)
127    .surface_role(SurfaceRole::Panel)
128    .fill(tokens::CARD)
129    .stroke(tokens::BORDER)
130    .radius(tokens::RADIUS_MD)
131    .shadow(tokens::SHADOW_MD)
132}
133
134fn settings_nav_item(label: &'static str, selected: bool) -> El {
135    let mut item = row([
136        El::new(Kind::Custom("nav-dot"))
137            .fill(tokens::MUTED_FOREGROUND)
138            .radius(tokens::RADIUS_PILL)
139            .width(Size::Fixed(6.0))
140            .height(Size::Fixed(6.0)),
141        text(label)
142            .font_weight(FontWeight::Medium)
143            .ellipsis()
144            .width(Size::Fill(1.0)),
145    ])
146    .key(if selected {
147        "metric:settings.nav.row".to_string()
148    } else {
149        format!("settings-nav-{label}")
150    })
151    .metrics_role(MetricsRole::ListItem)
152    .align(Align::Center)
153    .focusable();
154
155    if selected {
156        item = item.current();
157    } else {
158        item = item.color(tokens::MUTED_FOREGROUND);
159    }
160
161    item
162}
163
164fn settings_body() -> El {
165    column([
166        column([
167            h1("Account").heading().key("metric:section.title"),
168            text("Manage identity, workspace defaults, and security preferences.")
169                .muted()
170                .wrap_text()
171                .key("metric:page.subtitle"),
172        ])
173        .gap(tokens::SPACE_1)
174        .height(Size::Hug),
175        scroll([profile_card(), preferences_card()])
176            .key("settings-body-scroll")
177            .gap(tokens::SPACE_4)
178            .width(Size::Fill(1.0))
179            .height(Size::Fill(1.0)),
180    ])
181    .gap(tokens::SPACE_4)
182    .width(Size::Fill(1.0))
183    .height(Size::Fill(1.0))
184}
185
186fn profile_card() -> El {
187    card([
188        card_header([
189            card_title("Profile"),
190            card_description("This information appears in audit logs and shared documents."),
191        ]),
192        card_content([form([
193            row([
194                setting_field("Display name", "Alicia Koch", "display-name"),
195                setting_field("Email", "alicia@acme.co", "email"),
196            ])
197            .gap(tokens::SPACE_3),
198            row([
199                setting_select("Role", "Workspace admin", "role"),
200                setting_select("Region", "US East", "region"),
201            ])
202            .gap(tokens::SPACE_3),
203        ])]),
204    ])
205    .key("metric:profile.card")
206}
207
208fn setting_field(label: &'static str, value: &'static str, key: &'static str) -> El {
209    form_item([
210        form_label(label),
211        form_control(
212            text_input(value, &Selection::caret(key, value.len()), key).key(
213                if key == "display-name" {
214                    "metric:form.input"
215                } else {
216                    key
217                },
218            ),
219        ),
220    ])
221    .width(Size::Fill(1.0))
222}
223
224fn setting_select(label: &'static str, value: &'static str, key: &'static str) -> El {
225    form_item([form_label(label), form_control(select_trigger(key, value))]).width(Size::Fill(1.0))
226}
227
228fn preferences_card() -> El {
229    card([
230        card_header([
231            card_title("Preferences"),
232            card_description("Defaults used when creating new dashboards and exports."),
233        ]),
234        card_content([column([
235            preference_row(
236                "Compact navigation",
237                "Use tighter rows in the sidebar and command menus.",
238                switch(true).key("compact-navigation"),
239            ),
240            divider(),
241            preference_row(
242                "Email summaries",
243                "Send a daily digest when documents change.",
244                switch(false).key("email-summaries"),
245            ),
246            divider(),
247            preference_row(
248                "Require approval",
249                "Route external sharing through an owner review.",
250                checkbox(true).key("approval-required"),
251            ),
252        ])
253        .gap(0.0)
254        .width(Size::Fill(1.0))])
255        .padding(0.0),
256    ])
257    .key("metric:preferences.card")
258}
259
260fn preference_row(title: &'static str, description: &'static str, control: El) -> El {
261    row([
262        column([
263            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
264            text(description)
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0))
271        .height(Size::Hug),
272        control,
273    ])
274    .key(if title == "Compact navigation" {
275        "metric:preference.row".to_string()
276    } else {
277        format!("preference-{title}")
278    })
279    .metrics_role(MetricsRole::PreferenceRow)
280    .gap(tokens::SPACE_4)
281    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_3))
282    .align(Align::Center)
283}
284
285fn settings_aside() -> El {
286    column([security_card(), scale_card()])
287        .gap(tokens::SPACE_4)
288        .width(Size::Fixed(300.0))
289        .height(Size::Fill(1.0))
290}
291
292fn security_card() -> El {
293    card([
294        card_header([
295            card_title("Security"),
296            card_description("Two-factor authentication is enabled for all privileged users."),
297        ]),
298        card_content([
299            compact_stat("Passkeys", "2 registered", badge("On").success()),
300            compact_stat("Sessions", "3 active", button("Review").secondary()),
301        ]),
302    ])
303    .width(Size::Fill(1.0))
304}
305
306fn scale_card() -> El {
307    card([
308        card_header([
309            card_title("Interface scale"),
310            card_description("Reference captures keep browser zoom fixed and vary root UI scale."),
311        ]),
312        card_content([
313            row([text("Dense").caption(), spacer(), text("Default").caption()]),
314            slider(0.66, tokens::PRIMARY)
315                .key("interface-scale")
316                .width(Size::Fill(1.0)),
317        ]),
318    ])
319    .width(Size::Fill(1.0))
320}
321
322fn compact_stat(title: &'static str, detail: &'static str, control: El) -> El {
323    row([
324        column([
325            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
326            text(detail).caption().ellipsis().width(Size::Fill(1.0)),
327        ])
328        .gap(2.0)
329        .width(Size::Fill(1.0))
330        .height(Size::Hug),
331        control,
332    ])
333    .gap(tokens::SPACE_2)
334    .height(Size::Fixed(44.0))
335    .align(Align::Center)
336}
337
338fn section_label(label: &'static str) -> El {
339    text(label)
340        .caption()
341        .height(Size::Fixed(22.0))
342        .padding(Sides::xy(tokens::SPACE_2, 0.0))
343}
examples/dashboard_01_calibration.rs (line 51)
41fn dashboard_sidebar() -> El {
42    column([
43        row([
44            icon_cell("A"),
45            column([
46                text("Acme Inc.")
47                    .semibold()
48                    .ellipsis()
49                    .width(Size::Fill(1.0)),
50                text("Enterprise")
51                    .caption()
52                    .ellipsis()
53                    .width(Size::Fill(1.0)),
54            ])
55            .gap(2.0)
56            .width(Size::Fill(1.0))
57            .height(Size::Hug),
58        ])
59        .gap(tokens::SPACE_2)
60        .height(Size::Fixed(44.0))
61        .align(Align::Center),
62        section_label("Platform"),
63        side_item("layout-dashboard", "Dashboard", true),
64        side_item("activity", "Lifecycle", false),
65        side_item("bar-chart", "Analytics", false),
66        side_item("folder", "Projects", false),
67        spacer().height(Size::Fixed(tokens::SPACE_4)),
68        section_label("Documents"),
69        side_item("file-text", "Data library", false),
70        side_item("download", "Reports", false),
71        side_item("users", "Team", false),
72        spacer(),
73        row([
74            icon_cell("AK"),
75            column([
76                text("Alicia Koch")
77                    .semibold()
78                    .ellipsis()
79                    .width(Size::Fill(1.0)),
80                text("alicia@example.com")
81                    .caption()
82                    .ellipsis()
83                    .width(Size::Fill(1.0)),
84            ])
85            .gap(2.0)
86            .width(Size::Fill(1.0))
87            .height(Size::Hug),
88        ])
89        .gap(tokens::SPACE_2)
90        .height(Size::Fixed(50.0))
91        .align(Align::Center),
92    ])
93    .gap(tokens::SPACE_2)
94    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_2))
95    .key("metric:sidebar")
96    .width(Size::Fixed(244.0))
97    .height(Size::Fill(1.0))
98    .fill(tokens::CARD)
99    .stroke(tokens::BORDER)
100}
101
102fn section_label(label: &'static str) -> El {
103    text(label)
104        .caption()
105        .height(Size::Fixed(22.0))
106        .padding(Sides::xy(tokens::SPACE_2, 0.0))
107}
108
109fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
110    let mut item = row([
111        icon(icon_name)
112            .color(tokens::MUTED_FOREGROUND)
113            .icon_size(tokens::ICON_SM)
114            .width(Size::Fixed(tokens::ICON_SM)),
115        text(label)
116            .font_weight(FontWeight::Medium)
117            .ellipsis()
118            .width(Size::Fill(1.0)),
119    ])
120    .key(if selected {
121        "metric:sidebar.nav.row".to_string()
122    } else {
123        format!("side-item-{label}")
124    })
125    .metrics_role(MetricsRole::ListItem)
126    .gap(tokens::SPACE_2)
127    .padding(Sides::xy(tokens::SPACE_2, 0.0))
128    .height(Size::Fixed(32.0))
129    .align(Align::Center)
130    .focusable();
131
132    if selected {
133        item = item.current();
134    } else {
135        item = item.color(tokens::MUTED_FOREGROUND);
136    }
137
138    item
139}
140
141fn dashboard_main() -> El {
142    column([
143        dashboard_header(),
144        column([
145            row([
146                metric_card(
147                    "bar-chart",
148                    "Total Revenue",
149                    "$1,250.00",
150                    "+12.5%",
151                    "Trending up this month",
152                    true,
153                ),
154                metric_card(
155                    "users",
156                    "New Customers",
157                    "1,234",
158                    "-20%",
159                    "Acquisition needs attention",
160                    false,
161                ),
162                metric_card(
163                    "folder",
164                    "Active Accounts",
165                    "45,678",
166                    "+12.5%",
167                    "Strong user retention",
168                    true,
169                ),
170                metric_card(
171                    "activity",
172                    "Growth Rate",
173                    "4.5%",
174                    "+4.5%",
175                    "Meets growth projections",
176                    true,
177                ),
178            ])
179            .gap(tokens::SPACE_4),
180            row([chart_card(), sales_card()])
181                .gap(tokens::SPACE_4)
182                .height(Size::Fixed(306.0))
183                .align(Align::Stretch),
184            documents_card(),
185        ])
186        .gap(tokens::SPACE_4)
187        .padding(tokens::SPACE_7)
188        .height(Size::Fill(1.0)),
189    ])
190    .width(Size::Fill(1.0))
191    .height(Size::Fill(1.0))
192}
193
194fn dashboard_header() -> El {
195    row([
196        icon_button("menu").ghost(),
197        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
198        h3("Documents").key("metric:page.title"),
199        spacer(),
200        text_input("Search...", &Selection::default(), "dashboard-search")
201            .key("metric:command.input")
202            .width(Size::Fixed(260.0)),
203        icon_button("plus").ghost(),
204        icon_button("bell").ghost(),
205    ])
206    .key("metric:header")
207    .gap(tokens::SPACE_3)
208    .height(Size::Fixed(56.0))
209    .padding(Sides::xy(tokens::SPACE_4, 0.0))
210    .align(Align::Center)
211    .stroke(tokens::BORDER)
212}
213
214fn metric_card(
215    icon_name: &'static str,
216    title: &'static str,
217    value: &'static str,
218    delta: &'static str,
219    note: &'static str,
220    positive: bool,
221) -> El {
222    let badge = if positive {
223        badge(delta).success()
224    } else {
225        badge(delta).warning()
226    };
227    let badge = if title == "Total Revenue" {
228        badge.key("metric:kpi.badge")
229    } else {
230        badge
231    };
232    let value = if title == "Total Revenue" {
233        h2(value).ellipsis().key("metric:kpi.value")
234    } else {
235        h2(value).ellipsis()
236    };
237    card([card_content([
238        row([
239            row([
240                icon(icon_name)
241                    .color(tokens::MUTED_FOREGROUND)
242                    .icon_size(tokens::ICON_XS),
243                text(title).muted().ellipsis().width(Size::Fill(1.0)),
244            ])
245            .gap(tokens::SPACE_1)
246            .width(Size::Fill(1.0))
247            .align(Align::Center),
248            badge,
249        ])
250        .gap(tokens::SPACE_2)
251        .align(Align::Center),
252        value,
253        text(note).caption().ellipsis().width(Size::Fill(1.0)),
254    ])
255    .padding(tokens::SPACE_4)
256    .gap(tokens::SPACE_2)])
257    .key(if title == "Total Revenue" {
258        "metric:kpi.card"
259    } else {
260        title
261    })
262    .width(Size::Fill(1.0))
263}
264
265fn chart_card() -> El {
266    card([
267        card_header([
268            card_title("Visitors for the last 6 months"),
269            card_description("Total visitors by channel."),
270        ])
271        .padding(tokens::SPACE_4),
272        card_content([row(chart_bars())
273            .gap(2.0)
274            .height(Size::Fill(1.0))
275            .align(Align::End)])
276        .padding(Sides {
277            left: tokens::SPACE_4,
278            right: tokens::SPACE_4,
279            top: 0.0,
280            bottom: tokens::SPACE_4,
281        })
282        .height(Size::Fill(1.0)),
283    ])
284    .key("metric:chart.card")
285    .width(Size::Fill(1.0))
286    .height(Size::Fill(1.0))
287}
288
289fn chart_bars() -> Vec<El> {
290    [
291        48.0, 72.0, 56.0, 90.0, 64.0, 80.0, 108.0, 84.0, 122.0, 96.0, 136.0, 118.0,
292    ]
293    .into_iter()
294    .flat_map(|height| {
295        [
296            bar(height, tokens::MUTED_FOREGROUND),
297            bar((height - 28.0_f32).max(24.0), tokens::INPUT),
298        ]
299    })
300    .collect()
301}
302
303fn bar(height: f32, color: Color) -> El {
304    El::new(Kind::Custom("chart_bar"))
305        .fill(color)
306        .radius(tokens::RADIUS_SM)
307        .width(Size::Fill(1.0))
308        .height(Size::Fixed(height))
309}
310
311fn sales_card() -> El {
312    card([
313        card_header([
314            card_title("Recent Sales"),
315            card_description("You made 265 sales this month."),
316        ])
317        .padding(tokens::SPACE_4),
318        card_content([
319            sale_row("OM", "Olivia Martin", "olivia@example.com", "+$1,999.00"),
320            sale_row("JL", "Jackson Lee", "jackson@example.com", "+$39.00"),
321            sale_row("IN", "Isabella Nguyen", "isabella@example.com", "+$299.00"),
322            sale_row("WK", "William Kim", "will@example.com", "+$99.00"),
323        ])
324        .gap(tokens::SPACE_2)
325        .padding(Sides {
326            left: tokens::SPACE_4,
327            right: tokens::SPACE_4,
328            top: 0.0,
329            bottom: tokens::SPACE_4,
330        }),
331    ])
332    .key("metric:sales.card")
333    .width(Size::Fixed(330.0))
334    .height(Size::Fill(1.0))
335}
336
337fn sale_row(
338    initials: &'static str,
339    name: &'static str,
340    email: &'static str,
341    amount: &'static str,
342) -> El {
343    row([
344        icon_cell(initials),
345        column([
346            text(name).semibold().ellipsis().width(Size::Fill(1.0)),
347            text(email).caption().ellipsis().width(Size::Fill(1.0)),
348        ])
349        .gap(2.0)
350        .height(Size::Hug)
351        .width(Size::Fill(1.0)),
352        text(amount).label().small(),
353    ])
354    .gap(tokens::SPACE_2)
355    .height(Size::Fixed(42.0))
356    .align(Align::Center)
357}
358
359fn documents_card() -> El {
360    card([
361        card_header([card_title("Documents")]).padding(tokens::SPACE_4),
362        card_content([scroll([table([
363            table_header([table_row([
364                table_head("").width(Size::Fixed(35.0)),
365                table_head("Header").width(Size::Fill(1.8)),
366                table_head("Section Type").width(Size::Fill(1.0)),
367                table_head("Status").width(Size::Fixed(104.0)),
368                table_head("Target").width(Size::Fixed(64.0)),
369                table_head("Limit").width(Size::Fixed(64.0)),
370                table_head("Reviewer").width(Size::Fixed(128.0)),
371                table_head("").width(Size::Fixed(32.0)),
372            ])
373            .padding(Sides::xy(tokens::SPACE_4, 0.0))
374            .key("metric:table.header")]),
375            divider(),
376            table_body([
377                document_row(
378                    "Cover page",
379                    "Cover page",
380                    "In Process",
381                    "18",
382                    "5",
383                    "Eddie Lake",
384                    "info",
385                ),
386                document_row(
387                    "Table of contents",
388                    "Table of contents",
389                    "Done",
390                    "29",
391                    "24",
392                    "Eddie Lake",
393                    "success",
394                ),
395            ]),
396        ])])
397        .height(Size::Fill(1.0))])
398        .gap(0.0)
399        .padding(0.0)
400        .height(Size::Fill(1.0)),
401    ])
402    .key("metric:table.card")
403    .height(Size::Fill(1.0))
404}
405
406fn document_row(
407    header: &'static str,
408    section: &'static str,
409    status: &'static str,
410    target: &'static str,
411    limit: &'static str,
412    reviewer: &'static str,
413    tone: &'static str,
414) -> El {
415    let status_badge = match tone {
416        "success" => badge(status).success(),
417        _ => badge(status).info(),
418    };
419    table_row([
420        table_utility_cell("::"),
421        table_cell(text(header).label().small()).width(Size::Fill(1.8)),
422        table_cell(text(section).muted()).width(Size::Fill(1.0)),
423        table_cell(status_badge).width(Size::Fixed(104.0)),
424        table_cell(text(target).label().small()).width(Size::Fixed(64.0)),
425        table_cell(text(limit).label().small()).width(Size::Fixed(64.0)),
426        table_cell(text(reviewer).muted()).width(Size::Fixed(128.0)),
427        table_action_cell(),
428    ])
429    .padding(Sides::xy(tokens::SPACE_4, 0.0))
430    .key(if header == "Cover page" {
431        "metric:table.row"
432    } else {
433        header
434    })
435}
436
437fn table_utility_cell(label: &'static str) -> El {
438    table_cell(text(label).muted().center_text()).width(Size::Fixed(35.0))
439}
440
441fn table_action_cell() -> El {
442    stack([icon("more-horizontal")
443        .icon_size(tokens::ICON_SM)
444        .color(tokens::MUTED_FOREGROUND)])
445    .align(Align::Center)
446    .justify(Justify::Center)
447    .width(Size::Fixed(32.0))
448    .height(Size::Hug)
449}
450
451fn icon_cell(label: &'static str) -> El {
452    El::new(Kind::Custom("icon_cell"))
453        .style_profile(StyleProfile::Surface)
454        .text(label)
455        .text_align(TextAlign::Center)
456        .caption()
457        .font_weight(FontWeight::Semibold)
458        .fill(tokens::MUTED)
459        .radius(tokens::RADIUS_SM)
460        .width(Size::Fixed(30.0))
461        .height(Size::Fixed(30.0))
462}
Source

pub fn label(self) -> Self

Examples found in repository?
examples/dashboard_01_calibration.rs (line 352)
337fn sale_row(
338    initials: &'static str,
339    name: &'static str,
340    email: &'static str,
341    amount: &'static str,
342) -> El {
343    row([
344        icon_cell(initials),
345        column([
346            text(name).semibold().ellipsis().width(Size::Fill(1.0)),
347            text(email).caption().ellipsis().width(Size::Fill(1.0)),
348        ])
349        .gap(2.0)
350        .height(Size::Hug)
351        .width(Size::Fill(1.0)),
352        text(amount).label().small(),
353    ])
354    .gap(tokens::SPACE_2)
355    .height(Size::Fixed(42.0))
356    .align(Align::Center)
357}
358
359fn documents_card() -> El {
360    card([
361        card_header([card_title("Documents")]).padding(tokens::SPACE_4),
362        card_content([scroll([table([
363            table_header([table_row([
364                table_head("").width(Size::Fixed(35.0)),
365                table_head("Header").width(Size::Fill(1.8)),
366                table_head("Section Type").width(Size::Fill(1.0)),
367                table_head("Status").width(Size::Fixed(104.0)),
368                table_head("Target").width(Size::Fixed(64.0)),
369                table_head("Limit").width(Size::Fixed(64.0)),
370                table_head("Reviewer").width(Size::Fixed(128.0)),
371                table_head("").width(Size::Fixed(32.0)),
372            ])
373            .padding(Sides::xy(tokens::SPACE_4, 0.0))
374            .key("metric:table.header")]),
375            divider(),
376            table_body([
377                document_row(
378                    "Cover page",
379                    "Cover page",
380                    "In Process",
381                    "18",
382                    "5",
383                    "Eddie Lake",
384                    "info",
385                ),
386                document_row(
387                    "Table of contents",
388                    "Table of contents",
389                    "Done",
390                    "29",
391                    "24",
392                    "Eddie Lake",
393                    "success",
394                ),
395            ]),
396        ])])
397        .height(Size::Fill(1.0))])
398        .gap(0.0)
399        .padding(0.0)
400        .height(Size::Fill(1.0)),
401    ])
402    .key("metric:table.card")
403    .height(Size::Fill(1.0))
404}
405
406fn document_row(
407    header: &'static str,
408    section: &'static str,
409    status: &'static str,
410    target: &'static str,
411    limit: &'static str,
412    reviewer: &'static str,
413    tone: &'static str,
414) -> El {
415    let status_badge = match tone {
416        "success" => badge(status).success(),
417        _ => badge(status).info(),
418    };
419    table_row([
420        table_utility_cell("::"),
421        table_cell(text(header).label().small()).width(Size::Fill(1.8)),
422        table_cell(text(section).muted()).width(Size::Fill(1.0)),
423        table_cell(status_badge).width(Size::Fixed(104.0)),
424        table_cell(text(target).label().small()).width(Size::Fixed(64.0)),
425        table_cell(text(limit).label().small()).width(Size::Fixed(64.0)),
426        table_cell(text(reviewer).muted()).width(Size::Fixed(128.0)),
427        table_action_cell(),
428    ])
429    .padding(Sides::xy(tokens::SPACE_4, 0.0))
430    .key(if header == "Cover page" {
431        "metric:table.row"
432    } else {
433        header
434    })
435}
More examples
Hide additional examples
examples/palette_demo.rs (line 294)
282fn token_chip(token: TokenDef, palette: &Palette) -> El {
283    let resolved = palette.resolve(token.color);
284    row([
285        El::new(Kind::Custom("palette-swatch"))
286            .at(file!(), line!())
287            .fill(token.color)
288            .stroke(tokens::BORDER)
289            .radius(tokens::RADIUS_SM)
290            .width(Size::Fixed(42.0))
291            .height(Size::Fixed(34.0)),
292        column([
293            text(token.name)
294                .label()
295                .ellipsis()
296                .nowrap_text()
297                .width(Size::Fill(1.0)),
298            mono(rgba_label(resolved)).caption().muted(),
299        ])
300        .gap(0.0)
301        .width(Size::Fill(1.0))
302        .height(Size::Hug),
303    ])
304    .gap(tokens::SPACE_2)
305    .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_2))
306    .align(Align::Center)
307    .fill(tokens::CARD)
308    .stroke(tokens::BORDER)
309    .radius(tokens::RADIUS_MD)
310    .width(Size::Fill(1.0))
311    .height(Size::Fixed(54.0))
312}
313
314fn component_section() -> El {
315    card([
316        card_header([
317            card_title("Stock widgets"),
318            card_description("The same palette applied to regular component constructors."),
319        ]),
320        card_content([
321            row([
322                button("Primary").primary(),
323                button("Secondary").secondary(),
324                button("Outline").outline(),
325                button("Ghost").ghost(),
326            ])
327            .gap(tokens::SPACE_2)
328            .align(Align::Center),
329            row([
330                badge("success").success(),
331                badge("warning").warning(),
332                badge("destructive").destructive(),
333                badge("info").info(),
334                badge("muted").muted(),
335            ])
336            .gap(tokens::SPACE_2)
337            .align(Align::Center),
338            row([
339                text_input("palette search", &Selection::default(), "palette:search")
340                    .width(Size::Fill(1.0)),
341                button_with_icon("settings", "Tune").secondary(),
342            ])
343            .gap(tokens::SPACE_2)
344            .align(Align::Center),
345            row([
346                surface_sample("Card", tokens::CARD),
347                surface_sample("Muted", tokens::MUTED),
348                surface_sample("Popover", tokens::POPOVER),
349            ])
350            .gap(tokens::SPACE_3)
351            .align(Align::Stretch),
352        ])
353        .gap(tokens::SPACE_4),
354    ])
355    .height(Size::Hug)
356}
357
358fn surface_sample(title: &'static str, fill: Color) -> El {
359    column([
360        text(title).label(),
361        text("surface sample").caption().muted(),
362    ])
363    .gap(tokens::SPACE_1)
364    .padding(tokens::SPACE_3)
365    .fill(fill)
366    .stroke(tokens::BORDER)
367    .radius(tokens::RADIUS_MD)
368    .width(Size::Fill(1.0))
369    .height(Size::Fixed(76.0))
370}
examples/polish_calibration.rs (line 272)
237fn data_row(
238    status: &'static str,
239    title: &'static str,
240    owner: &'static str,
241    state: &'static str,
242    selected: bool,
243    tone: &'static str,
244) -> El {
245    let status_badge = match tone {
246        "success" => badge(status).success(),
247        "warning" => badge(status).warning(),
248        "destructive" => badge(status).destructive(),
249        _ => badge(status).info(),
250    };
251    let status_badge = if selected {
252        status_badge.key("metric:table.badge")
253    } else {
254        status_badge
255    };
256
257    let mut row = table_row([
258        table_cell(status_badge).width(Size::Fixed(70.0)),
259        column([
260            text(title)
261                .font_weight(FontWeight::Medium)
262                .ellipsis()
263                .width(Size::Fill(1.0)),
264            text("Default styling probe.")
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0)),
271        table_cell(text(owner).muted()).width(Size::Fixed(110.0)),
272        table_cell(text(state).label().small()).width(Size::Fixed(86.0)),
273    ])
274    .key(if selected {
275        "metric:table.row".to_string()
276    } else {
277        format!("row-{title}")
278    })
279    .focusable();
280
281    if selected {
282        row = row.selected();
283    }
284
285    row
286}
Source

pub fn body(self) -> Self

Source

pub fn title(self) -> Self

Source

pub fn heading(self) -> Self

Examples found in repository?
examples/settings_calibration.rs (line 167)
164fn settings_body() -> El {
165    column([
166        column([
167            h1("Account").heading().key("metric:section.title"),
168            text("Manage identity, workspace defaults, and security preferences.")
169                .muted()
170                .wrap_text()
171                .key("metric:page.subtitle"),
172        ])
173        .gap(tokens::SPACE_1)
174        .height(Size::Hug),
175        scroll([profile_card(), preferences_card()])
176            .key("settings-body-scroll")
177            .gap(tokens::SPACE_4)
178            .width(Size::Fill(1.0))
179            .height(Size::Fill(1.0)),
180    ])
181    .gap(tokens::SPACE_4)
182    .width(Size::Fill(1.0))
183    .height(Size::Fill(1.0))
184}
Source

pub fn display(self) -> Self

Examples found in repository?
examples/polish_calibration.rs (line 154)
143fn kpi_card(label: &'static str, value: &'static str, delta: &'static str, positive: bool) -> El {
144    let delta_badge = if positive {
145        badge(delta).success()
146    } else {
147        badge(delta).destructive()
148    };
149    let delta_badge = if label == "Latency" {
150        delta_badge.key("metric:kpi.badge")
151    } else {
152        delta_badge
153    };
154    let value_text = h2(value).display();
155    let value_text = if label == "Latency" {
156        value_text.key("metric:kpi.value")
157    } else {
158        value_text
159    };
160    card([
161        card_header([card_title(label)]),
162        card_content([
163            row([value_text, spacer(), delta_badge]).align(Align::Center),
164            text(if positive {
165                "Moving in the expected direction"
166            } else {
167                "Needs visual attention"
168            })
169            .muted(),
170        ])
171        .gap(tokens::SPACE_6),
172    ])
173    .key(if label == "Latency" {
174        "metric:kpi.card"
175    } else {
176        label
177    })
178    .width(Size::Fill(1.0))
179}
Source

pub fn bold(self) -> Self

Examples found in repository?
examples/scroll_list.rs (line 20)
15fn scroll_list_fixture() -> El {
16    let rows: Vec<El> = (0..20)
17        .map(|i| {
18            row([
19                badge(format!("#{i}")).info(),
20                text(format!("Notification {i}")).bold(),
21                spacer(),
22                text(format!("{}m ago", i + 1)).muted(),
23            ])
24            .gap(tokens::SPACE_2)
25            .height(Size::Fixed(44.0))
26            .padding(Sides::xy(tokens::SPACE_3, tokens::SPACE_2))
27        })
28        .collect();
29
30    let list = scroll(rows)
31        .key("notifications")
32        .height(Size::Fixed(420.0))
33        .padding(tokens::SPACE_2);
34
35    column([
36        h2("Notifications"),
37        text("Roll the wheel inside the panel to scroll. The content is taller than the viewport.")
38            .muted(),
39        list,
40    ])
41    .gap(tokens::SPACE_4)
42    .padding(tokens::SPACE_7)
43}
More examples
Hide additional examples
examples/inline_runs.rs (line 23)
18fn fixture() -> El {
19    column([
20        h2("Inline runs"),
21        text_runs([
22            text("Aetna's attributed-text path lets you compose runs with "),
23            text("bold").bold(),
24            text(", "),
25            text("italic").italic(),
26            text(", "),
27            text("colored").color(tokens::DESTRUCTIVE),
28            text(", and "),
29            text("inline code").code(),
30            text(" segments inside one wrapping paragraph."),
31            hard_break(),
32            text("Hard breaks act like ").muted(),
33            text("<br>").code(),
34            text(" — they end the current line without breaking out of the run.").muted(),
35        ])
36        .wrap_text()
37        .width(Size::Fill(1.0))
38        .height(Size::Hug),
39        paragraph(
40            "All of the above flows through one cosmic-text rich-text shape — \
41             wrapping decisions cross run boundaries the way real prose wraps.",
42        )
43        .muted(),
44    ])
45    .gap(tokens::SPACE_4)
46    .padding(tokens::SPACE_7)
47    .width(Size::Fixed(640.0))
48}
examples/modal.rs (line 24)
9fn modal_fixture() -> El {
10    stack([
11        column([
12            h1("Account"),
13            titled_card(
14                "Profile",
15                [
16                    row([text("Email"), spacer(), text("user@example.com").muted()]),
17                    row([text("Plan"), spacer(), badge("Pro").info()]),
18                ],
19            ),
20            titled_card(
21                "Danger zone",
22                [row([
23                    column([
24                        text("Delete account").bold(),
25                        text("Remove this account and all associated data.")
26                            .muted()
27                            .small(),
28                    ])
29                    .gap(tokens::SPACE_1)
30                    .align(Align::Start)
31                    .width(Size::Hug),
32                    spacer(),
33                    button("Delete").destructive().key("open-delete"),
34                ])],
35            ),
36        ])
37        .gap(tokens::SPACE_4)
38        .padding(tokens::SPACE_7),
39        modal(
40            "delete-account",
41            "Delete account?",
42            [
43                text("Permanent action. Export data first.").muted(),
44                row([
45                    spacer(),
46                    button("Cancel").ghost().key("cancel-delete"),
47                    button("Delete").destructive().key("confirm-delete"),
48                ])
49                .gap(tokens::SPACE_2),
50            ],
51        ),
52    ])
53}
examples/settings.rs (line 47)
16fn settings() -> El {
17    column([
18        h1("Settings"),
19        titled_card(
20            "Account",
21            [
22                row([text("Email"), spacer(), text("user@example.com").muted()]),
23                row([
24                    text("Two-factor authentication"),
25                    spacer(),
26                    badge("Enabled").success(),
27                ]),
28                row([
29                    text("Recovery codes"),
30                    spacer(),
31                    button("Generate").secondary(),
32                ]),
33            ],
34        ),
35        titled_card(
36            "Appearance",
37            [
38                row([text("Theme"), spacer(), button("Dark").secondary()]),
39                row([text("Compact mode"), spacer(), badge("Off").muted()]),
40                row([text("Font size"), spacer(), text("14")]),
41            ],
42        ),
43        titled_card(
44            "Danger zone",
45            [row([
46                column([
47                    text("Delete account").bold(),
48                    text("Permanently remove your account and all data.")
49                        .muted()
50                        .small(),
51                ])
52                .gap(tokens::SPACE_1)
53                .align(Align::Start)
54                .width(Size::Hug),
55                spacer(),
56                button("Delete").destructive(),
57            ])],
58        ),
59        row([spacer(), button("Cancel").ghost(), button("Save").primary()]),
60    ])
61    .gap(tokens::SPACE_4)
62    .padding(tokens::SPACE_7)
63}
examples/inline_runs_highlight.rs (line 38)
26fn fixture() -> El {
27    column([
28        h2("Inline run backgrounds"),
29        paragraph(
30            "RunStyle.bg paints a per-line solid quad behind the glyphs of \
31             a styled span — the shaper computes the rect from the actual \
32             glyph extents, so wrapping splits the highlight cleanly.",
33        )
34        .muted(),
35        // Search-result style.
36        text_runs([
37            text("…the matcher finds "),
38            text("aetna").background(HIGHLIGHT_YELLOW).bold(),
39            text(" in "),
40            text("aetna_core::widgets").mono(),
41            text(" — the highlight tracks the glyph extent."),
42        ])
43        .wrap_text()
44        .width(Size::Fill(1.0))
45        .height(Size::Hug),
46        // Diff-style: add + remove tints inside the same line.
47        text_runs([
48            text("- "),
49            text("error::Custom").mono().background(DIFF_REMOVE),
50            text("(\"too narrow\")"),
51            hard_break(),
52            text("+ "),
53            text("error::WrapTooNarrow").mono().background(DIFF_ADD),
54            text(" { available }"),
55        ])
56        .wrap_text()
57        .width(Size::Fill(1.0))
58        .height(Size::Hug),
59        // Wrapping highlight: long span that spans two lines.
60        text_runs([
61            text("Long highlight: "),
62            text("the quick brown fox jumps over the lazy dog and keeps going")
63                .background(HIGHLIGHT_YELLOW),
64            text(" — the rect is split per line."),
65        ])
66        .wrap_text()
67        .width(Size::Fill(1.0))
68        .height(Size::Hug),
69    ])
70    .gap(tokens::SPACE_4)
71    .padding(tokens::SPACE_7)
72    .width(Size::Fixed(640.0))
73}
examples/long_form_content.rs (line 33)
15fn fixture() -> El {
16    column([
17        h2("Long-form content widgets"),
18        paragraph(
19            "These primitives compose the markdown-shaped vocabulary an \
20             upcoming transformer will target. Each widget is plain \
21             Aetna — selectable text, themed surfaces, the same layout \
22             pass as everything else.",
23        ),
24        h3("Highlights"),
25        bullet_list(vec![
26            text_runs([
27                text("Bulleted lists with a hanging indent — wrapped lines align under "),
28                text("themselves").italic(),
29                text(", not under the marker."),
30            ]),
31            text_runs([
32                text("Inline runs work inside list items: "),
33                text("bold").bold(),
34                text(", "),
35                text("code").code(),
36                text(", "),
37                text("links").link("https://aetna.dev"),
38                text("."),
39            ]),
40            text("Nested blocks live inside an item by composing a column."),
41        ]),
42        blockquote([
43            paragraph(
44                "Markdown's shape is HTML's shape. The Aetna widget kit \
45                 already mirrors most of that shape, so the transformer \
46                 mostly hands events to existing constructors.",
47            ),
48            paragraph("— Aetna design notes").muted(),
49        ]),
50        h3("Setup steps"),
51        numbered_list(vec![
52            text("Add `aetna-markdown` to the workspace."),
53            text_runs([
54                text("Pull in "),
55                text("pulldown-cmark").code(),
56                text(" with the GFM features the project actually uses."),
57            ]),
58            text("Wire the transformer through the existing widget kit — paragraph, list, blockquote, code_block, divider, table."),
59        ]),
60        h3("Example fenced block"),
61        code_block(
62            "fn render(md: &str) -> El {\n    \
63                 // pulldown-cmark events -> El\n    \
64                 todo!(\"phase 2\")\n}",
65        ),
66    ])
67    .gap(tokens::SPACE_4)
68    .padding(tokens::SPACE_7)
69    .width(Size::Fixed(640.0))
70}
Source

pub fn semibold(self) -> Self

Examples found in repository?
examples/settings_calibration.rs (line 47)
41fn settings_sidebar() -> El {
42    column([
43        row([
44            icon_slot("settings"),
45            column([
46                text("Workspace")
47                    .semibold()
48                    .ellipsis()
49                    .width(Size::Fill(1.0)),
50                text("Settings").caption().ellipsis().width(Size::Fill(1.0)),
51            ])
52            .gap(2.0)
53            .width(Size::Fill(1.0))
54            .height(Size::Hug),
55        ])
56        .gap(tokens::SPACE_2)
57        .height(Size::Fixed(44.0))
58        .align(Align::Center),
59        section_label("Personal"),
60        side_item("users", "Profile", false),
61        side_item("settings", "Account", true),
62        side_item("alert-circle", "Security", false),
63        side_item("bell", "Notifications", false),
64        spacer().height(Size::Fixed(tokens::SPACE_4)),
65        section_label("Workspace"),
66        side_item("file-text", "Billing", false),
67        side_item("bar-chart", "Appearance", false),
68        side_item("activity", "Integrations", false),
69        spacer(),
70        column([text("Changes sync after save.").caption().wrap_text()])
71            .padding(tokens::SPACE_2)
72            .fill(tokens::MUTED)
73            .radius(tokens::RADIUS_MD),
74    ])
75    .gap(tokens::SPACE_2)
76    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_2))
77    .key("metric:sidebar")
78    .width(Size::Fixed(244.0))
79    .height(Size::Fill(1.0))
80    .fill(tokens::CARD)
81    .stroke(tokens::BORDER)
82}
83
84fn settings_main() -> El {
85    column([
86        settings_header(),
87        row([settings_nav_card(), settings_body(), settings_aside()])
88            .gap(tokens::SPACE_4)
89            .padding(tokens::SPACE_4)
90            .height(Size::Fill(1.0))
91            .align(Align::Stretch),
92    ])
93    .width(Size::Fill(1.0))
94    .height(Size::Fill(1.0))
95}
96
97fn settings_header() -> El {
98    row([
99        icon_button("menu").ghost(),
100        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
101        h3("Settings").key("metric:page.title"),
102        spacer(),
103        button("Reset").secondary(),
104        button("Save changes").primary(),
105    ])
106    .key("metric:header")
107    .gap(tokens::SPACE_3)
108    .height(Size::Fixed(56.0))
109    .padding(Sides::xy(tokens::SPACE_4, 0.0))
110    .align(Align::Center)
111    .stroke(tokens::BORDER)
112}
113
114fn settings_nav_card() -> El {
115    column([
116        settings_nav_item("Account", true),
117        settings_nav_item("Security", false),
118        settings_nav_item("Notifications", false),
119        settings_nav_item("Appearance", false),
120        settings_nav_item("Billing", false),
121    ])
122    .gap(tokens::SPACE_1)
123    .padding(tokens::SPACE_1)
124    .width(Size::Fixed(220.0))
125    .height(Size::Fill(1.0))
126    .style_profile(StyleProfile::Surface)
127    .surface_role(SurfaceRole::Panel)
128    .fill(tokens::CARD)
129    .stroke(tokens::BORDER)
130    .radius(tokens::RADIUS_MD)
131    .shadow(tokens::SHADOW_MD)
132}
133
134fn settings_nav_item(label: &'static str, selected: bool) -> El {
135    let mut item = row([
136        El::new(Kind::Custom("nav-dot"))
137            .fill(tokens::MUTED_FOREGROUND)
138            .radius(tokens::RADIUS_PILL)
139            .width(Size::Fixed(6.0))
140            .height(Size::Fixed(6.0)),
141        text(label)
142            .font_weight(FontWeight::Medium)
143            .ellipsis()
144            .width(Size::Fill(1.0)),
145    ])
146    .key(if selected {
147        "metric:settings.nav.row".to_string()
148    } else {
149        format!("settings-nav-{label}")
150    })
151    .metrics_role(MetricsRole::ListItem)
152    .align(Align::Center)
153    .focusable();
154
155    if selected {
156        item = item.current();
157    } else {
158        item = item.color(tokens::MUTED_FOREGROUND);
159    }
160
161    item
162}
163
164fn settings_body() -> El {
165    column([
166        column([
167            h1("Account").heading().key("metric:section.title"),
168            text("Manage identity, workspace defaults, and security preferences.")
169                .muted()
170                .wrap_text()
171                .key("metric:page.subtitle"),
172        ])
173        .gap(tokens::SPACE_1)
174        .height(Size::Hug),
175        scroll([profile_card(), preferences_card()])
176            .key("settings-body-scroll")
177            .gap(tokens::SPACE_4)
178            .width(Size::Fill(1.0))
179            .height(Size::Fill(1.0)),
180    ])
181    .gap(tokens::SPACE_4)
182    .width(Size::Fill(1.0))
183    .height(Size::Fill(1.0))
184}
185
186fn profile_card() -> El {
187    card([
188        card_header([
189            card_title("Profile"),
190            card_description("This information appears in audit logs and shared documents."),
191        ]),
192        card_content([form([
193            row([
194                setting_field("Display name", "Alicia Koch", "display-name"),
195                setting_field("Email", "alicia@acme.co", "email"),
196            ])
197            .gap(tokens::SPACE_3),
198            row([
199                setting_select("Role", "Workspace admin", "role"),
200                setting_select("Region", "US East", "region"),
201            ])
202            .gap(tokens::SPACE_3),
203        ])]),
204    ])
205    .key("metric:profile.card")
206}
207
208fn setting_field(label: &'static str, value: &'static str, key: &'static str) -> El {
209    form_item([
210        form_label(label),
211        form_control(
212            text_input(value, &Selection::caret(key, value.len()), key).key(
213                if key == "display-name" {
214                    "metric:form.input"
215                } else {
216                    key
217                },
218            ),
219        ),
220    ])
221    .width(Size::Fill(1.0))
222}
223
224fn setting_select(label: &'static str, value: &'static str, key: &'static str) -> El {
225    form_item([form_label(label), form_control(select_trigger(key, value))]).width(Size::Fill(1.0))
226}
227
228fn preferences_card() -> El {
229    card([
230        card_header([
231            card_title("Preferences"),
232            card_description("Defaults used when creating new dashboards and exports."),
233        ]),
234        card_content([column([
235            preference_row(
236                "Compact navigation",
237                "Use tighter rows in the sidebar and command menus.",
238                switch(true).key("compact-navigation"),
239            ),
240            divider(),
241            preference_row(
242                "Email summaries",
243                "Send a daily digest when documents change.",
244                switch(false).key("email-summaries"),
245            ),
246            divider(),
247            preference_row(
248                "Require approval",
249                "Route external sharing through an owner review.",
250                checkbox(true).key("approval-required"),
251            ),
252        ])
253        .gap(0.0)
254        .width(Size::Fill(1.0))])
255        .padding(0.0),
256    ])
257    .key("metric:preferences.card")
258}
259
260fn preference_row(title: &'static str, description: &'static str, control: El) -> El {
261    row([
262        column([
263            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
264            text(description)
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0))
271        .height(Size::Hug),
272        control,
273    ])
274    .key(if title == "Compact navigation" {
275        "metric:preference.row".to_string()
276    } else {
277        format!("preference-{title}")
278    })
279    .metrics_role(MetricsRole::PreferenceRow)
280    .gap(tokens::SPACE_4)
281    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_3))
282    .align(Align::Center)
283}
284
285fn settings_aside() -> El {
286    column([security_card(), scale_card()])
287        .gap(tokens::SPACE_4)
288        .width(Size::Fixed(300.0))
289        .height(Size::Fill(1.0))
290}
291
292fn security_card() -> El {
293    card([
294        card_header([
295            card_title("Security"),
296            card_description("Two-factor authentication is enabled for all privileged users."),
297        ]),
298        card_content([
299            compact_stat("Passkeys", "2 registered", badge("On").success()),
300            compact_stat("Sessions", "3 active", button("Review").secondary()),
301        ]),
302    ])
303    .width(Size::Fill(1.0))
304}
305
306fn scale_card() -> El {
307    card([
308        card_header([
309            card_title("Interface scale"),
310            card_description("Reference captures keep browser zoom fixed and vary root UI scale."),
311        ]),
312        card_content([
313            row([text("Dense").caption(), spacer(), text("Default").caption()]),
314            slider(0.66, tokens::PRIMARY)
315                .key("interface-scale")
316                .width(Size::Fill(1.0)),
317        ]),
318    ])
319    .width(Size::Fill(1.0))
320}
321
322fn compact_stat(title: &'static str, detail: &'static str, control: El) -> El {
323    row([
324        column([
325            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
326            text(detail).caption().ellipsis().width(Size::Fill(1.0)),
327        ])
328        .gap(2.0)
329        .width(Size::Fill(1.0))
330        .height(Size::Hug),
331        control,
332    ])
333    .gap(tokens::SPACE_2)
334    .height(Size::Fixed(44.0))
335    .align(Align::Center)
336}
More examples
Hide additional examples
examples/dashboard_01_calibration.rs (line 47)
41fn dashboard_sidebar() -> El {
42    column([
43        row([
44            icon_cell("A"),
45            column([
46                text("Acme Inc.")
47                    .semibold()
48                    .ellipsis()
49                    .width(Size::Fill(1.0)),
50                text("Enterprise")
51                    .caption()
52                    .ellipsis()
53                    .width(Size::Fill(1.0)),
54            ])
55            .gap(2.0)
56            .width(Size::Fill(1.0))
57            .height(Size::Hug),
58        ])
59        .gap(tokens::SPACE_2)
60        .height(Size::Fixed(44.0))
61        .align(Align::Center),
62        section_label("Platform"),
63        side_item("layout-dashboard", "Dashboard", true),
64        side_item("activity", "Lifecycle", false),
65        side_item("bar-chart", "Analytics", false),
66        side_item("folder", "Projects", false),
67        spacer().height(Size::Fixed(tokens::SPACE_4)),
68        section_label("Documents"),
69        side_item("file-text", "Data library", false),
70        side_item("download", "Reports", false),
71        side_item("users", "Team", false),
72        spacer(),
73        row([
74            icon_cell("AK"),
75            column([
76                text("Alicia Koch")
77                    .semibold()
78                    .ellipsis()
79                    .width(Size::Fill(1.0)),
80                text("alicia@example.com")
81                    .caption()
82                    .ellipsis()
83                    .width(Size::Fill(1.0)),
84            ])
85            .gap(2.0)
86            .width(Size::Fill(1.0))
87            .height(Size::Hug),
88        ])
89        .gap(tokens::SPACE_2)
90        .height(Size::Fixed(50.0))
91        .align(Align::Center),
92    ])
93    .gap(tokens::SPACE_2)
94    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_2))
95    .key("metric:sidebar")
96    .width(Size::Fixed(244.0))
97    .height(Size::Fill(1.0))
98    .fill(tokens::CARD)
99    .stroke(tokens::BORDER)
100}
101
102fn section_label(label: &'static str) -> El {
103    text(label)
104        .caption()
105        .height(Size::Fixed(22.0))
106        .padding(Sides::xy(tokens::SPACE_2, 0.0))
107}
108
109fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
110    let mut item = row([
111        icon(icon_name)
112            .color(tokens::MUTED_FOREGROUND)
113            .icon_size(tokens::ICON_SM)
114            .width(Size::Fixed(tokens::ICON_SM)),
115        text(label)
116            .font_weight(FontWeight::Medium)
117            .ellipsis()
118            .width(Size::Fill(1.0)),
119    ])
120    .key(if selected {
121        "metric:sidebar.nav.row".to_string()
122    } else {
123        format!("side-item-{label}")
124    })
125    .metrics_role(MetricsRole::ListItem)
126    .gap(tokens::SPACE_2)
127    .padding(Sides::xy(tokens::SPACE_2, 0.0))
128    .height(Size::Fixed(32.0))
129    .align(Align::Center)
130    .focusable();
131
132    if selected {
133        item = item.current();
134    } else {
135        item = item.color(tokens::MUTED_FOREGROUND);
136    }
137
138    item
139}
140
141fn dashboard_main() -> El {
142    column([
143        dashboard_header(),
144        column([
145            row([
146                metric_card(
147                    "bar-chart",
148                    "Total Revenue",
149                    "$1,250.00",
150                    "+12.5%",
151                    "Trending up this month",
152                    true,
153                ),
154                metric_card(
155                    "users",
156                    "New Customers",
157                    "1,234",
158                    "-20%",
159                    "Acquisition needs attention",
160                    false,
161                ),
162                metric_card(
163                    "folder",
164                    "Active Accounts",
165                    "45,678",
166                    "+12.5%",
167                    "Strong user retention",
168                    true,
169                ),
170                metric_card(
171                    "activity",
172                    "Growth Rate",
173                    "4.5%",
174                    "+4.5%",
175                    "Meets growth projections",
176                    true,
177                ),
178            ])
179            .gap(tokens::SPACE_4),
180            row([chart_card(), sales_card()])
181                .gap(tokens::SPACE_4)
182                .height(Size::Fixed(306.0))
183                .align(Align::Stretch),
184            documents_card(),
185        ])
186        .gap(tokens::SPACE_4)
187        .padding(tokens::SPACE_7)
188        .height(Size::Fill(1.0)),
189    ])
190    .width(Size::Fill(1.0))
191    .height(Size::Fill(1.0))
192}
193
194fn dashboard_header() -> El {
195    row([
196        icon_button("menu").ghost(),
197        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
198        h3("Documents").key("metric:page.title"),
199        spacer(),
200        text_input("Search...", &Selection::default(), "dashboard-search")
201            .key("metric:command.input")
202            .width(Size::Fixed(260.0)),
203        icon_button("plus").ghost(),
204        icon_button("bell").ghost(),
205    ])
206    .key("metric:header")
207    .gap(tokens::SPACE_3)
208    .height(Size::Fixed(56.0))
209    .padding(Sides::xy(tokens::SPACE_4, 0.0))
210    .align(Align::Center)
211    .stroke(tokens::BORDER)
212}
213
214fn metric_card(
215    icon_name: &'static str,
216    title: &'static str,
217    value: &'static str,
218    delta: &'static str,
219    note: &'static str,
220    positive: bool,
221) -> El {
222    let badge = if positive {
223        badge(delta).success()
224    } else {
225        badge(delta).warning()
226    };
227    let badge = if title == "Total Revenue" {
228        badge.key("metric:kpi.badge")
229    } else {
230        badge
231    };
232    let value = if title == "Total Revenue" {
233        h2(value).ellipsis().key("metric:kpi.value")
234    } else {
235        h2(value).ellipsis()
236    };
237    card([card_content([
238        row([
239            row([
240                icon(icon_name)
241                    .color(tokens::MUTED_FOREGROUND)
242                    .icon_size(tokens::ICON_XS),
243                text(title).muted().ellipsis().width(Size::Fill(1.0)),
244            ])
245            .gap(tokens::SPACE_1)
246            .width(Size::Fill(1.0))
247            .align(Align::Center),
248            badge,
249        ])
250        .gap(tokens::SPACE_2)
251        .align(Align::Center),
252        value,
253        text(note).caption().ellipsis().width(Size::Fill(1.0)),
254    ])
255    .padding(tokens::SPACE_4)
256    .gap(tokens::SPACE_2)])
257    .key(if title == "Total Revenue" {
258        "metric:kpi.card"
259    } else {
260        title
261    })
262    .width(Size::Fill(1.0))
263}
264
265fn chart_card() -> El {
266    card([
267        card_header([
268            card_title("Visitors for the last 6 months"),
269            card_description("Total visitors by channel."),
270        ])
271        .padding(tokens::SPACE_4),
272        card_content([row(chart_bars())
273            .gap(2.0)
274            .height(Size::Fill(1.0))
275            .align(Align::End)])
276        .padding(Sides {
277            left: tokens::SPACE_4,
278            right: tokens::SPACE_4,
279            top: 0.0,
280            bottom: tokens::SPACE_4,
281        })
282        .height(Size::Fill(1.0)),
283    ])
284    .key("metric:chart.card")
285    .width(Size::Fill(1.0))
286    .height(Size::Fill(1.0))
287}
288
289fn chart_bars() -> Vec<El> {
290    [
291        48.0, 72.0, 56.0, 90.0, 64.0, 80.0, 108.0, 84.0, 122.0, 96.0, 136.0, 118.0,
292    ]
293    .into_iter()
294    .flat_map(|height| {
295        [
296            bar(height, tokens::MUTED_FOREGROUND),
297            bar((height - 28.0_f32).max(24.0), tokens::INPUT),
298        ]
299    })
300    .collect()
301}
302
303fn bar(height: f32, color: Color) -> El {
304    El::new(Kind::Custom("chart_bar"))
305        .fill(color)
306        .radius(tokens::RADIUS_SM)
307        .width(Size::Fill(1.0))
308        .height(Size::Fixed(height))
309}
310
311fn sales_card() -> El {
312    card([
313        card_header([
314            card_title("Recent Sales"),
315            card_description("You made 265 sales this month."),
316        ])
317        .padding(tokens::SPACE_4),
318        card_content([
319            sale_row("OM", "Olivia Martin", "olivia@example.com", "+$1,999.00"),
320            sale_row("JL", "Jackson Lee", "jackson@example.com", "+$39.00"),
321            sale_row("IN", "Isabella Nguyen", "isabella@example.com", "+$299.00"),
322            sale_row("WK", "William Kim", "will@example.com", "+$99.00"),
323        ])
324        .gap(tokens::SPACE_2)
325        .padding(Sides {
326            left: tokens::SPACE_4,
327            right: tokens::SPACE_4,
328            top: 0.0,
329            bottom: tokens::SPACE_4,
330        }),
331    ])
332    .key("metric:sales.card")
333    .width(Size::Fixed(330.0))
334    .height(Size::Fill(1.0))
335}
336
337fn sale_row(
338    initials: &'static str,
339    name: &'static str,
340    email: &'static str,
341    amount: &'static str,
342) -> El {
343    row([
344        icon_cell(initials),
345        column([
346            text(name).semibold().ellipsis().width(Size::Fill(1.0)),
347            text(email).caption().ellipsis().width(Size::Fill(1.0)),
348        ])
349        .gap(2.0)
350        .height(Size::Hug)
351        .width(Size::Fill(1.0)),
352        text(amount).label().small(),
353    ])
354    .gap(tokens::SPACE_2)
355    .height(Size::Fixed(42.0))
356    .align(Align::Center)
357}
Source

pub fn small(self) -> Self

Examples found in repository?
examples/dashboard_01_calibration.rs (line 352)
337fn sale_row(
338    initials: &'static str,
339    name: &'static str,
340    email: &'static str,
341    amount: &'static str,
342) -> El {
343    row([
344        icon_cell(initials),
345        column([
346            text(name).semibold().ellipsis().width(Size::Fill(1.0)),
347            text(email).caption().ellipsis().width(Size::Fill(1.0)),
348        ])
349        .gap(2.0)
350        .height(Size::Hug)
351        .width(Size::Fill(1.0)),
352        text(amount).label().small(),
353    ])
354    .gap(tokens::SPACE_2)
355    .height(Size::Fixed(42.0))
356    .align(Align::Center)
357}
358
359fn documents_card() -> El {
360    card([
361        card_header([card_title("Documents")]).padding(tokens::SPACE_4),
362        card_content([scroll([table([
363            table_header([table_row([
364                table_head("").width(Size::Fixed(35.0)),
365                table_head("Header").width(Size::Fill(1.8)),
366                table_head("Section Type").width(Size::Fill(1.0)),
367                table_head("Status").width(Size::Fixed(104.0)),
368                table_head("Target").width(Size::Fixed(64.0)),
369                table_head("Limit").width(Size::Fixed(64.0)),
370                table_head("Reviewer").width(Size::Fixed(128.0)),
371                table_head("").width(Size::Fixed(32.0)),
372            ])
373            .padding(Sides::xy(tokens::SPACE_4, 0.0))
374            .key("metric:table.header")]),
375            divider(),
376            table_body([
377                document_row(
378                    "Cover page",
379                    "Cover page",
380                    "In Process",
381                    "18",
382                    "5",
383                    "Eddie Lake",
384                    "info",
385                ),
386                document_row(
387                    "Table of contents",
388                    "Table of contents",
389                    "Done",
390                    "29",
391                    "24",
392                    "Eddie Lake",
393                    "success",
394                ),
395            ]),
396        ])])
397        .height(Size::Fill(1.0))])
398        .gap(0.0)
399        .padding(0.0)
400        .height(Size::Fill(1.0)),
401    ])
402    .key("metric:table.card")
403    .height(Size::Fill(1.0))
404}
405
406fn document_row(
407    header: &'static str,
408    section: &'static str,
409    status: &'static str,
410    target: &'static str,
411    limit: &'static str,
412    reviewer: &'static str,
413    tone: &'static str,
414) -> El {
415    let status_badge = match tone {
416        "success" => badge(status).success(),
417        _ => badge(status).info(),
418    };
419    table_row([
420        table_utility_cell("::"),
421        table_cell(text(header).label().small()).width(Size::Fill(1.8)),
422        table_cell(text(section).muted()).width(Size::Fill(1.0)),
423        table_cell(status_badge).width(Size::Fixed(104.0)),
424        table_cell(text(target).label().small()).width(Size::Fixed(64.0)),
425        table_cell(text(limit).label().small()).width(Size::Fixed(64.0)),
426        table_cell(text(reviewer).muted()).width(Size::Fixed(128.0)),
427        table_action_cell(),
428    ])
429    .padding(Sides::xy(tokens::SPACE_4, 0.0))
430    .key(if header == "Cover page" {
431        "metric:table.row"
432    } else {
433        header
434    })
435}
More examples
Hide additional examples
examples/polish_calibration.rs (line 272)
237fn data_row(
238    status: &'static str,
239    title: &'static str,
240    owner: &'static str,
241    state: &'static str,
242    selected: bool,
243    tone: &'static str,
244) -> El {
245    let status_badge = match tone {
246        "success" => badge(status).success(),
247        "warning" => badge(status).warning(),
248        "destructive" => badge(status).destructive(),
249        _ => badge(status).info(),
250    };
251    let status_badge = if selected {
252        status_badge.key("metric:table.badge")
253    } else {
254        status_badge
255    };
256
257    let mut row = table_row([
258        table_cell(status_badge).width(Size::Fixed(70.0)),
259        column([
260            text(title)
261                .font_weight(FontWeight::Medium)
262                .ellipsis()
263                .width(Size::Fill(1.0)),
264            text("Default styling probe.")
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0)),
271        table_cell(text(owner).muted()).width(Size::Fixed(110.0)),
272        table_cell(text(state).label().small()).width(Size::Fixed(86.0)),
273    ])
274    .key(if selected {
275        "metric:table.row".to_string()
276    } else {
277        format!("row-{title}")
278    })
279    .focusable();
280
281    if selected {
282        row = row.selected();
283    }
284
285    row
286}
examples/modal.rs (line 27)
9fn modal_fixture() -> El {
10    stack([
11        column([
12            h1("Account"),
13            titled_card(
14                "Profile",
15                [
16                    row([text("Email"), spacer(), text("user@example.com").muted()]),
17                    row([text("Plan"), spacer(), badge("Pro").info()]),
18                ],
19            ),
20            titled_card(
21                "Danger zone",
22                [row([
23                    column([
24                        text("Delete account").bold(),
25                        text("Remove this account and all associated data.")
26                            .muted()
27                            .small(),
28                    ])
29                    .gap(tokens::SPACE_1)
30                    .align(Align::Start)
31                    .width(Size::Hug),
32                    spacer(),
33                    button("Delete").destructive().key("open-delete"),
34                ])],
35            ),
36        ])
37        .gap(tokens::SPACE_4)
38        .padding(tokens::SPACE_7),
39        modal(
40            "delete-account",
41            "Delete account?",
42            [
43                text("Permanent action. Export data first.").muted(),
44                row([
45                    spacer(),
46                    button("Cancel").ghost().key("cancel-delete"),
47                    button("Delete").destructive().key("confirm-delete"),
48                ])
49                .gap(tokens::SPACE_2),
50            ],
51        ),
52    ])
53}
examples/settings.rs (line 50)
16fn settings() -> El {
17    column([
18        h1("Settings"),
19        titled_card(
20            "Account",
21            [
22                row([text("Email"), spacer(), text("user@example.com").muted()]),
23                row([
24                    text("Two-factor authentication"),
25                    spacer(),
26                    badge("Enabled").success(),
27                ]),
28                row([
29                    text("Recovery codes"),
30                    spacer(),
31                    button("Generate").secondary(),
32                ]),
33            ],
34        ),
35        titled_card(
36            "Appearance",
37            [
38                row([text("Theme"), spacer(), button("Dark").secondary()]),
39                row([text("Compact mode"), spacer(), badge("Off").muted()]),
40                row([text("Font size"), spacer(), text("14")]),
41            ],
42        ),
43        titled_card(
44            "Danger zone",
45            [row([
46                column([
47                    text("Delete account").bold(),
48                    text("Permanently remove your account and all data.")
49                        .muted()
50                        .small(),
51                ])
52                .gap(tokens::SPACE_1)
53                .align(Align::Start)
54                .width(Size::Hug),
55                spacer(),
56                button("Delete").destructive(),
57            ])],
58        ),
59        row([spacer(), button("Cancel").ghost(), button("Save").primary()]),
60    ])
61    .gap(tokens::SPACE_4)
62    .padding(tokens::SPACE_7)
63}
Source

pub fn xsmall(self) -> Self

Source

pub fn color(self, c: Color) -> Self

Set an explicit text color.

Examples found in repository?
examples/settings_calibration.rs (line 158)
134fn settings_nav_item(label: &'static str, selected: bool) -> El {
135    let mut item = row([
136        El::new(Kind::Custom("nav-dot"))
137            .fill(tokens::MUTED_FOREGROUND)
138            .radius(tokens::RADIUS_PILL)
139            .width(Size::Fixed(6.0))
140            .height(Size::Fixed(6.0)),
141        text(label)
142            .font_weight(FontWeight::Medium)
143            .ellipsis()
144            .width(Size::Fill(1.0)),
145    ])
146    .key(if selected {
147        "metric:settings.nav.row".to_string()
148    } else {
149        format!("settings-nav-{label}")
150    })
151    .metrics_role(MetricsRole::ListItem)
152    .align(Align::Center)
153    .focusable();
154
155    if selected {
156        item = item.current();
157    } else {
158        item = item.color(tokens::MUTED_FOREGROUND);
159    }
160
161    item
162}
163
164fn settings_body() -> El {
165    column([
166        column([
167            h1("Account").heading().key("metric:section.title"),
168            text("Manage identity, workspace defaults, and security preferences.")
169                .muted()
170                .wrap_text()
171                .key("metric:page.subtitle"),
172        ])
173        .gap(tokens::SPACE_1)
174        .height(Size::Hug),
175        scroll([profile_card(), preferences_card()])
176            .key("settings-body-scroll")
177            .gap(tokens::SPACE_4)
178            .width(Size::Fill(1.0))
179            .height(Size::Fill(1.0)),
180    ])
181    .gap(tokens::SPACE_4)
182    .width(Size::Fill(1.0))
183    .height(Size::Fill(1.0))
184}
185
186fn profile_card() -> El {
187    card([
188        card_header([
189            card_title("Profile"),
190            card_description("This information appears in audit logs and shared documents."),
191        ]),
192        card_content([form([
193            row([
194                setting_field("Display name", "Alicia Koch", "display-name"),
195                setting_field("Email", "alicia@acme.co", "email"),
196            ])
197            .gap(tokens::SPACE_3),
198            row([
199                setting_select("Role", "Workspace admin", "role"),
200                setting_select("Region", "US East", "region"),
201            ])
202            .gap(tokens::SPACE_3),
203        ])]),
204    ])
205    .key("metric:profile.card")
206}
207
208fn setting_field(label: &'static str, value: &'static str, key: &'static str) -> El {
209    form_item([
210        form_label(label),
211        form_control(
212            text_input(value, &Selection::caret(key, value.len()), key).key(
213                if key == "display-name" {
214                    "metric:form.input"
215                } else {
216                    key
217                },
218            ),
219        ),
220    ])
221    .width(Size::Fill(1.0))
222}
223
224fn setting_select(label: &'static str, value: &'static str, key: &'static str) -> El {
225    form_item([form_label(label), form_control(select_trigger(key, value))]).width(Size::Fill(1.0))
226}
227
228fn preferences_card() -> El {
229    card([
230        card_header([
231            card_title("Preferences"),
232            card_description("Defaults used when creating new dashboards and exports."),
233        ]),
234        card_content([column([
235            preference_row(
236                "Compact navigation",
237                "Use tighter rows in the sidebar and command menus.",
238                switch(true).key("compact-navigation"),
239            ),
240            divider(),
241            preference_row(
242                "Email summaries",
243                "Send a daily digest when documents change.",
244                switch(false).key("email-summaries"),
245            ),
246            divider(),
247            preference_row(
248                "Require approval",
249                "Route external sharing through an owner review.",
250                checkbox(true).key("approval-required"),
251            ),
252        ])
253        .gap(0.0)
254        .width(Size::Fill(1.0))])
255        .padding(0.0),
256    ])
257    .key("metric:preferences.card")
258}
259
260fn preference_row(title: &'static str, description: &'static str, control: El) -> El {
261    row([
262        column([
263            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
264            text(description)
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0))
271        .height(Size::Hug),
272        control,
273    ])
274    .key(if title == "Compact navigation" {
275        "metric:preference.row".to_string()
276    } else {
277        format!("preference-{title}")
278    })
279    .metrics_role(MetricsRole::PreferenceRow)
280    .gap(tokens::SPACE_4)
281    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_3))
282    .align(Align::Center)
283}
284
285fn settings_aside() -> El {
286    column([security_card(), scale_card()])
287        .gap(tokens::SPACE_4)
288        .width(Size::Fixed(300.0))
289        .height(Size::Fill(1.0))
290}
291
292fn security_card() -> El {
293    card([
294        card_header([
295            card_title("Security"),
296            card_description("Two-factor authentication is enabled for all privileged users."),
297        ]),
298        card_content([
299            compact_stat("Passkeys", "2 registered", badge("On").success()),
300            compact_stat("Sessions", "3 active", button("Review").secondary()),
301        ]),
302    ])
303    .width(Size::Fill(1.0))
304}
305
306fn scale_card() -> El {
307    card([
308        card_header([
309            card_title("Interface scale"),
310            card_description("Reference captures keep browser zoom fixed and vary root UI scale."),
311        ]),
312        card_content([
313            row([text("Dense").caption(), spacer(), text("Default").caption()]),
314            slider(0.66, tokens::PRIMARY)
315                .key("interface-scale")
316                .width(Size::Fill(1.0)),
317        ]),
318    ])
319    .width(Size::Fill(1.0))
320}
321
322fn compact_stat(title: &'static str, detail: &'static str, control: El) -> El {
323    row([
324        column([
325            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
326            text(detail).caption().ellipsis().width(Size::Fill(1.0)),
327        ])
328        .gap(2.0)
329        .width(Size::Fill(1.0))
330        .height(Size::Hug),
331        control,
332    ])
333    .gap(tokens::SPACE_2)
334    .height(Size::Fixed(44.0))
335    .align(Align::Center)
336}
337
338fn section_label(label: &'static str) -> El {
339    text(label)
340        .caption()
341        .height(Size::Fixed(22.0))
342        .padding(Sides::xy(tokens::SPACE_2, 0.0))
343}
344
345fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
346    let mut item = row([
347        icon(icon_name)
348            .color(tokens::MUTED_FOREGROUND)
349            .icon_size(tokens::ICON_SM)
350            .width(Size::Fixed(tokens::ICON_SM)),
351        text(label)
352            .font_weight(FontWeight::Medium)
353            .ellipsis()
354            .width(Size::Fill(1.0)),
355    ])
356    .key(if selected {
357        "metric:sidebar.nav.row".to_string()
358    } else {
359        format!("side-item-{label}")
360    })
361    .metrics_role(MetricsRole::ListItem)
362    .gap(tokens::SPACE_2)
363    .padding(Sides::xy(tokens::SPACE_2, 0.0))
364    .height(Size::Fixed(32.0))
365    .align(Align::Center)
366    .focusable();
367
368    if selected {
369        item = item.current();
370    } else {
371        item = item.color(tokens::MUTED_FOREGROUND);
372    }
373
374    item
375}
376
377fn icon_slot(icon_name: &'static str) -> El {
378    El::new(Kind::Custom("icon_cell"))
379        .style_profile(StyleProfile::Surface)
380        .child(
381            icon(icon_name)
382                .color(tokens::FOREGROUND)
383                .icon_size(tokens::ICON_XS),
384        )
385        .align(Align::Center)
386        .justify(Justify::Center)
387        .fill(tokens::MUTED)
388        .stroke(tokens::BORDER)
389        .radius(tokens::RADIUS_SM)
390        .width(Size::Fixed(30.0))
391        .height(Size::Fixed(30.0))
392}
More examples
Hide additional examples
examples/dashboard_01_calibration.rs (line 112)
109fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
110    let mut item = row([
111        icon(icon_name)
112            .color(tokens::MUTED_FOREGROUND)
113            .icon_size(tokens::ICON_SM)
114            .width(Size::Fixed(tokens::ICON_SM)),
115        text(label)
116            .font_weight(FontWeight::Medium)
117            .ellipsis()
118            .width(Size::Fill(1.0)),
119    ])
120    .key(if selected {
121        "metric:sidebar.nav.row".to_string()
122    } else {
123        format!("side-item-{label}")
124    })
125    .metrics_role(MetricsRole::ListItem)
126    .gap(tokens::SPACE_2)
127    .padding(Sides::xy(tokens::SPACE_2, 0.0))
128    .height(Size::Fixed(32.0))
129    .align(Align::Center)
130    .focusable();
131
132    if selected {
133        item = item.current();
134    } else {
135        item = item.color(tokens::MUTED_FOREGROUND);
136    }
137
138    item
139}
140
141fn dashboard_main() -> El {
142    column([
143        dashboard_header(),
144        column([
145            row([
146                metric_card(
147                    "bar-chart",
148                    "Total Revenue",
149                    "$1,250.00",
150                    "+12.5%",
151                    "Trending up this month",
152                    true,
153                ),
154                metric_card(
155                    "users",
156                    "New Customers",
157                    "1,234",
158                    "-20%",
159                    "Acquisition needs attention",
160                    false,
161                ),
162                metric_card(
163                    "folder",
164                    "Active Accounts",
165                    "45,678",
166                    "+12.5%",
167                    "Strong user retention",
168                    true,
169                ),
170                metric_card(
171                    "activity",
172                    "Growth Rate",
173                    "4.5%",
174                    "+4.5%",
175                    "Meets growth projections",
176                    true,
177                ),
178            ])
179            .gap(tokens::SPACE_4),
180            row([chart_card(), sales_card()])
181                .gap(tokens::SPACE_4)
182                .height(Size::Fixed(306.0))
183                .align(Align::Stretch),
184            documents_card(),
185        ])
186        .gap(tokens::SPACE_4)
187        .padding(tokens::SPACE_7)
188        .height(Size::Fill(1.0)),
189    ])
190    .width(Size::Fill(1.0))
191    .height(Size::Fill(1.0))
192}
193
194fn dashboard_header() -> El {
195    row([
196        icon_button("menu").ghost(),
197        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
198        h3("Documents").key("metric:page.title"),
199        spacer(),
200        text_input("Search...", &Selection::default(), "dashboard-search")
201            .key("metric:command.input")
202            .width(Size::Fixed(260.0)),
203        icon_button("plus").ghost(),
204        icon_button("bell").ghost(),
205    ])
206    .key("metric:header")
207    .gap(tokens::SPACE_3)
208    .height(Size::Fixed(56.0))
209    .padding(Sides::xy(tokens::SPACE_4, 0.0))
210    .align(Align::Center)
211    .stroke(tokens::BORDER)
212}
213
214fn metric_card(
215    icon_name: &'static str,
216    title: &'static str,
217    value: &'static str,
218    delta: &'static str,
219    note: &'static str,
220    positive: bool,
221) -> El {
222    let badge = if positive {
223        badge(delta).success()
224    } else {
225        badge(delta).warning()
226    };
227    let badge = if title == "Total Revenue" {
228        badge.key("metric:kpi.badge")
229    } else {
230        badge
231    };
232    let value = if title == "Total Revenue" {
233        h2(value).ellipsis().key("metric:kpi.value")
234    } else {
235        h2(value).ellipsis()
236    };
237    card([card_content([
238        row([
239            row([
240                icon(icon_name)
241                    .color(tokens::MUTED_FOREGROUND)
242                    .icon_size(tokens::ICON_XS),
243                text(title).muted().ellipsis().width(Size::Fill(1.0)),
244            ])
245            .gap(tokens::SPACE_1)
246            .width(Size::Fill(1.0))
247            .align(Align::Center),
248            badge,
249        ])
250        .gap(tokens::SPACE_2)
251        .align(Align::Center),
252        value,
253        text(note).caption().ellipsis().width(Size::Fill(1.0)),
254    ])
255    .padding(tokens::SPACE_4)
256    .gap(tokens::SPACE_2)])
257    .key(if title == "Total Revenue" {
258        "metric:kpi.card"
259    } else {
260        title
261    })
262    .width(Size::Fill(1.0))
263}
264
265fn chart_card() -> El {
266    card([
267        card_header([
268            card_title("Visitors for the last 6 months"),
269            card_description("Total visitors by channel."),
270        ])
271        .padding(tokens::SPACE_4),
272        card_content([row(chart_bars())
273            .gap(2.0)
274            .height(Size::Fill(1.0))
275            .align(Align::End)])
276        .padding(Sides {
277            left: tokens::SPACE_4,
278            right: tokens::SPACE_4,
279            top: 0.0,
280            bottom: tokens::SPACE_4,
281        })
282        .height(Size::Fill(1.0)),
283    ])
284    .key("metric:chart.card")
285    .width(Size::Fill(1.0))
286    .height(Size::Fill(1.0))
287}
288
289fn chart_bars() -> Vec<El> {
290    [
291        48.0, 72.0, 56.0, 90.0, 64.0, 80.0, 108.0, 84.0, 122.0, 96.0, 136.0, 118.0,
292    ]
293    .into_iter()
294    .flat_map(|height| {
295        [
296            bar(height, tokens::MUTED_FOREGROUND),
297            bar((height - 28.0_f32).max(24.0), tokens::INPUT),
298        ]
299    })
300    .collect()
301}
302
303fn bar(height: f32, color: Color) -> El {
304    El::new(Kind::Custom("chart_bar"))
305        .fill(color)
306        .radius(tokens::RADIUS_SM)
307        .width(Size::Fill(1.0))
308        .height(Size::Fixed(height))
309}
310
311fn sales_card() -> El {
312    card([
313        card_header([
314            card_title("Recent Sales"),
315            card_description("You made 265 sales this month."),
316        ])
317        .padding(tokens::SPACE_4),
318        card_content([
319            sale_row("OM", "Olivia Martin", "olivia@example.com", "+$1,999.00"),
320            sale_row("JL", "Jackson Lee", "jackson@example.com", "+$39.00"),
321            sale_row("IN", "Isabella Nguyen", "isabella@example.com", "+$299.00"),
322            sale_row("WK", "William Kim", "will@example.com", "+$99.00"),
323        ])
324        .gap(tokens::SPACE_2)
325        .padding(Sides {
326            left: tokens::SPACE_4,
327            right: tokens::SPACE_4,
328            top: 0.0,
329            bottom: tokens::SPACE_4,
330        }),
331    ])
332    .key("metric:sales.card")
333    .width(Size::Fixed(330.0))
334    .height(Size::Fill(1.0))
335}
336
337fn sale_row(
338    initials: &'static str,
339    name: &'static str,
340    email: &'static str,
341    amount: &'static str,
342) -> El {
343    row([
344        icon_cell(initials),
345        column([
346            text(name).semibold().ellipsis().width(Size::Fill(1.0)),
347            text(email).caption().ellipsis().width(Size::Fill(1.0)),
348        ])
349        .gap(2.0)
350        .height(Size::Hug)
351        .width(Size::Fill(1.0)),
352        text(amount).label().small(),
353    ])
354    .gap(tokens::SPACE_2)
355    .height(Size::Fixed(42.0))
356    .align(Align::Center)
357}
358
359fn documents_card() -> El {
360    card([
361        card_header([card_title("Documents")]).padding(tokens::SPACE_4),
362        card_content([scroll([table([
363            table_header([table_row([
364                table_head("").width(Size::Fixed(35.0)),
365                table_head("Header").width(Size::Fill(1.8)),
366                table_head("Section Type").width(Size::Fill(1.0)),
367                table_head("Status").width(Size::Fixed(104.0)),
368                table_head("Target").width(Size::Fixed(64.0)),
369                table_head("Limit").width(Size::Fixed(64.0)),
370                table_head("Reviewer").width(Size::Fixed(128.0)),
371                table_head("").width(Size::Fixed(32.0)),
372            ])
373            .padding(Sides::xy(tokens::SPACE_4, 0.0))
374            .key("metric:table.header")]),
375            divider(),
376            table_body([
377                document_row(
378                    "Cover page",
379                    "Cover page",
380                    "In Process",
381                    "18",
382                    "5",
383                    "Eddie Lake",
384                    "info",
385                ),
386                document_row(
387                    "Table of contents",
388                    "Table of contents",
389                    "Done",
390                    "29",
391                    "24",
392                    "Eddie Lake",
393                    "success",
394                ),
395            ]),
396        ])])
397        .height(Size::Fill(1.0))])
398        .gap(0.0)
399        .padding(0.0)
400        .height(Size::Fill(1.0)),
401    ])
402    .key("metric:table.card")
403    .height(Size::Fill(1.0))
404}
405
406fn document_row(
407    header: &'static str,
408    section: &'static str,
409    status: &'static str,
410    target: &'static str,
411    limit: &'static str,
412    reviewer: &'static str,
413    tone: &'static str,
414) -> El {
415    let status_badge = match tone {
416        "success" => badge(status).success(),
417        _ => badge(status).info(),
418    };
419    table_row([
420        table_utility_cell("::"),
421        table_cell(text(header).label().small()).width(Size::Fill(1.8)),
422        table_cell(text(section).muted()).width(Size::Fill(1.0)),
423        table_cell(status_badge).width(Size::Fixed(104.0)),
424        table_cell(text(target).label().small()).width(Size::Fixed(64.0)),
425        table_cell(text(limit).label().small()).width(Size::Fixed(64.0)),
426        table_cell(text(reviewer).muted()).width(Size::Fixed(128.0)),
427        table_action_cell(),
428    ])
429    .padding(Sides::xy(tokens::SPACE_4, 0.0))
430    .key(if header == "Cover page" {
431        "metric:table.row"
432    } else {
433        header
434    })
435}
436
437fn table_utility_cell(label: &'static str) -> El {
438    table_cell(text(label).muted().center_text()).width(Size::Fixed(35.0))
439}
440
441fn table_action_cell() -> El {
442    stack([icon("more-horizontal")
443        .icon_size(tokens::ICON_SM)
444        .color(tokens::MUTED_FOREGROUND)])
445    .align(Align::Center)
446    .justify(Justify::Center)
447    .width(Size::Fixed(32.0))
448    .height(Size::Hug)
449}
examples/inline_runs.rs (line 27)
18fn fixture() -> El {
19    column([
20        h2("Inline runs"),
21        text_runs([
22            text("Aetna's attributed-text path lets you compose runs with "),
23            text("bold").bold(),
24            text(", "),
25            text("italic").italic(),
26            text(", "),
27            text("colored").color(tokens::DESTRUCTIVE),
28            text(", and "),
29            text("inline code").code(),
30            text(" segments inside one wrapping paragraph."),
31            hard_break(),
32            text("Hard breaks act like ").muted(),
33            text("<br>").code(),
34            text(" — they end the current line without breaking out of the run.").muted(),
35        ])
36        .wrap_text()
37        .width(Size::Fill(1.0))
38        .height(Size::Hug),
39        paragraph(
40            "All of the above flows through one cosmic-text rich-text shape — \
41             wrapping decisions cross run boundaries the way real prose wraps.",
42        )
43        .muted(),
44    ])
45    .gap(tokens::SPACE_4)
46    .padding(tokens::SPACE_7)
47    .width(Size::Fixed(640.0))
48}
Source§

impl El

Source

pub fn text(self, t: impl Into<String>) -> Self

Examples found in repository?
examples/dashboard_01_calibration.rs (line 454)
451fn icon_cell(label: &'static str) -> El {
452    El::new(Kind::Custom("icon_cell"))
453        .style_profile(StyleProfile::Surface)
454        .text(label)
455        .text_align(TextAlign::Center)
456        .caption()
457        .font_weight(FontWeight::Semibold)
458        .fill(tokens::MUTED)
459        .radius(tokens::RADIUS_SM)
460        .width(Size::Fixed(30.0))
461        .height(Size::Fixed(30.0))
462}
More examples
Hide additional examples
examples/polish_calibration.rs (line 358)
355fn icon_cell(label: &'static str) -> El {
356    El::new(Kind::Custom("icon_cell"))
357        .style_profile(StyleProfile::Surface)
358        .text(label)
359        .text_align(TextAlign::Center)
360        .caption()
361        .font_weight(FontWeight::Semibold)
362        .fill(tokens::MUTED)
363        .stroke(tokens::BORDER)
364        .radius(tokens::RADIUS_SM)
365        .width(Size::Fixed(26.0))
366        .height(Size::Fixed(26.0))
367}
Source

pub fn text_color(self, c: Color) -> Self

Examples found in repository?
examples/custom_shader.rs (line 23)
22fn gradient_button(label: &str, top: Color, bottom: Color, radius: f32) -> El {
23    button(label).text_color(tokens::PRIMARY_FOREGROUND).shader(
24        ShaderBinding::custom("gradient")
25            .color("vec_a", top)
26            .color("vec_b", bottom)
27            .f32("vec_c", radius),
28    )
29}
Source

pub fn text_align(self, align: TextAlign) -> Self

Examples found in repository?
examples/dashboard_01_calibration.rs (line 455)
451fn icon_cell(label: &'static str) -> El {
452    El::new(Kind::Custom("icon_cell"))
453        .style_profile(StyleProfile::Surface)
454        .text(label)
455        .text_align(TextAlign::Center)
456        .caption()
457        .font_weight(FontWeight::Semibold)
458        .fill(tokens::MUTED)
459        .radius(tokens::RADIUS_SM)
460        .width(Size::Fixed(30.0))
461        .height(Size::Fixed(30.0))
462}
More examples
Hide additional examples
examples/polish_calibration.rs (line 359)
355fn icon_cell(label: &'static str) -> El {
356    El::new(Kind::Custom("icon_cell"))
357        .style_profile(StyleProfile::Surface)
358        .text(label)
359        .text_align(TextAlign::Center)
360        .caption()
361        .font_weight(FontWeight::Semibold)
362        .fill(tokens::MUTED)
363        .stroke(tokens::BORDER)
364        .radius(tokens::RADIUS_SM)
365        .width(Size::Fixed(26.0))
366        .height(Size::Fixed(26.0))
367}
Source

pub fn center_text(self) -> Self

Examples found in repository?
examples/dashboard_01_calibration.rs (line 438)
437fn table_utility_cell(label: &'static str) -> El {
438    table_cell(text(label).muted().center_text()).width(Size::Fixed(35.0))
439}
More examples
Hide additional examples
examples/circular_layout.rs (line 49)
48fn fixture() -> El {
49    let centre = h2("Compass").center_text();
50    let dirs = [
51        ("North", "n"),
52        ("NE", "ne"),
53        ("East", "e"),
54        ("SE", "se"),
55        ("South", "s"),
56        ("SW", "sw"),
57        ("West", "w"),
58        ("NW", "nw"),
59    ];
60
61    let mut children: Vec<El> = vec![centre];
62    for (label, k) in dirs {
63        children.push(button(label).key(k).primary());
64    }
65
66    column([
67        h1("Custom layout — circular"),
68        paragraph(
69            "Eight buttons positioned on a circle by an author-supplied \
70             LayoutFn. Stock paint, automatic hover/press, and hit-test \
71             all keep working — only the rect distribution changed.",
72        )
73        .muted(),
74        stack(children)
75            .key("compass")
76            .layout(circular)
77            .width(Size::Fill(1.0))
78            .height(Size::Fixed(360.0)),
79    ])
80    .gap(tokens::SPACE_4)
81    .padding(tokens::SPACE_7)
82}
Source

pub fn end_text(self) -> Self

Source

pub fn text_wrap(self, wrap: TextWrap) -> Self

Source

pub fn wrap_text(self) -> Self

Examples found in repository?
examples/inline_runs.rs (line 36)
18fn fixture() -> El {
19    column([
20        h2("Inline runs"),
21        text_runs([
22            text("Aetna's attributed-text path lets you compose runs with "),
23            text("bold").bold(),
24            text(", "),
25            text("italic").italic(),
26            text(", "),
27            text("colored").color(tokens::DESTRUCTIVE),
28            text(", and "),
29            text("inline code").code(),
30            text(" segments inside one wrapping paragraph."),
31            hard_break(),
32            text("Hard breaks act like ").muted(),
33            text("<br>").code(),
34            text(" — they end the current line without breaking out of the run.").muted(),
35        ])
36        .wrap_text()
37        .width(Size::Fill(1.0))
38        .height(Size::Hug),
39        paragraph(
40            "All of the above flows through one cosmic-text rich-text shape — \
41             wrapping decisions cross run boundaries the way real prose wraps.",
42        )
43        .muted(),
44    ])
45    .gap(tokens::SPACE_4)
46    .padding(tokens::SPACE_7)
47    .width(Size::Fixed(640.0))
48}
More examples
Hide additional examples
examples/settings_calibration.rs (line 70)
41fn settings_sidebar() -> El {
42    column([
43        row([
44            icon_slot("settings"),
45            column([
46                text("Workspace")
47                    .semibold()
48                    .ellipsis()
49                    .width(Size::Fill(1.0)),
50                text("Settings").caption().ellipsis().width(Size::Fill(1.0)),
51            ])
52            .gap(2.0)
53            .width(Size::Fill(1.0))
54            .height(Size::Hug),
55        ])
56        .gap(tokens::SPACE_2)
57        .height(Size::Fixed(44.0))
58        .align(Align::Center),
59        section_label("Personal"),
60        side_item("users", "Profile", false),
61        side_item("settings", "Account", true),
62        side_item("alert-circle", "Security", false),
63        side_item("bell", "Notifications", false),
64        spacer().height(Size::Fixed(tokens::SPACE_4)),
65        section_label("Workspace"),
66        side_item("file-text", "Billing", false),
67        side_item("bar-chart", "Appearance", false),
68        side_item("activity", "Integrations", false),
69        spacer(),
70        column([text("Changes sync after save.").caption().wrap_text()])
71            .padding(tokens::SPACE_2)
72            .fill(tokens::MUTED)
73            .radius(tokens::RADIUS_MD),
74    ])
75    .gap(tokens::SPACE_2)
76    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_2))
77    .key("metric:sidebar")
78    .width(Size::Fixed(244.0))
79    .height(Size::Fill(1.0))
80    .fill(tokens::CARD)
81    .stroke(tokens::BORDER)
82}
83
84fn settings_main() -> El {
85    column([
86        settings_header(),
87        row([settings_nav_card(), settings_body(), settings_aside()])
88            .gap(tokens::SPACE_4)
89            .padding(tokens::SPACE_4)
90            .height(Size::Fill(1.0))
91            .align(Align::Stretch),
92    ])
93    .width(Size::Fill(1.0))
94    .height(Size::Fill(1.0))
95}
96
97fn settings_header() -> El {
98    row([
99        icon_button("menu").ghost(),
100        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
101        h3("Settings").key("metric:page.title"),
102        spacer(),
103        button("Reset").secondary(),
104        button("Save changes").primary(),
105    ])
106    .key("metric:header")
107    .gap(tokens::SPACE_3)
108    .height(Size::Fixed(56.0))
109    .padding(Sides::xy(tokens::SPACE_4, 0.0))
110    .align(Align::Center)
111    .stroke(tokens::BORDER)
112}
113
114fn settings_nav_card() -> El {
115    column([
116        settings_nav_item("Account", true),
117        settings_nav_item("Security", false),
118        settings_nav_item("Notifications", false),
119        settings_nav_item("Appearance", false),
120        settings_nav_item("Billing", false),
121    ])
122    .gap(tokens::SPACE_1)
123    .padding(tokens::SPACE_1)
124    .width(Size::Fixed(220.0))
125    .height(Size::Fill(1.0))
126    .style_profile(StyleProfile::Surface)
127    .surface_role(SurfaceRole::Panel)
128    .fill(tokens::CARD)
129    .stroke(tokens::BORDER)
130    .radius(tokens::RADIUS_MD)
131    .shadow(tokens::SHADOW_MD)
132}
133
134fn settings_nav_item(label: &'static str, selected: bool) -> El {
135    let mut item = row([
136        El::new(Kind::Custom("nav-dot"))
137            .fill(tokens::MUTED_FOREGROUND)
138            .radius(tokens::RADIUS_PILL)
139            .width(Size::Fixed(6.0))
140            .height(Size::Fixed(6.0)),
141        text(label)
142            .font_weight(FontWeight::Medium)
143            .ellipsis()
144            .width(Size::Fill(1.0)),
145    ])
146    .key(if selected {
147        "metric:settings.nav.row".to_string()
148    } else {
149        format!("settings-nav-{label}")
150    })
151    .metrics_role(MetricsRole::ListItem)
152    .align(Align::Center)
153    .focusable();
154
155    if selected {
156        item = item.current();
157    } else {
158        item = item.color(tokens::MUTED_FOREGROUND);
159    }
160
161    item
162}
163
164fn settings_body() -> El {
165    column([
166        column([
167            h1("Account").heading().key("metric:section.title"),
168            text("Manage identity, workspace defaults, and security preferences.")
169                .muted()
170                .wrap_text()
171                .key("metric:page.subtitle"),
172        ])
173        .gap(tokens::SPACE_1)
174        .height(Size::Hug),
175        scroll([profile_card(), preferences_card()])
176            .key("settings-body-scroll")
177            .gap(tokens::SPACE_4)
178            .width(Size::Fill(1.0))
179            .height(Size::Fill(1.0)),
180    ])
181    .gap(tokens::SPACE_4)
182    .width(Size::Fill(1.0))
183    .height(Size::Fill(1.0))
184}
examples/inline_runs_highlight.rs (line 43)
26fn fixture() -> El {
27    column([
28        h2("Inline run backgrounds"),
29        paragraph(
30            "RunStyle.bg paints a per-line solid quad behind the glyphs of \
31             a styled span — the shaper computes the rect from the actual \
32             glyph extents, so wrapping splits the highlight cleanly.",
33        )
34        .muted(),
35        // Search-result style.
36        text_runs([
37            text("…the matcher finds "),
38            text("aetna").background(HIGHLIGHT_YELLOW).bold(),
39            text(" in "),
40            text("aetna_core::widgets").mono(),
41            text(" — the highlight tracks the glyph extent."),
42        ])
43        .wrap_text()
44        .width(Size::Fill(1.0))
45        .height(Size::Hug),
46        // Diff-style: add + remove tints inside the same line.
47        text_runs([
48            text("- "),
49            text("error::Custom").mono().background(DIFF_REMOVE),
50            text("(\"too narrow\")"),
51            hard_break(),
52            text("+ "),
53            text("error::WrapTooNarrow").mono().background(DIFF_ADD),
54            text(" { available }"),
55        ])
56        .wrap_text()
57        .width(Size::Fill(1.0))
58        .height(Size::Hug),
59        // Wrapping highlight: long span that spans two lines.
60        text_runs([
61            text("Long highlight: "),
62            text("the quick brown fox jumps over the lazy dog and keeps going")
63                .background(HIGHLIGHT_YELLOW),
64            text(" — the rect is split per line."),
65        ])
66        .wrap_text()
67        .width(Size::Fill(1.0))
68        .height(Size::Hug),
69    ])
70    .gap(tokens::SPACE_4)
71    .padding(tokens::SPACE_7)
72    .width(Size::Fixed(640.0))
73}
Source

pub fn nowrap_text(self) -> Self

Examples found in repository?
examples/palette_demo.rs (line 296)
282fn token_chip(token: TokenDef, palette: &Palette) -> El {
283    let resolved = palette.resolve(token.color);
284    row([
285        El::new(Kind::Custom("palette-swatch"))
286            .at(file!(), line!())
287            .fill(token.color)
288            .stroke(tokens::BORDER)
289            .radius(tokens::RADIUS_SM)
290            .width(Size::Fixed(42.0))
291            .height(Size::Fixed(34.0)),
292        column([
293            text(token.name)
294                .label()
295                .ellipsis()
296                .nowrap_text()
297                .width(Size::Fill(1.0)),
298            mono(rgba_label(resolved)).caption().muted(),
299        ])
300        .gap(0.0)
301        .width(Size::Fill(1.0))
302        .height(Size::Hug),
303    ])
304    .gap(tokens::SPACE_2)
305    .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_2))
306    .align(Align::Center)
307    .fill(tokens::CARD)
308    .stroke(tokens::BORDER)
309    .radius(tokens::RADIUS_MD)
310    .width(Size::Fill(1.0))
311    .height(Size::Fixed(54.0))
312}
Source

pub fn text_overflow(self, overflow: TextOverflow) -> Self

Source

pub fn ellipsis(self) -> Self

Examples found in repository?
examples/polish_calibration.rs (line 72)
67fn nav_item(icon: &'static str, label: &'static str, selected: bool) -> El {
68    let mut item = row([
69        icon_cell(icon),
70        text(label)
71            .font_weight(FontWeight::Medium)
72            .ellipsis()
73            .width(Size::Fill(1.0)),
74    ])
75    .key(if selected {
76        "metric:sidebar.nav.row".to_string()
77    } else {
78        format!("nav-{label}")
79    })
80    .metrics_role(MetricsRole::ListItem)
81    .gap(tokens::SPACE_3)
82    .padding(Sides::xy(tokens::SPACE_2, 0.0))
83    .height(Size::Fixed(40.0))
84    .align(Align::Center)
85    .focusable();
86
87    if selected {
88        item = item.current();
89    }
90
91    item
92}
93
94fn main_panel() -> El {
95    column([
96        toolbar(),
97        column([
98            row([
99                kpi_card("Latency", "42 ms", "-18%", true),
100                kpi_card("Runs", "1,284", "+12%", true),
101                kpi_card("Errors", "7", "+2", false),
102            ])
103            .gap(tokens::SPACE_4),
104            row([table_card(), command_card()])
105                .gap(tokens::SPACE_4)
106                .height(Size::Fill(1.0))
107                .align(Align::Stretch),
108        ])
109        .gap(tokens::SPACE_4)
110        .height(Size::Fill(1.0))
111        .align(Align::Stretch),
112    ])
113    .padding(tokens::SPACE_7)
114    .gap(tokens::SPACE_2)
115    .width(Size::Fill(1.0))
116    .height(Size::Fill(1.0))
117}
118
119fn toolbar() -> El {
120    row([
121        column([
122            h1("Polish calibration").key("metric:page.title"),
123            text("A representative app surface for default tuning.")
124                .muted()
125                .key("metric:page.subtitle"),
126        ])
127        .gap(tokens::SPACE_2)
128        .height(Size::Hug),
129        spacer(),
130        button_with_icon("search", "Preview")
131            .secondary()
132            .key("metric:action.secondary"),
133        button_with_icon("upload", "Publish")
134            .primary()
135            .key("metric:action.primary"),
136    ])
137    .key("metric:header")
138    .gap(tokens::SPACE_4)
139    .height(Size::Hug)
140    .align(Align::Start)
141}
142
143fn kpi_card(label: &'static str, value: &'static str, delta: &'static str, positive: bool) -> El {
144    let delta_badge = if positive {
145        badge(delta).success()
146    } else {
147        badge(delta).destructive()
148    };
149    let delta_badge = if label == "Latency" {
150        delta_badge.key("metric:kpi.badge")
151    } else {
152        delta_badge
153    };
154    let value_text = h2(value).display();
155    let value_text = if label == "Latency" {
156        value_text.key("metric:kpi.value")
157    } else {
158        value_text
159    };
160    card([
161        card_header([card_title(label)]),
162        card_content([
163            row([value_text, spacer(), delta_badge]).align(Align::Center),
164            text(if positive {
165                "Moving in the expected direction"
166            } else {
167                "Needs visual attention"
168            })
169            .muted(),
170        ])
171        .gap(tokens::SPACE_6),
172    ])
173    .key(if label == "Latency" {
174        "metric:kpi.card"
175    } else {
176        label
177    })
178    .width(Size::Fill(1.0))
179}
180
181fn table_card() -> El {
182    card([
183        card_header([card_title("Reference rows")]),
184        card_content([table([
185            table_header([table_row([
186                table_head("Status").width(Size::Fixed(86.0)),
187                table_head("Surface").width(Size::Fill(1.0)),
188                table_head("Owner").width(Size::Fixed(110.0)),
189                table_head("State").width(Size::Fixed(86.0)),
190            ])
191            .key("metric:table.header")]),
192            divider(),
193            table_body([
194                data_row("OK", "Settings card", "core", "selected", true, "success"),
195                data_row(
196                    "WARN",
197                    "Command palette density",
198                    "widgets",
199                    "needs work",
200                    false,
201                    "warning",
202                ),
203                data_row(
204                    "ERR",
205                    "Disabled and invalid states",
206                    "style",
207                    "missing",
208                    false,
209                    "destructive",
210                ),
211                data_row(
212                    "INFO",
213                    "Token resolution",
214                    "theme",
215                    "planned",
216                    false,
217                    "info",
218                ),
219                data_row(
220                    "OK",
221                    "Popover elevation",
222                    "shader",
223                    "queued",
224                    false,
225                    "success",
226                ),
227            ])
228            .gap(tokens::SPACE_1)
229            .width(Size::Fill(1.0)),
230        ])]),
231    ])
232    .key("metric:table.card")
233    .width(Size::Fill(1.2))
234    .height(Size::Fill(1.0))
235}
236
237fn data_row(
238    status: &'static str,
239    title: &'static str,
240    owner: &'static str,
241    state: &'static str,
242    selected: bool,
243    tone: &'static str,
244) -> El {
245    let status_badge = match tone {
246        "success" => badge(status).success(),
247        "warning" => badge(status).warning(),
248        "destructive" => badge(status).destructive(),
249        _ => badge(status).info(),
250    };
251    let status_badge = if selected {
252        status_badge.key("metric:table.badge")
253    } else {
254        status_badge
255    };
256
257    let mut row = table_row([
258        table_cell(status_badge).width(Size::Fixed(70.0)),
259        column([
260            text(title)
261                .font_weight(FontWeight::Medium)
262                .ellipsis()
263                .width(Size::Fill(1.0)),
264            text("Default styling probe.")
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0)),
271        table_cell(text(owner).muted()).width(Size::Fixed(110.0)),
272        table_cell(text(state).label().small()).width(Size::Fixed(86.0)),
273    ])
274    .key(if selected {
275        "metric:table.row".to_string()
276    } else {
277        format!("row-{title}")
278    })
279    .focusable();
280
281    if selected {
282        row = row.selected();
283    }
284
285    row
286}
More examples
Hide additional examples
examples/palette_demo.rs (line 295)
282fn token_chip(token: TokenDef, palette: &Palette) -> El {
283    let resolved = palette.resolve(token.color);
284    row([
285        El::new(Kind::Custom("palette-swatch"))
286            .at(file!(), line!())
287            .fill(token.color)
288            .stroke(tokens::BORDER)
289            .radius(tokens::RADIUS_SM)
290            .width(Size::Fixed(42.0))
291            .height(Size::Fixed(34.0)),
292        column([
293            text(token.name)
294                .label()
295                .ellipsis()
296                .nowrap_text()
297                .width(Size::Fill(1.0)),
298            mono(rgba_label(resolved)).caption().muted(),
299        ])
300        .gap(0.0)
301        .width(Size::Fill(1.0))
302        .height(Size::Hug),
303    ])
304    .gap(tokens::SPACE_2)
305    .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_2))
306    .align(Align::Center)
307    .fill(tokens::CARD)
308    .stroke(tokens::BORDER)
309    .radius(tokens::RADIUS_MD)
310    .width(Size::Fill(1.0))
311    .height(Size::Fixed(54.0))
312}
examples/settings_calibration.rs (line 48)
41fn settings_sidebar() -> El {
42    column([
43        row([
44            icon_slot("settings"),
45            column([
46                text("Workspace")
47                    .semibold()
48                    .ellipsis()
49                    .width(Size::Fill(1.0)),
50                text("Settings").caption().ellipsis().width(Size::Fill(1.0)),
51            ])
52            .gap(2.0)
53            .width(Size::Fill(1.0))
54            .height(Size::Hug),
55        ])
56        .gap(tokens::SPACE_2)
57        .height(Size::Fixed(44.0))
58        .align(Align::Center),
59        section_label("Personal"),
60        side_item("users", "Profile", false),
61        side_item("settings", "Account", true),
62        side_item("alert-circle", "Security", false),
63        side_item("bell", "Notifications", false),
64        spacer().height(Size::Fixed(tokens::SPACE_4)),
65        section_label("Workspace"),
66        side_item("file-text", "Billing", false),
67        side_item("bar-chart", "Appearance", false),
68        side_item("activity", "Integrations", false),
69        spacer(),
70        column([text("Changes sync after save.").caption().wrap_text()])
71            .padding(tokens::SPACE_2)
72            .fill(tokens::MUTED)
73            .radius(tokens::RADIUS_MD),
74    ])
75    .gap(tokens::SPACE_2)
76    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_2))
77    .key("metric:sidebar")
78    .width(Size::Fixed(244.0))
79    .height(Size::Fill(1.0))
80    .fill(tokens::CARD)
81    .stroke(tokens::BORDER)
82}
83
84fn settings_main() -> El {
85    column([
86        settings_header(),
87        row([settings_nav_card(), settings_body(), settings_aside()])
88            .gap(tokens::SPACE_4)
89            .padding(tokens::SPACE_4)
90            .height(Size::Fill(1.0))
91            .align(Align::Stretch),
92    ])
93    .width(Size::Fill(1.0))
94    .height(Size::Fill(1.0))
95}
96
97fn settings_header() -> El {
98    row([
99        icon_button("menu").ghost(),
100        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
101        h3("Settings").key("metric:page.title"),
102        spacer(),
103        button("Reset").secondary(),
104        button("Save changes").primary(),
105    ])
106    .key("metric:header")
107    .gap(tokens::SPACE_3)
108    .height(Size::Fixed(56.0))
109    .padding(Sides::xy(tokens::SPACE_4, 0.0))
110    .align(Align::Center)
111    .stroke(tokens::BORDER)
112}
113
114fn settings_nav_card() -> El {
115    column([
116        settings_nav_item("Account", true),
117        settings_nav_item("Security", false),
118        settings_nav_item("Notifications", false),
119        settings_nav_item("Appearance", false),
120        settings_nav_item("Billing", false),
121    ])
122    .gap(tokens::SPACE_1)
123    .padding(tokens::SPACE_1)
124    .width(Size::Fixed(220.0))
125    .height(Size::Fill(1.0))
126    .style_profile(StyleProfile::Surface)
127    .surface_role(SurfaceRole::Panel)
128    .fill(tokens::CARD)
129    .stroke(tokens::BORDER)
130    .radius(tokens::RADIUS_MD)
131    .shadow(tokens::SHADOW_MD)
132}
133
134fn settings_nav_item(label: &'static str, selected: bool) -> El {
135    let mut item = row([
136        El::new(Kind::Custom("nav-dot"))
137            .fill(tokens::MUTED_FOREGROUND)
138            .radius(tokens::RADIUS_PILL)
139            .width(Size::Fixed(6.0))
140            .height(Size::Fixed(6.0)),
141        text(label)
142            .font_weight(FontWeight::Medium)
143            .ellipsis()
144            .width(Size::Fill(1.0)),
145    ])
146    .key(if selected {
147        "metric:settings.nav.row".to_string()
148    } else {
149        format!("settings-nav-{label}")
150    })
151    .metrics_role(MetricsRole::ListItem)
152    .align(Align::Center)
153    .focusable();
154
155    if selected {
156        item = item.current();
157    } else {
158        item = item.color(tokens::MUTED_FOREGROUND);
159    }
160
161    item
162}
163
164fn settings_body() -> El {
165    column([
166        column([
167            h1("Account").heading().key("metric:section.title"),
168            text("Manage identity, workspace defaults, and security preferences.")
169                .muted()
170                .wrap_text()
171                .key("metric:page.subtitle"),
172        ])
173        .gap(tokens::SPACE_1)
174        .height(Size::Hug),
175        scroll([profile_card(), preferences_card()])
176            .key("settings-body-scroll")
177            .gap(tokens::SPACE_4)
178            .width(Size::Fill(1.0))
179            .height(Size::Fill(1.0)),
180    ])
181    .gap(tokens::SPACE_4)
182    .width(Size::Fill(1.0))
183    .height(Size::Fill(1.0))
184}
185
186fn profile_card() -> El {
187    card([
188        card_header([
189            card_title("Profile"),
190            card_description("This information appears in audit logs and shared documents."),
191        ]),
192        card_content([form([
193            row([
194                setting_field("Display name", "Alicia Koch", "display-name"),
195                setting_field("Email", "alicia@acme.co", "email"),
196            ])
197            .gap(tokens::SPACE_3),
198            row([
199                setting_select("Role", "Workspace admin", "role"),
200                setting_select("Region", "US East", "region"),
201            ])
202            .gap(tokens::SPACE_3),
203        ])]),
204    ])
205    .key("metric:profile.card")
206}
207
208fn setting_field(label: &'static str, value: &'static str, key: &'static str) -> El {
209    form_item([
210        form_label(label),
211        form_control(
212            text_input(value, &Selection::caret(key, value.len()), key).key(
213                if key == "display-name" {
214                    "metric:form.input"
215                } else {
216                    key
217                },
218            ),
219        ),
220    ])
221    .width(Size::Fill(1.0))
222}
223
224fn setting_select(label: &'static str, value: &'static str, key: &'static str) -> El {
225    form_item([form_label(label), form_control(select_trigger(key, value))]).width(Size::Fill(1.0))
226}
227
228fn preferences_card() -> El {
229    card([
230        card_header([
231            card_title("Preferences"),
232            card_description("Defaults used when creating new dashboards and exports."),
233        ]),
234        card_content([column([
235            preference_row(
236                "Compact navigation",
237                "Use tighter rows in the sidebar and command menus.",
238                switch(true).key("compact-navigation"),
239            ),
240            divider(),
241            preference_row(
242                "Email summaries",
243                "Send a daily digest when documents change.",
244                switch(false).key("email-summaries"),
245            ),
246            divider(),
247            preference_row(
248                "Require approval",
249                "Route external sharing through an owner review.",
250                checkbox(true).key("approval-required"),
251            ),
252        ])
253        .gap(0.0)
254        .width(Size::Fill(1.0))])
255        .padding(0.0),
256    ])
257    .key("metric:preferences.card")
258}
259
260fn preference_row(title: &'static str, description: &'static str, control: El) -> El {
261    row([
262        column([
263            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
264            text(description)
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0))
271        .height(Size::Hug),
272        control,
273    ])
274    .key(if title == "Compact navigation" {
275        "metric:preference.row".to_string()
276    } else {
277        format!("preference-{title}")
278    })
279    .metrics_role(MetricsRole::PreferenceRow)
280    .gap(tokens::SPACE_4)
281    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_3))
282    .align(Align::Center)
283}
284
285fn settings_aside() -> El {
286    column([security_card(), scale_card()])
287        .gap(tokens::SPACE_4)
288        .width(Size::Fixed(300.0))
289        .height(Size::Fill(1.0))
290}
291
292fn security_card() -> El {
293    card([
294        card_header([
295            card_title("Security"),
296            card_description("Two-factor authentication is enabled for all privileged users."),
297        ]),
298        card_content([
299            compact_stat("Passkeys", "2 registered", badge("On").success()),
300            compact_stat("Sessions", "3 active", button("Review").secondary()),
301        ]),
302    ])
303    .width(Size::Fill(1.0))
304}
305
306fn scale_card() -> El {
307    card([
308        card_header([
309            card_title("Interface scale"),
310            card_description("Reference captures keep browser zoom fixed and vary root UI scale."),
311        ]),
312        card_content([
313            row([text("Dense").caption(), spacer(), text("Default").caption()]),
314            slider(0.66, tokens::PRIMARY)
315                .key("interface-scale")
316                .width(Size::Fill(1.0)),
317        ]),
318    ])
319    .width(Size::Fill(1.0))
320}
321
322fn compact_stat(title: &'static str, detail: &'static str, control: El) -> El {
323    row([
324        column([
325            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
326            text(detail).caption().ellipsis().width(Size::Fill(1.0)),
327        ])
328        .gap(2.0)
329        .width(Size::Fill(1.0))
330        .height(Size::Hug),
331        control,
332    ])
333    .gap(tokens::SPACE_2)
334    .height(Size::Fixed(44.0))
335    .align(Align::Center)
336}
337
338fn section_label(label: &'static str) -> El {
339    text(label)
340        .caption()
341        .height(Size::Fixed(22.0))
342        .padding(Sides::xy(tokens::SPACE_2, 0.0))
343}
344
345fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
346    let mut item = row([
347        icon(icon_name)
348            .color(tokens::MUTED_FOREGROUND)
349            .icon_size(tokens::ICON_SM)
350            .width(Size::Fixed(tokens::ICON_SM)),
351        text(label)
352            .font_weight(FontWeight::Medium)
353            .ellipsis()
354            .width(Size::Fill(1.0)),
355    ])
356    .key(if selected {
357        "metric:sidebar.nav.row".to_string()
358    } else {
359        format!("side-item-{label}")
360    })
361    .metrics_role(MetricsRole::ListItem)
362    .gap(tokens::SPACE_2)
363    .padding(Sides::xy(tokens::SPACE_2, 0.0))
364    .height(Size::Fixed(32.0))
365    .align(Align::Center)
366    .focusable();
367
368    if selected {
369        item = item.current();
370    } else {
371        item = item.color(tokens::MUTED_FOREGROUND);
372    }
373
374    item
375}
examples/dashboard_01_calibration.rs (line 48)
41fn dashboard_sidebar() -> El {
42    column([
43        row([
44            icon_cell("A"),
45            column([
46                text("Acme Inc.")
47                    .semibold()
48                    .ellipsis()
49                    .width(Size::Fill(1.0)),
50                text("Enterprise")
51                    .caption()
52                    .ellipsis()
53                    .width(Size::Fill(1.0)),
54            ])
55            .gap(2.0)
56            .width(Size::Fill(1.0))
57            .height(Size::Hug),
58        ])
59        .gap(tokens::SPACE_2)
60        .height(Size::Fixed(44.0))
61        .align(Align::Center),
62        section_label("Platform"),
63        side_item("layout-dashboard", "Dashboard", true),
64        side_item("activity", "Lifecycle", false),
65        side_item("bar-chart", "Analytics", false),
66        side_item("folder", "Projects", false),
67        spacer().height(Size::Fixed(tokens::SPACE_4)),
68        section_label("Documents"),
69        side_item("file-text", "Data library", false),
70        side_item("download", "Reports", false),
71        side_item("users", "Team", false),
72        spacer(),
73        row([
74            icon_cell("AK"),
75            column([
76                text("Alicia Koch")
77                    .semibold()
78                    .ellipsis()
79                    .width(Size::Fill(1.0)),
80                text("alicia@example.com")
81                    .caption()
82                    .ellipsis()
83                    .width(Size::Fill(1.0)),
84            ])
85            .gap(2.0)
86            .width(Size::Fill(1.0))
87            .height(Size::Hug),
88        ])
89        .gap(tokens::SPACE_2)
90        .height(Size::Fixed(50.0))
91        .align(Align::Center),
92    ])
93    .gap(tokens::SPACE_2)
94    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_2))
95    .key("metric:sidebar")
96    .width(Size::Fixed(244.0))
97    .height(Size::Fill(1.0))
98    .fill(tokens::CARD)
99    .stroke(tokens::BORDER)
100}
101
102fn section_label(label: &'static str) -> El {
103    text(label)
104        .caption()
105        .height(Size::Fixed(22.0))
106        .padding(Sides::xy(tokens::SPACE_2, 0.0))
107}
108
109fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
110    let mut item = row([
111        icon(icon_name)
112            .color(tokens::MUTED_FOREGROUND)
113            .icon_size(tokens::ICON_SM)
114            .width(Size::Fixed(tokens::ICON_SM)),
115        text(label)
116            .font_weight(FontWeight::Medium)
117            .ellipsis()
118            .width(Size::Fill(1.0)),
119    ])
120    .key(if selected {
121        "metric:sidebar.nav.row".to_string()
122    } else {
123        format!("side-item-{label}")
124    })
125    .metrics_role(MetricsRole::ListItem)
126    .gap(tokens::SPACE_2)
127    .padding(Sides::xy(tokens::SPACE_2, 0.0))
128    .height(Size::Fixed(32.0))
129    .align(Align::Center)
130    .focusable();
131
132    if selected {
133        item = item.current();
134    } else {
135        item = item.color(tokens::MUTED_FOREGROUND);
136    }
137
138    item
139}
140
141fn dashboard_main() -> El {
142    column([
143        dashboard_header(),
144        column([
145            row([
146                metric_card(
147                    "bar-chart",
148                    "Total Revenue",
149                    "$1,250.00",
150                    "+12.5%",
151                    "Trending up this month",
152                    true,
153                ),
154                metric_card(
155                    "users",
156                    "New Customers",
157                    "1,234",
158                    "-20%",
159                    "Acquisition needs attention",
160                    false,
161                ),
162                metric_card(
163                    "folder",
164                    "Active Accounts",
165                    "45,678",
166                    "+12.5%",
167                    "Strong user retention",
168                    true,
169                ),
170                metric_card(
171                    "activity",
172                    "Growth Rate",
173                    "4.5%",
174                    "+4.5%",
175                    "Meets growth projections",
176                    true,
177                ),
178            ])
179            .gap(tokens::SPACE_4),
180            row([chart_card(), sales_card()])
181                .gap(tokens::SPACE_4)
182                .height(Size::Fixed(306.0))
183                .align(Align::Stretch),
184            documents_card(),
185        ])
186        .gap(tokens::SPACE_4)
187        .padding(tokens::SPACE_7)
188        .height(Size::Fill(1.0)),
189    ])
190    .width(Size::Fill(1.0))
191    .height(Size::Fill(1.0))
192}
193
194fn dashboard_header() -> El {
195    row([
196        icon_button("menu").ghost(),
197        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
198        h3("Documents").key("metric:page.title"),
199        spacer(),
200        text_input("Search...", &Selection::default(), "dashboard-search")
201            .key("metric:command.input")
202            .width(Size::Fixed(260.0)),
203        icon_button("plus").ghost(),
204        icon_button("bell").ghost(),
205    ])
206    .key("metric:header")
207    .gap(tokens::SPACE_3)
208    .height(Size::Fixed(56.0))
209    .padding(Sides::xy(tokens::SPACE_4, 0.0))
210    .align(Align::Center)
211    .stroke(tokens::BORDER)
212}
213
214fn metric_card(
215    icon_name: &'static str,
216    title: &'static str,
217    value: &'static str,
218    delta: &'static str,
219    note: &'static str,
220    positive: bool,
221) -> El {
222    let badge = if positive {
223        badge(delta).success()
224    } else {
225        badge(delta).warning()
226    };
227    let badge = if title == "Total Revenue" {
228        badge.key("metric:kpi.badge")
229    } else {
230        badge
231    };
232    let value = if title == "Total Revenue" {
233        h2(value).ellipsis().key("metric:kpi.value")
234    } else {
235        h2(value).ellipsis()
236    };
237    card([card_content([
238        row([
239            row([
240                icon(icon_name)
241                    .color(tokens::MUTED_FOREGROUND)
242                    .icon_size(tokens::ICON_XS),
243                text(title).muted().ellipsis().width(Size::Fill(1.0)),
244            ])
245            .gap(tokens::SPACE_1)
246            .width(Size::Fill(1.0))
247            .align(Align::Center),
248            badge,
249        ])
250        .gap(tokens::SPACE_2)
251        .align(Align::Center),
252        value,
253        text(note).caption().ellipsis().width(Size::Fill(1.0)),
254    ])
255    .padding(tokens::SPACE_4)
256    .gap(tokens::SPACE_2)])
257    .key(if title == "Total Revenue" {
258        "metric:kpi.card"
259    } else {
260        title
261    })
262    .width(Size::Fill(1.0))
263}
264
265fn chart_card() -> El {
266    card([
267        card_header([
268            card_title("Visitors for the last 6 months"),
269            card_description("Total visitors by channel."),
270        ])
271        .padding(tokens::SPACE_4),
272        card_content([row(chart_bars())
273            .gap(2.0)
274            .height(Size::Fill(1.0))
275            .align(Align::End)])
276        .padding(Sides {
277            left: tokens::SPACE_4,
278            right: tokens::SPACE_4,
279            top: 0.0,
280            bottom: tokens::SPACE_4,
281        })
282        .height(Size::Fill(1.0)),
283    ])
284    .key("metric:chart.card")
285    .width(Size::Fill(1.0))
286    .height(Size::Fill(1.0))
287}
288
289fn chart_bars() -> Vec<El> {
290    [
291        48.0, 72.0, 56.0, 90.0, 64.0, 80.0, 108.0, 84.0, 122.0, 96.0, 136.0, 118.0,
292    ]
293    .into_iter()
294    .flat_map(|height| {
295        [
296            bar(height, tokens::MUTED_FOREGROUND),
297            bar((height - 28.0_f32).max(24.0), tokens::INPUT),
298        ]
299    })
300    .collect()
301}
302
303fn bar(height: f32, color: Color) -> El {
304    El::new(Kind::Custom("chart_bar"))
305        .fill(color)
306        .radius(tokens::RADIUS_SM)
307        .width(Size::Fill(1.0))
308        .height(Size::Fixed(height))
309}
310
311fn sales_card() -> El {
312    card([
313        card_header([
314            card_title("Recent Sales"),
315            card_description("You made 265 sales this month."),
316        ])
317        .padding(tokens::SPACE_4),
318        card_content([
319            sale_row("OM", "Olivia Martin", "olivia@example.com", "+$1,999.00"),
320            sale_row("JL", "Jackson Lee", "jackson@example.com", "+$39.00"),
321            sale_row("IN", "Isabella Nguyen", "isabella@example.com", "+$299.00"),
322            sale_row("WK", "William Kim", "will@example.com", "+$99.00"),
323        ])
324        .gap(tokens::SPACE_2)
325        .padding(Sides {
326            left: tokens::SPACE_4,
327            right: tokens::SPACE_4,
328            top: 0.0,
329            bottom: tokens::SPACE_4,
330        }),
331    ])
332    .key("metric:sales.card")
333    .width(Size::Fixed(330.0))
334    .height(Size::Fill(1.0))
335}
336
337fn sale_row(
338    initials: &'static str,
339    name: &'static str,
340    email: &'static str,
341    amount: &'static str,
342) -> El {
343    row([
344        icon_cell(initials),
345        column([
346            text(name).semibold().ellipsis().width(Size::Fill(1.0)),
347            text(email).caption().ellipsis().width(Size::Fill(1.0)),
348        ])
349        .gap(2.0)
350        .height(Size::Hug)
351        .width(Size::Fill(1.0)),
352        text(amount).label().small(),
353    ])
354    .gap(tokens::SPACE_2)
355    .height(Size::Fixed(42.0))
356    .align(Align::Center)
357}
Source

pub fn max_lines(self, lines: usize) -> Self

Source

pub fn font_size(self, s: f32) -> Self

Source

pub fn line_height(self, h: f32) -> Self

Source

pub fn font_weight(self, w: FontWeight) -> Self

Examples found in repository?
examples/polish_calibration.rs (line 71)
67fn nav_item(icon: &'static str, label: &'static str, selected: bool) -> El {
68    let mut item = row([
69        icon_cell(icon),
70        text(label)
71            .font_weight(FontWeight::Medium)
72            .ellipsis()
73            .width(Size::Fill(1.0)),
74    ])
75    .key(if selected {
76        "metric:sidebar.nav.row".to_string()
77    } else {
78        format!("nav-{label}")
79    })
80    .metrics_role(MetricsRole::ListItem)
81    .gap(tokens::SPACE_3)
82    .padding(Sides::xy(tokens::SPACE_2, 0.0))
83    .height(Size::Fixed(40.0))
84    .align(Align::Center)
85    .focusable();
86
87    if selected {
88        item = item.current();
89    }
90
91    item
92}
93
94fn main_panel() -> El {
95    column([
96        toolbar(),
97        column([
98            row([
99                kpi_card("Latency", "42 ms", "-18%", true),
100                kpi_card("Runs", "1,284", "+12%", true),
101                kpi_card("Errors", "7", "+2", false),
102            ])
103            .gap(tokens::SPACE_4),
104            row([table_card(), command_card()])
105                .gap(tokens::SPACE_4)
106                .height(Size::Fill(1.0))
107                .align(Align::Stretch),
108        ])
109        .gap(tokens::SPACE_4)
110        .height(Size::Fill(1.0))
111        .align(Align::Stretch),
112    ])
113    .padding(tokens::SPACE_7)
114    .gap(tokens::SPACE_2)
115    .width(Size::Fill(1.0))
116    .height(Size::Fill(1.0))
117}
118
119fn toolbar() -> El {
120    row([
121        column([
122            h1("Polish calibration").key("metric:page.title"),
123            text("A representative app surface for default tuning.")
124                .muted()
125                .key("metric:page.subtitle"),
126        ])
127        .gap(tokens::SPACE_2)
128        .height(Size::Hug),
129        spacer(),
130        button_with_icon("search", "Preview")
131            .secondary()
132            .key("metric:action.secondary"),
133        button_with_icon("upload", "Publish")
134            .primary()
135            .key("metric:action.primary"),
136    ])
137    .key("metric:header")
138    .gap(tokens::SPACE_4)
139    .height(Size::Hug)
140    .align(Align::Start)
141}
142
143fn kpi_card(label: &'static str, value: &'static str, delta: &'static str, positive: bool) -> El {
144    let delta_badge = if positive {
145        badge(delta).success()
146    } else {
147        badge(delta).destructive()
148    };
149    let delta_badge = if label == "Latency" {
150        delta_badge.key("metric:kpi.badge")
151    } else {
152        delta_badge
153    };
154    let value_text = h2(value).display();
155    let value_text = if label == "Latency" {
156        value_text.key("metric:kpi.value")
157    } else {
158        value_text
159    };
160    card([
161        card_header([card_title(label)]),
162        card_content([
163            row([value_text, spacer(), delta_badge]).align(Align::Center),
164            text(if positive {
165                "Moving in the expected direction"
166            } else {
167                "Needs visual attention"
168            })
169            .muted(),
170        ])
171        .gap(tokens::SPACE_6),
172    ])
173    .key(if label == "Latency" {
174        "metric:kpi.card"
175    } else {
176        label
177    })
178    .width(Size::Fill(1.0))
179}
180
181fn table_card() -> El {
182    card([
183        card_header([card_title("Reference rows")]),
184        card_content([table([
185            table_header([table_row([
186                table_head("Status").width(Size::Fixed(86.0)),
187                table_head("Surface").width(Size::Fill(1.0)),
188                table_head("Owner").width(Size::Fixed(110.0)),
189                table_head("State").width(Size::Fixed(86.0)),
190            ])
191            .key("metric:table.header")]),
192            divider(),
193            table_body([
194                data_row("OK", "Settings card", "core", "selected", true, "success"),
195                data_row(
196                    "WARN",
197                    "Command palette density",
198                    "widgets",
199                    "needs work",
200                    false,
201                    "warning",
202                ),
203                data_row(
204                    "ERR",
205                    "Disabled and invalid states",
206                    "style",
207                    "missing",
208                    false,
209                    "destructive",
210                ),
211                data_row(
212                    "INFO",
213                    "Token resolution",
214                    "theme",
215                    "planned",
216                    false,
217                    "info",
218                ),
219                data_row(
220                    "OK",
221                    "Popover elevation",
222                    "shader",
223                    "queued",
224                    false,
225                    "success",
226                ),
227            ])
228            .gap(tokens::SPACE_1)
229            .width(Size::Fill(1.0)),
230        ])]),
231    ])
232    .key("metric:table.card")
233    .width(Size::Fill(1.2))
234    .height(Size::Fill(1.0))
235}
236
237fn data_row(
238    status: &'static str,
239    title: &'static str,
240    owner: &'static str,
241    state: &'static str,
242    selected: bool,
243    tone: &'static str,
244) -> El {
245    let status_badge = match tone {
246        "success" => badge(status).success(),
247        "warning" => badge(status).warning(),
248        "destructive" => badge(status).destructive(),
249        _ => badge(status).info(),
250    };
251    let status_badge = if selected {
252        status_badge.key("metric:table.badge")
253    } else {
254        status_badge
255    };
256
257    let mut row = table_row([
258        table_cell(status_badge).width(Size::Fixed(70.0)),
259        column([
260            text(title)
261                .font_weight(FontWeight::Medium)
262                .ellipsis()
263                .width(Size::Fill(1.0)),
264            text("Default styling probe.")
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0)),
271        table_cell(text(owner).muted()).width(Size::Fixed(110.0)),
272        table_cell(text(state).label().small()).width(Size::Fixed(86.0)),
273    ])
274    .key(if selected {
275        "metric:table.row".to_string()
276    } else {
277        format!("row-{title}")
278    })
279    .focusable();
280
281    if selected {
282        row = row.selected();
283    }
284
285    row
286}
287
288fn command_card() -> El {
289    card([
290        card_header([card_title("Command surface")]),
291        card_content([
292            text_input(
293                "Search commands...",
294                &Selection::default(),
295                "command-search",
296            )
297            .key("metric:command.input")
298            .width(Size::Fill(1.0)),
299            popover_panel([
300                command_row("git-branch", "New branch", "Ctrl+B").key("metric:command.row"),
301                command_row("git-commit", "Commit staged files", "Ctrl+Enter")
302                    .key("command-row-commit"),
303                command_row("refresh-cw", "Refresh repository", "Ctrl+R")
304                    .key("command-row-refresh"),
305                command_row("alert-circle", "Force push", "Danger").key("command-row-force"),
306            ])
307            .width(Size::Fill(1.0)),
308            scroll([form_probe()]).key("form-probe-scroll"),
309        ])
310        .height(Size::Fill(1.0)),
311    ])
312    .key("metric:command.card")
313    .width(Size::Fill(0.8))
314    .height(Size::Fill(1.0))
315}
316
317fn form_probe() -> El {
318    form([
319        form_item([
320            form_label("Valid input"),
321            form_control(
322                text_input(
323                    "Valid input",
324                    &Selection::caret("valid-input", 11),
325                    "valid-input",
326                )
327                .key("metric:form.input"),
328            ),
329            form_description("Default field spacing and helper text."),
330        ]),
331        form_item([
332            form_label("Invalid input"),
333            form_control(
334                text_input(
335                    "Invalid input",
336                    &Selection::caret("invalid-input", 13),
337                    "invalid-input",
338                )
339                .invalid(),
340            ),
341            form_message("This field needs attention."),
342        ]),
343        row([
344            button("Disabled").secondary().disabled(),
345            button("Loading").primary().loading(),
346            spacer(),
347        ]),
348    ])
349    .padding(tokens::SPACE_3)
350    .fill(tokens::MUTED)
351    .stroke(tokens::BORDER)
352    .radius(tokens::RADIUS_MD)
353}
354
355fn icon_cell(label: &'static str) -> El {
356    El::new(Kind::Custom("icon_cell"))
357        .style_profile(StyleProfile::Surface)
358        .text(label)
359        .text_align(TextAlign::Center)
360        .caption()
361        .font_weight(FontWeight::Semibold)
362        .fill(tokens::MUTED)
363        .stroke(tokens::BORDER)
364        .radius(tokens::RADIUS_SM)
365        .width(Size::Fixed(26.0))
366        .height(Size::Fixed(26.0))
367}
More examples
Hide additional examples
examples/settings_calibration.rs (line 142)
134fn settings_nav_item(label: &'static str, selected: bool) -> El {
135    let mut item = row([
136        El::new(Kind::Custom("nav-dot"))
137            .fill(tokens::MUTED_FOREGROUND)
138            .radius(tokens::RADIUS_PILL)
139            .width(Size::Fixed(6.0))
140            .height(Size::Fixed(6.0)),
141        text(label)
142            .font_weight(FontWeight::Medium)
143            .ellipsis()
144            .width(Size::Fill(1.0)),
145    ])
146    .key(if selected {
147        "metric:settings.nav.row".to_string()
148    } else {
149        format!("settings-nav-{label}")
150    })
151    .metrics_role(MetricsRole::ListItem)
152    .align(Align::Center)
153    .focusable();
154
155    if selected {
156        item = item.current();
157    } else {
158        item = item.color(tokens::MUTED_FOREGROUND);
159    }
160
161    item
162}
163
164fn settings_body() -> El {
165    column([
166        column([
167            h1("Account").heading().key("metric:section.title"),
168            text("Manage identity, workspace defaults, and security preferences.")
169                .muted()
170                .wrap_text()
171                .key("metric:page.subtitle"),
172        ])
173        .gap(tokens::SPACE_1)
174        .height(Size::Hug),
175        scroll([profile_card(), preferences_card()])
176            .key("settings-body-scroll")
177            .gap(tokens::SPACE_4)
178            .width(Size::Fill(1.0))
179            .height(Size::Fill(1.0)),
180    ])
181    .gap(tokens::SPACE_4)
182    .width(Size::Fill(1.0))
183    .height(Size::Fill(1.0))
184}
185
186fn profile_card() -> El {
187    card([
188        card_header([
189            card_title("Profile"),
190            card_description("This information appears in audit logs and shared documents."),
191        ]),
192        card_content([form([
193            row([
194                setting_field("Display name", "Alicia Koch", "display-name"),
195                setting_field("Email", "alicia@acme.co", "email"),
196            ])
197            .gap(tokens::SPACE_3),
198            row([
199                setting_select("Role", "Workspace admin", "role"),
200                setting_select("Region", "US East", "region"),
201            ])
202            .gap(tokens::SPACE_3),
203        ])]),
204    ])
205    .key("metric:profile.card")
206}
207
208fn setting_field(label: &'static str, value: &'static str, key: &'static str) -> El {
209    form_item([
210        form_label(label),
211        form_control(
212            text_input(value, &Selection::caret(key, value.len()), key).key(
213                if key == "display-name" {
214                    "metric:form.input"
215                } else {
216                    key
217                },
218            ),
219        ),
220    ])
221    .width(Size::Fill(1.0))
222}
223
224fn setting_select(label: &'static str, value: &'static str, key: &'static str) -> El {
225    form_item([form_label(label), form_control(select_trigger(key, value))]).width(Size::Fill(1.0))
226}
227
228fn preferences_card() -> El {
229    card([
230        card_header([
231            card_title("Preferences"),
232            card_description("Defaults used when creating new dashboards and exports."),
233        ]),
234        card_content([column([
235            preference_row(
236                "Compact navigation",
237                "Use tighter rows in the sidebar and command menus.",
238                switch(true).key("compact-navigation"),
239            ),
240            divider(),
241            preference_row(
242                "Email summaries",
243                "Send a daily digest when documents change.",
244                switch(false).key("email-summaries"),
245            ),
246            divider(),
247            preference_row(
248                "Require approval",
249                "Route external sharing through an owner review.",
250                checkbox(true).key("approval-required"),
251            ),
252        ])
253        .gap(0.0)
254        .width(Size::Fill(1.0))])
255        .padding(0.0),
256    ])
257    .key("metric:preferences.card")
258}
259
260fn preference_row(title: &'static str, description: &'static str, control: El) -> El {
261    row([
262        column([
263            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
264            text(description)
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0))
271        .height(Size::Hug),
272        control,
273    ])
274    .key(if title == "Compact navigation" {
275        "metric:preference.row".to_string()
276    } else {
277        format!("preference-{title}")
278    })
279    .metrics_role(MetricsRole::PreferenceRow)
280    .gap(tokens::SPACE_4)
281    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_3))
282    .align(Align::Center)
283}
284
285fn settings_aside() -> El {
286    column([security_card(), scale_card()])
287        .gap(tokens::SPACE_4)
288        .width(Size::Fixed(300.0))
289        .height(Size::Fill(1.0))
290}
291
292fn security_card() -> El {
293    card([
294        card_header([
295            card_title("Security"),
296            card_description("Two-factor authentication is enabled for all privileged users."),
297        ]),
298        card_content([
299            compact_stat("Passkeys", "2 registered", badge("On").success()),
300            compact_stat("Sessions", "3 active", button("Review").secondary()),
301        ]),
302    ])
303    .width(Size::Fill(1.0))
304}
305
306fn scale_card() -> El {
307    card([
308        card_header([
309            card_title("Interface scale"),
310            card_description("Reference captures keep browser zoom fixed and vary root UI scale."),
311        ]),
312        card_content([
313            row([text("Dense").caption(), spacer(), text("Default").caption()]),
314            slider(0.66, tokens::PRIMARY)
315                .key("interface-scale")
316                .width(Size::Fill(1.0)),
317        ]),
318    ])
319    .width(Size::Fill(1.0))
320}
321
322fn compact_stat(title: &'static str, detail: &'static str, control: El) -> El {
323    row([
324        column([
325            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
326            text(detail).caption().ellipsis().width(Size::Fill(1.0)),
327        ])
328        .gap(2.0)
329        .width(Size::Fill(1.0))
330        .height(Size::Hug),
331        control,
332    ])
333    .gap(tokens::SPACE_2)
334    .height(Size::Fixed(44.0))
335    .align(Align::Center)
336}
337
338fn section_label(label: &'static str) -> El {
339    text(label)
340        .caption()
341        .height(Size::Fixed(22.0))
342        .padding(Sides::xy(tokens::SPACE_2, 0.0))
343}
344
345fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
346    let mut item = row([
347        icon(icon_name)
348            .color(tokens::MUTED_FOREGROUND)
349            .icon_size(tokens::ICON_SM)
350            .width(Size::Fixed(tokens::ICON_SM)),
351        text(label)
352            .font_weight(FontWeight::Medium)
353            .ellipsis()
354            .width(Size::Fill(1.0)),
355    ])
356    .key(if selected {
357        "metric:sidebar.nav.row".to_string()
358    } else {
359        format!("side-item-{label}")
360    })
361    .metrics_role(MetricsRole::ListItem)
362    .gap(tokens::SPACE_2)
363    .padding(Sides::xy(tokens::SPACE_2, 0.0))
364    .height(Size::Fixed(32.0))
365    .align(Align::Center)
366    .focusable();
367
368    if selected {
369        item = item.current();
370    } else {
371        item = item.color(tokens::MUTED_FOREGROUND);
372    }
373
374    item
375}
examples/dashboard_01_calibration.rs (line 116)
109fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
110    let mut item = row([
111        icon(icon_name)
112            .color(tokens::MUTED_FOREGROUND)
113            .icon_size(tokens::ICON_SM)
114            .width(Size::Fixed(tokens::ICON_SM)),
115        text(label)
116            .font_weight(FontWeight::Medium)
117            .ellipsis()
118            .width(Size::Fill(1.0)),
119    ])
120    .key(if selected {
121        "metric:sidebar.nav.row".to_string()
122    } else {
123        format!("side-item-{label}")
124    })
125    .metrics_role(MetricsRole::ListItem)
126    .gap(tokens::SPACE_2)
127    .padding(Sides::xy(tokens::SPACE_2, 0.0))
128    .height(Size::Fixed(32.0))
129    .align(Align::Center)
130    .focusable();
131
132    if selected {
133        item = item.current();
134    } else {
135        item = item.color(tokens::MUTED_FOREGROUND);
136    }
137
138    item
139}
140
141fn dashboard_main() -> El {
142    column([
143        dashboard_header(),
144        column([
145            row([
146                metric_card(
147                    "bar-chart",
148                    "Total Revenue",
149                    "$1,250.00",
150                    "+12.5%",
151                    "Trending up this month",
152                    true,
153                ),
154                metric_card(
155                    "users",
156                    "New Customers",
157                    "1,234",
158                    "-20%",
159                    "Acquisition needs attention",
160                    false,
161                ),
162                metric_card(
163                    "folder",
164                    "Active Accounts",
165                    "45,678",
166                    "+12.5%",
167                    "Strong user retention",
168                    true,
169                ),
170                metric_card(
171                    "activity",
172                    "Growth Rate",
173                    "4.5%",
174                    "+4.5%",
175                    "Meets growth projections",
176                    true,
177                ),
178            ])
179            .gap(tokens::SPACE_4),
180            row([chart_card(), sales_card()])
181                .gap(tokens::SPACE_4)
182                .height(Size::Fixed(306.0))
183                .align(Align::Stretch),
184            documents_card(),
185        ])
186        .gap(tokens::SPACE_4)
187        .padding(tokens::SPACE_7)
188        .height(Size::Fill(1.0)),
189    ])
190    .width(Size::Fill(1.0))
191    .height(Size::Fill(1.0))
192}
193
194fn dashboard_header() -> El {
195    row([
196        icon_button("menu").ghost(),
197        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
198        h3("Documents").key("metric:page.title"),
199        spacer(),
200        text_input("Search...", &Selection::default(), "dashboard-search")
201            .key("metric:command.input")
202            .width(Size::Fixed(260.0)),
203        icon_button("plus").ghost(),
204        icon_button("bell").ghost(),
205    ])
206    .key("metric:header")
207    .gap(tokens::SPACE_3)
208    .height(Size::Fixed(56.0))
209    .padding(Sides::xy(tokens::SPACE_4, 0.0))
210    .align(Align::Center)
211    .stroke(tokens::BORDER)
212}
213
214fn metric_card(
215    icon_name: &'static str,
216    title: &'static str,
217    value: &'static str,
218    delta: &'static str,
219    note: &'static str,
220    positive: bool,
221) -> El {
222    let badge = if positive {
223        badge(delta).success()
224    } else {
225        badge(delta).warning()
226    };
227    let badge = if title == "Total Revenue" {
228        badge.key("metric:kpi.badge")
229    } else {
230        badge
231    };
232    let value = if title == "Total Revenue" {
233        h2(value).ellipsis().key("metric:kpi.value")
234    } else {
235        h2(value).ellipsis()
236    };
237    card([card_content([
238        row([
239            row([
240                icon(icon_name)
241                    .color(tokens::MUTED_FOREGROUND)
242                    .icon_size(tokens::ICON_XS),
243                text(title).muted().ellipsis().width(Size::Fill(1.0)),
244            ])
245            .gap(tokens::SPACE_1)
246            .width(Size::Fill(1.0))
247            .align(Align::Center),
248            badge,
249        ])
250        .gap(tokens::SPACE_2)
251        .align(Align::Center),
252        value,
253        text(note).caption().ellipsis().width(Size::Fill(1.0)),
254    ])
255    .padding(tokens::SPACE_4)
256    .gap(tokens::SPACE_2)])
257    .key(if title == "Total Revenue" {
258        "metric:kpi.card"
259    } else {
260        title
261    })
262    .width(Size::Fill(1.0))
263}
264
265fn chart_card() -> El {
266    card([
267        card_header([
268            card_title("Visitors for the last 6 months"),
269            card_description("Total visitors by channel."),
270        ])
271        .padding(tokens::SPACE_4),
272        card_content([row(chart_bars())
273            .gap(2.0)
274            .height(Size::Fill(1.0))
275            .align(Align::End)])
276        .padding(Sides {
277            left: tokens::SPACE_4,
278            right: tokens::SPACE_4,
279            top: 0.0,
280            bottom: tokens::SPACE_4,
281        })
282        .height(Size::Fill(1.0)),
283    ])
284    .key("metric:chart.card")
285    .width(Size::Fill(1.0))
286    .height(Size::Fill(1.0))
287}
288
289fn chart_bars() -> Vec<El> {
290    [
291        48.0, 72.0, 56.0, 90.0, 64.0, 80.0, 108.0, 84.0, 122.0, 96.0, 136.0, 118.0,
292    ]
293    .into_iter()
294    .flat_map(|height| {
295        [
296            bar(height, tokens::MUTED_FOREGROUND),
297            bar((height - 28.0_f32).max(24.0), tokens::INPUT),
298        ]
299    })
300    .collect()
301}
302
303fn bar(height: f32, color: Color) -> El {
304    El::new(Kind::Custom("chart_bar"))
305        .fill(color)
306        .radius(tokens::RADIUS_SM)
307        .width(Size::Fill(1.0))
308        .height(Size::Fixed(height))
309}
310
311fn sales_card() -> El {
312    card([
313        card_header([
314            card_title("Recent Sales"),
315            card_description("You made 265 sales this month."),
316        ])
317        .padding(tokens::SPACE_4),
318        card_content([
319            sale_row("OM", "Olivia Martin", "olivia@example.com", "+$1,999.00"),
320            sale_row("JL", "Jackson Lee", "jackson@example.com", "+$39.00"),
321            sale_row("IN", "Isabella Nguyen", "isabella@example.com", "+$299.00"),
322            sale_row("WK", "William Kim", "will@example.com", "+$99.00"),
323        ])
324        .gap(tokens::SPACE_2)
325        .padding(Sides {
326            left: tokens::SPACE_4,
327            right: tokens::SPACE_4,
328            top: 0.0,
329            bottom: tokens::SPACE_4,
330        }),
331    ])
332    .key("metric:sales.card")
333    .width(Size::Fixed(330.0))
334    .height(Size::Fill(1.0))
335}
336
337fn sale_row(
338    initials: &'static str,
339    name: &'static str,
340    email: &'static str,
341    amount: &'static str,
342) -> El {
343    row([
344        icon_cell(initials),
345        column([
346            text(name).semibold().ellipsis().width(Size::Fill(1.0)),
347            text(email).caption().ellipsis().width(Size::Fill(1.0)),
348        ])
349        .gap(2.0)
350        .height(Size::Hug)
351        .width(Size::Fill(1.0)),
352        text(amount).label().small(),
353    ])
354    .gap(tokens::SPACE_2)
355    .height(Size::Fixed(42.0))
356    .align(Align::Center)
357}
358
359fn documents_card() -> El {
360    card([
361        card_header([card_title("Documents")]).padding(tokens::SPACE_4),
362        card_content([scroll([table([
363            table_header([table_row([
364                table_head("").width(Size::Fixed(35.0)),
365                table_head("Header").width(Size::Fill(1.8)),
366                table_head("Section Type").width(Size::Fill(1.0)),
367                table_head("Status").width(Size::Fixed(104.0)),
368                table_head("Target").width(Size::Fixed(64.0)),
369                table_head("Limit").width(Size::Fixed(64.0)),
370                table_head("Reviewer").width(Size::Fixed(128.0)),
371                table_head("").width(Size::Fixed(32.0)),
372            ])
373            .padding(Sides::xy(tokens::SPACE_4, 0.0))
374            .key("metric:table.header")]),
375            divider(),
376            table_body([
377                document_row(
378                    "Cover page",
379                    "Cover page",
380                    "In Process",
381                    "18",
382                    "5",
383                    "Eddie Lake",
384                    "info",
385                ),
386                document_row(
387                    "Table of contents",
388                    "Table of contents",
389                    "Done",
390                    "29",
391                    "24",
392                    "Eddie Lake",
393                    "success",
394                ),
395            ]),
396        ])])
397        .height(Size::Fill(1.0))])
398        .gap(0.0)
399        .padding(0.0)
400        .height(Size::Fill(1.0)),
401    ])
402    .key("metric:table.card")
403    .height(Size::Fill(1.0))
404}
405
406fn document_row(
407    header: &'static str,
408    section: &'static str,
409    status: &'static str,
410    target: &'static str,
411    limit: &'static str,
412    reviewer: &'static str,
413    tone: &'static str,
414) -> El {
415    let status_badge = match tone {
416        "success" => badge(status).success(),
417        _ => badge(status).info(),
418    };
419    table_row([
420        table_utility_cell("::"),
421        table_cell(text(header).label().small()).width(Size::Fill(1.8)),
422        table_cell(text(section).muted()).width(Size::Fill(1.0)),
423        table_cell(status_badge).width(Size::Fixed(104.0)),
424        table_cell(text(target).label().small()).width(Size::Fixed(64.0)),
425        table_cell(text(limit).label().small()).width(Size::Fixed(64.0)),
426        table_cell(text(reviewer).muted()).width(Size::Fixed(128.0)),
427        table_action_cell(),
428    ])
429    .padding(Sides::xy(tokens::SPACE_4, 0.0))
430    .key(if header == "Cover page" {
431        "metric:table.row"
432    } else {
433        header
434    })
435}
436
437fn table_utility_cell(label: &'static str) -> El {
438    table_cell(text(label).muted().center_text()).width(Size::Fixed(35.0))
439}
440
441fn table_action_cell() -> El {
442    stack([icon("more-horizontal")
443        .icon_size(tokens::ICON_SM)
444        .color(tokens::MUTED_FOREGROUND)])
445    .align(Align::Center)
446    .justify(Justify::Center)
447    .width(Size::Fixed(32.0))
448    .height(Size::Hug)
449}
450
451fn icon_cell(label: &'static str) -> El {
452    El::new(Kind::Custom("icon_cell"))
453        .style_profile(StyleProfile::Surface)
454        .text(label)
455        .text_align(TextAlign::Center)
456        .caption()
457        .font_weight(FontWeight::Semibold)
458        .fill(tokens::MUTED)
459        .radius(tokens::RADIUS_SM)
460        .width(Size::Fixed(30.0))
461        .height(Size::Fixed(30.0))
462}
Source

pub fn font_family(self, family: FontFamily) -> Self

Source

pub fn inter(self) -> Self

Source

pub fn roboto(self) -> Self

Source

pub fn mono_font_family(self, family: FontFamily) -> Self

Override the monospace face used when this node renders as code (font_mono = true, TextRole::Code, or any descendant that inherits the value through theme propagation). Setting this pins the node — theme with_mono_font_family(...) no longer stamps over it.

Source

pub fn jetbrains_mono(self) -> Self

Pin this node’s monospace face to JetBrains Mono. Convenience shorthand for .mono_font_family(FontFamily::JetBrainsMono).

Source

pub fn icon_source(self, source: impl IntoIconSource) -> Self

Set the icon for this element to either a built-in crate::IconName, an app-supplied crate::SvgIcon, or a string-typed name from the built-in vocabulary.

Source

pub fn icon_name(self, source: impl IntoIconSource) -> Self

Convenience alias for Self::icon_source preserved for call sites that want the historical name.

Source

pub fn icon_stroke_width(self, width: f32) -> Self

Source

pub fn icon_size(self, size: f32) -> Self

Examples found in repository?
examples/dashboard_01_calibration.rs (line 113)
109fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
110    let mut item = row([
111        icon(icon_name)
112            .color(tokens::MUTED_FOREGROUND)
113            .icon_size(tokens::ICON_SM)
114            .width(Size::Fixed(tokens::ICON_SM)),
115        text(label)
116            .font_weight(FontWeight::Medium)
117            .ellipsis()
118            .width(Size::Fill(1.0)),
119    ])
120    .key(if selected {
121        "metric:sidebar.nav.row".to_string()
122    } else {
123        format!("side-item-{label}")
124    })
125    .metrics_role(MetricsRole::ListItem)
126    .gap(tokens::SPACE_2)
127    .padding(Sides::xy(tokens::SPACE_2, 0.0))
128    .height(Size::Fixed(32.0))
129    .align(Align::Center)
130    .focusable();
131
132    if selected {
133        item = item.current();
134    } else {
135        item = item.color(tokens::MUTED_FOREGROUND);
136    }
137
138    item
139}
140
141fn dashboard_main() -> El {
142    column([
143        dashboard_header(),
144        column([
145            row([
146                metric_card(
147                    "bar-chart",
148                    "Total Revenue",
149                    "$1,250.00",
150                    "+12.5%",
151                    "Trending up this month",
152                    true,
153                ),
154                metric_card(
155                    "users",
156                    "New Customers",
157                    "1,234",
158                    "-20%",
159                    "Acquisition needs attention",
160                    false,
161                ),
162                metric_card(
163                    "folder",
164                    "Active Accounts",
165                    "45,678",
166                    "+12.5%",
167                    "Strong user retention",
168                    true,
169                ),
170                metric_card(
171                    "activity",
172                    "Growth Rate",
173                    "4.5%",
174                    "+4.5%",
175                    "Meets growth projections",
176                    true,
177                ),
178            ])
179            .gap(tokens::SPACE_4),
180            row([chart_card(), sales_card()])
181                .gap(tokens::SPACE_4)
182                .height(Size::Fixed(306.0))
183                .align(Align::Stretch),
184            documents_card(),
185        ])
186        .gap(tokens::SPACE_4)
187        .padding(tokens::SPACE_7)
188        .height(Size::Fill(1.0)),
189    ])
190    .width(Size::Fill(1.0))
191    .height(Size::Fill(1.0))
192}
193
194fn dashboard_header() -> El {
195    row([
196        icon_button("menu").ghost(),
197        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
198        h3("Documents").key("metric:page.title"),
199        spacer(),
200        text_input("Search...", &Selection::default(), "dashboard-search")
201            .key("metric:command.input")
202            .width(Size::Fixed(260.0)),
203        icon_button("plus").ghost(),
204        icon_button("bell").ghost(),
205    ])
206    .key("metric:header")
207    .gap(tokens::SPACE_3)
208    .height(Size::Fixed(56.0))
209    .padding(Sides::xy(tokens::SPACE_4, 0.0))
210    .align(Align::Center)
211    .stroke(tokens::BORDER)
212}
213
214fn metric_card(
215    icon_name: &'static str,
216    title: &'static str,
217    value: &'static str,
218    delta: &'static str,
219    note: &'static str,
220    positive: bool,
221) -> El {
222    let badge = if positive {
223        badge(delta).success()
224    } else {
225        badge(delta).warning()
226    };
227    let badge = if title == "Total Revenue" {
228        badge.key("metric:kpi.badge")
229    } else {
230        badge
231    };
232    let value = if title == "Total Revenue" {
233        h2(value).ellipsis().key("metric:kpi.value")
234    } else {
235        h2(value).ellipsis()
236    };
237    card([card_content([
238        row([
239            row([
240                icon(icon_name)
241                    .color(tokens::MUTED_FOREGROUND)
242                    .icon_size(tokens::ICON_XS),
243                text(title).muted().ellipsis().width(Size::Fill(1.0)),
244            ])
245            .gap(tokens::SPACE_1)
246            .width(Size::Fill(1.0))
247            .align(Align::Center),
248            badge,
249        ])
250        .gap(tokens::SPACE_2)
251        .align(Align::Center),
252        value,
253        text(note).caption().ellipsis().width(Size::Fill(1.0)),
254    ])
255    .padding(tokens::SPACE_4)
256    .gap(tokens::SPACE_2)])
257    .key(if title == "Total Revenue" {
258        "metric:kpi.card"
259    } else {
260        title
261    })
262    .width(Size::Fill(1.0))
263}
264
265fn chart_card() -> El {
266    card([
267        card_header([
268            card_title("Visitors for the last 6 months"),
269            card_description("Total visitors by channel."),
270        ])
271        .padding(tokens::SPACE_4),
272        card_content([row(chart_bars())
273            .gap(2.0)
274            .height(Size::Fill(1.0))
275            .align(Align::End)])
276        .padding(Sides {
277            left: tokens::SPACE_4,
278            right: tokens::SPACE_4,
279            top: 0.0,
280            bottom: tokens::SPACE_4,
281        })
282        .height(Size::Fill(1.0)),
283    ])
284    .key("metric:chart.card")
285    .width(Size::Fill(1.0))
286    .height(Size::Fill(1.0))
287}
288
289fn chart_bars() -> Vec<El> {
290    [
291        48.0, 72.0, 56.0, 90.0, 64.0, 80.0, 108.0, 84.0, 122.0, 96.0, 136.0, 118.0,
292    ]
293    .into_iter()
294    .flat_map(|height| {
295        [
296            bar(height, tokens::MUTED_FOREGROUND),
297            bar((height - 28.0_f32).max(24.0), tokens::INPUT),
298        ]
299    })
300    .collect()
301}
302
303fn bar(height: f32, color: Color) -> El {
304    El::new(Kind::Custom("chart_bar"))
305        .fill(color)
306        .radius(tokens::RADIUS_SM)
307        .width(Size::Fill(1.0))
308        .height(Size::Fixed(height))
309}
310
311fn sales_card() -> El {
312    card([
313        card_header([
314            card_title("Recent Sales"),
315            card_description("You made 265 sales this month."),
316        ])
317        .padding(tokens::SPACE_4),
318        card_content([
319            sale_row("OM", "Olivia Martin", "olivia@example.com", "+$1,999.00"),
320            sale_row("JL", "Jackson Lee", "jackson@example.com", "+$39.00"),
321            sale_row("IN", "Isabella Nguyen", "isabella@example.com", "+$299.00"),
322            sale_row("WK", "William Kim", "will@example.com", "+$99.00"),
323        ])
324        .gap(tokens::SPACE_2)
325        .padding(Sides {
326            left: tokens::SPACE_4,
327            right: tokens::SPACE_4,
328            top: 0.0,
329            bottom: tokens::SPACE_4,
330        }),
331    ])
332    .key("metric:sales.card")
333    .width(Size::Fixed(330.0))
334    .height(Size::Fill(1.0))
335}
336
337fn sale_row(
338    initials: &'static str,
339    name: &'static str,
340    email: &'static str,
341    amount: &'static str,
342) -> El {
343    row([
344        icon_cell(initials),
345        column([
346            text(name).semibold().ellipsis().width(Size::Fill(1.0)),
347            text(email).caption().ellipsis().width(Size::Fill(1.0)),
348        ])
349        .gap(2.0)
350        .height(Size::Hug)
351        .width(Size::Fill(1.0)),
352        text(amount).label().small(),
353    ])
354    .gap(tokens::SPACE_2)
355    .height(Size::Fixed(42.0))
356    .align(Align::Center)
357}
358
359fn documents_card() -> El {
360    card([
361        card_header([card_title("Documents")]).padding(tokens::SPACE_4),
362        card_content([scroll([table([
363            table_header([table_row([
364                table_head("").width(Size::Fixed(35.0)),
365                table_head("Header").width(Size::Fill(1.8)),
366                table_head("Section Type").width(Size::Fill(1.0)),
367                table_head("Status").width(Size::Fixed(104.0)),
368                table_head("Target").width(Size::Fixed(64.0)),
369                table_head("Limit").width(Size::Fixed(64.0)),
370                table_head("Reviewer").width(Size::Fixed(128.0)),
371                table_head("").width(Size::Fixed(32.0)),
372            ])
373            .padding(Sides::xy(tokens::SPACE_4, 0.0))
374            .key("metric:table.header")]),
375            divider(),
376            table_body([
377                document_row(
378                    "Cover page",
379                    "Cover page",
380                    "In Process",
381                    "18",
382                    "5",
383                    "Eddie Lake",
384                    "info",
385                ),
386                document_row(
387                    "Table of contents",
388                    "Table of contents",
389                    "Done",
390                    "29",
391                    "24",
392                    "Eddie Lake",
393                    "success",
394                ),
395            ]),
396        ])])
397        .height(Size::Fill(1.0))])
398        .gap(0.0)
399        .padding(0.0)
400        .height(Size::Fill(1.0)),
401    ])
402    .key("metric:table.card")
403    .height(Size::Fill(1.0))
404}
405
406fn document_row(
407    header: &'static str,
408    section: &'static str,
409    status: &'static str,
410    target: &'static str,
411    limit: &'static str,
412    reviewer: &'static str,
413    tone: &'static str,
414) -> El {
415    let status_badge = match tone {
416        "success" => badge(status).success(),
417        _ => badge(status).info(),
418    };
419    table_row([
420        table_utility_cell("::"),
421        table_cell(text(header).label().small()).width(Size::Fill(1.8)),
422        table_cell(text(section).muted()).width(Size::Fill(1.0)),
423        table_cell(status_badge).width(Size::Fixed(104.0)),
424        table_cell(text(target).label().small()).width(Size::Fixed(64.0)),
425        table_cell(text(limit).label().small()).width(Size::Fixed(64.0)),
426        table_cell(text(reviewer).muted()).width(Size::Fixed(128.0)),
427        table_action_cell(),
428    ])
429    .padding(Sides::xy(tokens::SPACE_4, 0.0))
430    .key(if header == "Cover page" {
431        "metric:table.row"
432    } else {
433        header
434    })
435}
436
437fn table_utility_cell(label: &'static str) -> El {
438    table_cell(text(label).muted().center_text()).width(Size::Fixed(35.0))
439}
440
441fn table_action_cell() -> El {
442    stack([icon("more-horizontal")
443        .icon_size(tokens::ICON_SM)
444        .color(tokens::MUTED_FOREGROUND)])
445    .align(Align::Center)
446    .justify(Justify::Center)
447    .width(Size::Fixed(32.0))
448    .height(Size::Hug)
449}
More examples
Hide additional examples
examples/settings_calibration.rs (line 349)
345fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
346    let mut item = row([
347        icon(icon_name)
348            .color(tokens::MUTED_FOREGROUND)
349            .icon_size(tokens::ICON_SM)
350            .width(Size::Fixed(tokens::ICON_SM)),
351        text(label)
352            .font_weight(FontWeight::Medium)
353            .ellipsis()
354            .width(Size::Fill(1.0)),
355    ])
356    .key(if selected {
357        "metric:sidebar.nav.row".to_string()
358    } else {
359        format!("side-item-{label}")
360    })
361    .metrics_role(MetricsRole::ListItem)
362    .gap(tokens::SPACE_2)
363    .padding(Sides::xy(tokens::SPACE_2, 0.0))
364    .height(Size::Fixed(32.0))
365    .align(Align::Center)
366    .focusable();
367
368    if selected {
369        item = item.current();
370    } else {
371        item = item.color(tokens::MUTED_FOREGROUND);
372    }
373
374    item
375}
376
377fn icon_slot(icon_name: &'static str) -> El {
378    El::new(Kind::Custom("icon_cell"))
379        .style_profile(StyleProfile::Surface)
380        .child(
381            icon(icon_name)
382                .color(tokens::FOREGROUND)
383                .icon_size(tokens::ICON_XS),
384        )
385        .align(Align::Center)
386        .justify(Justify::Center)
387        .fill(tokens::MUTED)
388        .stroke(tokens::BORDER)
389        .radius(tokens::RADIUS_SM)
390        .width(Size::Fixed(30.0))
391        .height(Size::Fixed(30.0))
392}
Source

pub fn image(self, image: impl Into<Image>) -> Self

Attach a raster image. Usually you’ll want the crate::image free builder instead, which sets crate::Kind::Image for you; this method exists for cases where you’ve already constructed an El (e.g. through a stock widget) and want to swap in pixel art.

Source

pub fn image_fit(self, fit: ImageFit) -> Self

Source

pub fn image_tint(self, c: Color) -> Self

Source

pub fn surface_source(self, source: SurfaceSource) -> Self

Attach an app-owned GPU texture source. Typically set via the crate::tree::surface builder (which also sets crate::Kind::Surface); reach for this method on a stock widget El whose Kind you want to keep.

Source

pub fn surface_alpha(self, alpha: SurfaceAlpha) -> Self

How a crate::Kind::Surface El composes with widgets below it. Default is crate::surface::SurfaceAlpha::Premultiplied.

Source

pub fn surface_fit(self, fit: ImageFit) -> Self

How a crate::Kind::Surface El’s texture projects into its resolved rect. Defaults to crate::image::ImageFit::Fill — stretch to the rect — for parity with the pre-surface_fit behaviour. Contain / Cover / None mirror the modes on crate::El::image_fit.

Source

pub fn surface_transform(self, transform: Affine2) -> Self

Affine applied to the texture quad in destination space, around the centre of the post-Self::surface_fit rect. Defaults to identity. Use this for rotation, mirroring, source-dimension- independent zoom/pan, or any combination thereof. The El’s auto-clip scissor still clamps the rendered content to the resolved rect.

Source

pub fn vector_source(self, asset: impl Into<Arc<VectorAsset>>) -> Self

Attach a vector asset source. Typically set via the crate::tree::vector builder (which also sets crate::Kind::Vector); reach for this method on a stock widget El whose Kind you want to keep.

Source

pub fn vector_render_mode(self, mode: VectorRenderMode) -> Self

Select how a vector asset should render. The default is crate::vector::VectorRenderMode::Painted, which preserves authored fills/strokes/gradients. Use Self::vector_mask when the asset is intended as one-colour coverage geometry.

Source

pub fn vector_mask(self, color: Color) -> Self

Treat this vector as coverage geometry and paint it with one colour. Backends can render this through their MSDF path.

Source

pub fn vector_painted(self) -> Self

Preserve authored vector paint. This is the default for crate::tree::vector.

Source

pub fn redraw_within(self, deadline: Duration) -> Self

Inside-out redraw deadline. While this El is visible (rect intersects the viewport), Aetna asks the host to drive the next frame within deadline. Aggregated across the tree via min, so the host gets a single signal regardless of how many widgets are asking. Use Duration::ZERO for “next frame ASAP”; non-zero values pace the redraw loop below the display rate.

Apps that pause / resume animation (e.g. GIF playback) just stop calling this method on the relevant El — Aetna re-runs the aggregation each frame, so the redraw scheduler quiets automatically when no visible widget is asking.

Source

pub fn mono(self) -> Self

Opt this node into the monospace face. Setting this flag also sets El::explicit_mono so a subsequent role modifier (.caption() / .label() / .body() / .title() / .heading() / .display()) won’t silently reset font_mono when the role’s default is non-mono. The natural reading order text(s).mono().caption() therefore renders in mono.

Examples found in repository?
examples/scrollbar.rs (line 24)
20fn list_rows() -> Vec<El> {
21    (0..40)
22        .map(|i| {
23            row([
24                text(format!("{i:02}.")).mono().muted(),
25                text(format!("scrollable list item {i}")),
26            ])
27            .gap(tokens::SPACE_2)
28            .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_1))
29            .height(Size::Fixed(28.0))
30            .align(Align::Center)
31        })
32        .collect()
33}
34
35fn fixture() -> El {
36    column([
37        h2("Scrollbar"),
38        text("scroll() and virtual_list() show a draggable thumb by default.").muted(),
39        row([
40            // 1) scroll() — default-on scrollbar.
41            column([
42                text("scroll() — default").bold(),
43                scroll(list_rows())
44                    .height(Size::Fixed(240.0))
45                    .padding(tokens::SPACE_2)
46                    .stroke(tokens::BORDER)
47                    .stroke_width(1.0)
48                    .radius(tokens::RADIUS_MD),
49            ])
50            .gap(tokens::SPACE_2)
51            .width(Size::Fill(1.0))
52            .height(Size::Hug),
53            // 2) virtual_list — thumb scales to content size.
54            column([
55                text("virtual_list(200, 28)").bold(),
56                virtual_list(200, 28.0, |i| {
57                    row([
58                        text(format!("{i:03}")).mono().muted(),
59                        text(format!("row {i}")),
60                    ])
61                    .gap(tokens::SPACE_2)
62                    .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_1))
63                    .height(Size::Fixed(28.0))
64                    .align(Align::Center)
65                })
66                .height(Size::Fixed(240.0))
67                .padding(tokens::SPACE_2)
68                .stroke(tokens::BORDER)
69                .stroke_width(1.0)
70                .radius(tokens::RADIUS_MD),
71            ])
72            .gap(tokens::SPACE_2)
73            .width(Size::Fill(1.0))
74            .height(Size::Hug),
75            // 3) Opt-out: same content, no thumb.
76            column([
77                text("scroll().no_scrollbar()").bold(),
78                scroll(list_rows())
79                    .no_scrollbar()
80                    .height(Size::Fixed(240.0))
81                    .padding(tokens::SPACE_2)
82                    .stroke(tokens::BORDER)
83                    .stroke_width(1.0)
84                    .radius(tokens::RADIUS_MD),
85            ])
86            .gap(tokens::SPACE_2)
87            .width(Size::Fill(1.0))
88            .height(Size::Hug),
89        ])
90        .gap(tokens::SPACE_4)
91        .width(Size::Fill(1.0)),
92    ])
93    .gap(tokens::SPACE_4)
94    .padding(tokens::SPACE_7)
95}
More examples
Hide additional examples
examples/custom_paint.rs (line 71)
68fn build_row(c: &FakeCommit, idx: usize, selected: bool) -> El {
69    row([
70        graph_cell(c.lane, selected),
71        text(c.sha).mono().muted(),
72        text(c.subject),
73        spacer(),
74        text(format!("{} · {}", c.author, c.when)).muted(),
75    ])
76    .key(format!("commit-{idx}"))
77    .gap(tokens::SPACE_3)
78    .padding(Sides::xy(tokens::SPACE_2, 0.0))
79    .height(Size::Fixed(ROW_HEIGHT))
80    .align(Align::Center)
81}
examples/virtual_list.rs (line 27)
18fn build_row(i: usize) -> El {
19    let badge_el = match i % 5 {
20        0 => badge("info").muted(),
21        1 => badge("warn").warning(),
22        2 => badge("ok").success(),
23        3 => badge("err").destructive(),
24        _ => spacer(),
25    };
26    row([
27        text(format!("#{i:05}")).mono(),
28        spacer(),
29        text(format!("entry {i}")),
30        spacer(),
31        badge_el,
32    ])
33    .key(format!("row-{i}"))
34    .gap(tokens::SPACE_3)
35    .padding(Sides::xy(tokens::SPACE_3, tokens::SPACE_2))
36    .height(Size::Fixed(ROW_HEIGHT))
37}
examples/inline_runs_highlight.rs (line 40)
26fn fixture() -> El {
27    column([
28        h2("Inline run backgrounds"),
29        paragraph(
30            "RunStyle.bg paints a per-line solid quad behind the glyphs of \
31             a styled span — the shaper computes the rect from the actual \
32             glyph extents, so wrapping splits the highlight cleanly.",
33        )
34        .muted(),
35        // Search-result style.
36        text_runs([
37            text("…the matcher finds "),
38            text("aetna").background(HIGHLIGHT_YELLOW).bold(),
39            text(" in "),
40            text("aetna_core::widgets").mono(),
41            text(" — the highlight tracks the glyph extent."),
42        ])
43        .wrap_text()
44        .width(Size::Fill(1.0))
45        .height(Size::Hug),
46        // Diff-style: add + remove tints inside the same line.
47        text_runs([
48            text("- "),
49            text("error::Custom").mono().background(DIFF_REMOVE),
50            text("(\"too narrow\")"),
51            hard_break(),
52            text("+ "),
53            text("error::WrapTooNarrow").mono().background(DIFF_ADD),
54            text(" { available }"),
55        ])
56        .wrap_text()
57        .width(Size::Fill(1.0))
58        .height(Size::Hug),
59        // Wrapping highlight: long span that spans two lines.
60        text_runs([
61            text("Long highlight: "),
62            text("the quick brown fox jumps over the lazy dog and keeps going")
63                .background(HIGHLIGHT_YELLOW),
64            text(" — the rect is split per line."),
65        ])
66        .wrap_text()
67        .width(Size::Fill(1.0))
68        .height(Size::Hug),
69    ])
70    .gap(tokens::SPACE_4)
71    .padding(tokens::SPACE_7)
72    .width(Size::Fixed(640.0))
73}
Source

pub fn italic(self) -> Self

Italic styling for a text run. Honoured by the crate::Kind::Inlines layout pass and (best-effort) on standalone text Els.

Examples found in repository?
examples/inline_runs.rs (line 25)
18fn fixture() -> El {
19    column([
20        h2("Inline runs"),
21        text_runs([
22            text("Aetna's attributed-text path lets you compose runs with "),
23            text("bold").bold(),
24            text(", "),
25            text("italic").italic(),
26            text(", "),
27            text("colored").color(tokens::DESTRUCTIVE),
28            text(", and "),
29            text("inline code").code(),
30            text(" segments inside one wrapping paragraph."),
31            hard_break(),
32            text("Hard breaks act like ").muted(),
33            text("<br>").code(),
34            text(" — they end the current line without breaking out of the run.").muted(),
35        ])
36        .wrap_text()
37        .width(Size::Fill(1.0))
38        .height(Size::Hug),
39        paragraph(
40            "All of the above flows through one cosmic-text rich-text shape — \
41             wrapping decisions cross run boundaries the way real prose wraps.",
42        )
43        .muted(),
44    ])
45    .gap(tokens::SPACE_4)
46    .padding(tokens::SPACE_7)
47    .width(Size::Fixed(640.0))
48}
More examples
Hide additional examples
examples/long_form_content.rs (line 28)
15fn fixture() -> El {
16    column([
17        h2("Long-form content widgets"),
18        paragraph(
19            "These primitives compose the markdown-shaped vocabulary an \
20             upcoming transformer will target. Each widget is plain \
21             Aetna — selectable text, themed surfaces, the same layout \
22             pass as everything else.",
23        ),
24        h3("Highlights"),
25        bullet_list(vec![
26            text_runs([
27                text("Bulleted lists with a hanging indent — wrapped lines align under "),
28                text("themselves").italic(),
29                text(", not under the marker."),
30            ]),
31            text_runs([
32                text("Inline runs work inside list items: "),
33                text("bold").bold(),
34                text(", "),
35                text("code").code(),
36                text(", "),
37                text("links").link("https://aetna.dev"),
38                text("."),
39            ]),
40            text("Nested blocks live inside an item by composing a column."),
41        ]),
42        blockquote([
43            paragraph(
44                "Markdown's shape is HTML's shape. The Aetna widget kit \
45                 already mirrors most of that shape, so the transformer \
46                 mostly hands events to existing constructors.",
47            ),
48            paragraph("— Aetna design notes").muted(),
49        ]),
50        h3("Setup steps"),
51        numbered_list(vec![
52            text("Add `aetna-markdown` to the workspace."),
53            text_runs([
54                text("Pull in "),
55                text("pulldown-cmark").code(),
56                text(" with the GFM features the project actually uses."),
57            ]),
58            text("Wire the transformer through the existing widget kit — paragraph, list, blockquote, code_block, divider, table."),
59        ]),
60        h3("Example fenced block"),
61        code_block(
62            "fn render(md: &str) -> El {\n    \
63                 // pulldown-cmark events -> El\n    \
64                 todo!(\"phase 2\")\n}",
65        ),
66    ])
67    .gap(tokens::SPACE_4)
68    .padding(tokens::SPACE_7)
69    .width(Size::Fixed(640.0))
70}
Source

pub fn background(self, color: Color) -> Self

Inline-run background. Honoured when this El is a styled text leaf inside an crate::Kind::Inlines parent: the shaped span paints a solid quad behind its glyphs (per-line if the span wraps). Mirrors HTML’s <mark> / inline background; the rect tracks the glyph extent rather than the El’s layout box, so a wrapped highlight follows the prose. No effect on standalone text Els.

Examples found in repository?
examples/inline_runs_highlight.rs (line 38)
26fn fixture() -> El {
27    column([
28        h2("Inline run backgrounds"),
29        paragraph(
30            "RunStyle.bg paints a per-line solid quad behind the glyphs of \
31             a styled span — the shaper computes the rect from the actual \
32             glyph extents, so wrapping splits the highlight cleanly.",
33        )
34        .muted(),
35        // Search-result style.
36        text_runs([
37            text("…the matcher finds "),
38            text("aetna").background(HIGHLIGHT_YELLOW).bold(),
39            text(" in "),
40            text("aetna_core::widgets").mono(),
41            text(" — the highlight tracks the glyph extent."),
42        ])
43        .wrap_text()
44        .width(Size::Fill(1.0))
45        .height(Size::Hug),
46        // Diff-style: add + remove tints inside the same line.
47        text_runs([
48            text("- "),
49            text("error::Custom").mono().background(DIFF_REMOVE),
50            text("(\"too narrow\")"),
51            hard_break(),
52            text("+ "),
53            text("error::WrapTooNarrow").mono().background(DIFF_ADD),
54            text(" { available }"),
55        ])
56        .wrap_text()
57        .width(Size::Fill(1.0))
58        .height(Size::Hug),
59        // Wrapping highlight: long span that spans two lines.
60        text_runs([
61            text("Long highlight: "),
62            text("the quick brown fox jumps over the lazy dog and keeps going")
63                .background(HIGHLIGHT_YELLOW),
64            text(" — the rect is split per line."),
65        ])
66        .wrap_text()
67        .width(Size::Fill(1.0))
68        .height(Size::Hug),
69    ])
70    .gap(tokens::SPACE_4)
71    .padding(tokens::SPACE_7)
72    .width(Size::Fixed(640.0))
73}
Source

pub fn underline(self) -> Self

Underline styling for a text run.

Source

pub fn strikethrough(self) -> Self

Strikethrough styling for a text run.

Source

pub fn code(self) -> Self

Markdown-flavoured inline-code styling. Currently mono-styled; a tinted background per the theme is a future addition. Authors who want raw mono without code chrome should use Self::mono instead.

Examples found in repository?
examples/inline_runs.rs (line 29)
18fn fixture() -> El {
19    column([
20        h2("Inline runs"),
21        text_runs([
22            text("Aetna's attributed-text path lets you compose runs with "),
23            text("bold").bold(),
24            text(", "),
25            text("italic").italic(),
26            text(", "),
27            text("colored").color(tokens::DESTRUCTIVE),
28            text(", and "),
29            text("inline code").code(),
30            text(" segments inside one wrapping paragraph."),
31            hard_break(),
32            text("Hard breaks act like ").muted(),
33            text("<br>").code(),
34            text(" — they end the current line without breaking out of the run.").muted(),
35        ])
36        .wrap_text()
37        .width(Size::Fill(1.0))
38        .height(Size::Hug),
39        paragraph(
40            "All of the above flows through one cosmic-text rich-text shape — \
41             wrapping decisions cross run boundaries the way real prose wraps.",
42        )
43        .muted(),
44    ])
45    .gap(tokens::SPACE_4)
46    .padding(tokens::SPACE_7)
47    .width(Size::Fixed(640.0))
48}
More examples
Hide additional examples
examples/long_form_content.rs (line 35)
15fn fixture() -> El {
16    column([
17        h2("Long-form content widgets"),
18        paragraph(
19            "These primitives compose the markdown-shaped vocabulary an \
20             upcoming transformer will target. Each widget is plain \
21             Aetna — selectable text, themed surfaces, the same layout \
22             pass as everything else.",
23        ),
24        h3("Highlights"),
25        bullet_list(vec![
26            text_runs([
27                text("Bulleted lists with a hanging indent — wrapped lines align under "),
28                text("themselves").italic(),
29                text(", not under the marker."),
30            ]),
31            text_runs([
32                text("Inline runs work inside list items: "),
33                text("bold").bold(),
34                text(", "),
35                text("code").code(),
36                text(", "),
37                text("links").link("https://aetna.dev"),
38                text("."),
39            ]),
40            text("Nested blocks live inside an item by composing a column."),
41        ]),
42        blockquote([
43            paragraph(
44                "Markdown's shape is HTML's shape. The Aetna widget kit \
45                 already mirrors most of that shape, so the transformer \
46                 mostly hands events to existing constructors.",
47            ),
48            paragraph("— Aetna design notes").muted(),
49        ]),
50        h3("Setup steps"),
51        numbered_list(vec![
52            text("Add `aetna-markdown` to the workspace."),
53            text_runs([
54                text("Pull in "),
55                text("pulldown-cmark").code(),
56                text(" with the GFM features the project actually uses."),
57            ]),
58            text("Wire the transformer through the existing widget kit — paragraph, list, blockquote, code_block, divider, table."),
59        ]),
60        h3("Example fenced block"),
61        code_block(
62            "fn render(md: &str) -> El {\n    \
63                 // pulldown-cmark events -> El\n    \
64                 todo!(\"phase 2\")\n}",
65        ),
66    ])
67    .gap(tokens::SPACE_4)
68    .padding(tokens::SPACE_7)
69    .width(Size::Fixed(640.0))
70}

Mark this run as a link to url. Inside an crate::Kind::Inlines parent the run paints with a link-themed color; runs sharing the same URL group together for hit-test.

Examples found in repository?
examples/long_form_content.rs (line 37)
15fn fixture() -> El {
16    column([
17        h2("Long-form content widgets"),
18        paragraph(
19            "These primitives compose the markdown-shaped vocabulary an \
20             upcoming transformer will target. Each widget is plain \
21             Aetna — selectable text, themed surfaces, the same layout \
22             pass as everything else.",
23        ),
24        h3("Highlights"),
25        bullet_list(vec![
26            text_runs([
27                text("Bulleted lists with a hanging indent — wrapped lines align under "),
28                text("themselves").italic(),
29                text(", not under the marker."),
30            ]),
31            text_runs([
32                text("Inline runs work inside list items: "),
33                text("bold").bold(),
34                text(", "),
35                text("code").code(),
36                text(", "),
37                text("links").link("https://aetna.dev"),
38                text("."),
39            ]),
40            text("Nested blocks live inside an item by composing a column."),
41        ]),
42        blockquote([
43            paragraph(
44                "Markdown's shape is HTML's shape. The Aetna widget kit \
45                 already mirrors most of that shape, so the transformer \
46                 mostly hands events to existing constructors.",
47            ),
48            paragraph("— Aetna design notes").muted(),
49        ]),
50        h3("Setup steps"),
51        numbered_list(vec![
52            text("Add `aetna-markdown` to the workspace."),
53            text_runs([
54                text("Pull in "),
55                text("pulldown-cmark").code(),
56                text(" with the GFM features the project actually uses."),
57            ]),
58            text("Wire the transformer through the existing widget kit — paragraph, list, blockquote, code_block, divider, table."),
59        ]),
60        h3("Example fenced block"),
61        code_block(
62            "fn render(md: &str) -> El {\n    \
63                 // pulldown-cmark events -> El\n    \
64                 todo!(\"phase 2\")\n}",
65        ),
66    ])
67    .gap(tokens::SPACE_4)
68    .padding(tokens::SPACE_7)
69    .width(Size::Fixed(640.0))
70}
Source

pub fn math_expr(self, expr: impl Into<Arc<MathExpr>>) -> Self

Source

pub fn math_display(self, display: MathDisplay) -> Self

Source§

impl El

Source

pub fn new(kind: Kind) -> Self

Examples found in repository?
examples/dashboard_01_calibration.rs (line 304)
303fn bar(height: f32, color: Color) -> El {
304    El::new(Kind::Custom("chart_bar"))
305        .fill(color)
306        .radius(tokens::RADIUS_SM)
307        .width(Size::Fill(1.0))
308        .height(Size::Fixed(height))
309}
310
311fn sales_card() -> El {
312    card([
313        card_header([
314            card_title("Recent Sales"),
315            card_description("You made 265 sales this month."),
316        ])
317        .padding(tokens::SPACE_4),
318        card_content([
319            sale_row("OM", "Olivia Martin", "olivia@example.com", "+$1,999.00"),
320            sale_row("JL", "Jackson Lee", "jackson@example.com", "+$39.00"),
321            sale_row("IN", "Isabella Nguyen", "isabella@example.com", "+$299.00"),
322            sale_row("WK", "William Kim", "will@example.com", "+$99.00"),
323        ])
324        .gap(tokens::SPACE_2)
325        .padding(Sides {
326            left: tokens::SPACE_4,
327            right: tokens::SPACE_4,
328            top: 0.0,
329            bottom: tokens::SPACE_4,
330        }),
331    ])
332    .key("metric:sales.card")
333    .width(Size::Fixed(330.0))
334    .height(Size::Fill(1.0))
335}
336
337fn sale_row(
338    initials: &'static str,
339    name: &'static str,
340    email: &'static str,
341    amount: &'static str,
342) -> El {
343    row([
344        icon_cell(initials),
345        column([
346            text(name).semibold().ellipsis().width(Size::Fill(1.0)),
347            text(email).caption().ellipsis().width(Size::Fill(1.0)),
348        ])
349        .gap(2.0)
350        .height(Size::Hug)
351        .width(Size::Fill(1.0)),
352        text(amount).label().small(),
353    ])
354    .gap(tokens::SPACE_2)
355    .height(Size::Fixed(42.0))
356    .align(Align::Center)
357}
358
359fn documents_card() -> El {
360    card([
361        card_header([card_title("Documents")]).padding(tokens::SPACE_4),
362        card_content([scroll([table([
363            table_header([table_row([
364                table_head("").width(Size::Fixed(35.0)),
365                table_head("Header").width(Size::Fill(1.8)),
366                table_head("Section Type").width(Size::Fill(1.0)),
367                table_head("Status").width(Size::Fixed(104.0)),
368                table_head("Target").width(Size::Fixed(64.0)),
369                table_head("Limit").width(Size::Fixed(64.0)),
370                table_head("Reviewer").width(Size::Fixed(128.0)),
371                table_head("").width(Size::Fixed(32.0)),
372            ])
373            .padding(Sides::xy(tokens::SPACE_4, 0.0))
374            .key("metric:table.header")]),
375            divider(),
376            table_body([
377                document_row(
378                    "Cover page",
379                    "Cover page",
380                    "In Process",
381                    "18",
382                    "5",
383                    "Eddie Lake",
384                    "info",
385                ),
386                document_row(
387                    "Table of contents",
388                    "Table of contents",
389                    "Done",
390                    "29",
391                    "24",
392                    "Eddie Lake",
393                    "success",
394                ),
395            ]),
396        ])])
397        .height(Size::Fill(1.0))])
398        .gap(0.0)
399        .padding(0.0)
400        .height(Size::Fill(1.0)),
401    ])
402    .key("metric:table.card")
403    .height(Size::Fill(1.0))
404}
405
406fn document_row(
407    header: &'static str,
408    section: &'static str,
409    status: &'static str,
410    target: &'static str,
411    limit: &'static str,
412    reviewer: &'static str,
413    tone: &'static str,
414) -> El {
415    let status_badge = match tone {
416        "success" => badge(status).success(),
417        _ => badge(status).info(),
418    };
419    table_row([
420        table_utility_cell("::"),
421        table_cell(text(header).label().small()).width(Size::Fill(1.8)),
422        table_cell(text(section).muted()).width(Size::Fill(1.0)),
423        table_cell(status_badge).width(Size::Fixed(104.0)),
424        table_cell(text(target).label().small()).width(Size::Fixed(64.0)),
425        table_cell(text(limit).label().small()).width(Size::Fixed(64.0)),
426        table_cell(text(reviewer).muted()).width(Size::Fixed(128.0)),
427        table_action_cell(),
428    ])
429    .padding(Sides::xy(tokens::SPACE_4, 0.0))
430    .key(if header == "Cover page" {
431        "metric:table.row"
432    } else {
433        header
434    })
435}
436
437fn table_utility_cell(label: &'static str) -> El {
438    table_cell(text(label).muted().center_text()).width(Size::Fixed(35.0))
439}
440
441fn table_action_cell() -> El {
442    stack([icon("more-horizontal")
443        .icon_size(tokens::ICON_SM)
444        .color(tokens::MUTED_FOREGROUND)])
445    .align(Align::Center)
446    .justify(Justify::Center)
447    .width(Size::Fixed(32.0))
448    .height(Size::Hug)
449}
450
451fn icon_cell(label: &'static str) -> El {
452    El::new(Kind::Custom("icon_cell"))
453        .style_profile(StyleProfile::Surface)
454        .text(label)
455        .text_align(TextAlign::Center)
456        .caption()
457        .font_weight(FontWeight::Semibold)
458        .fill(tokens::MUTED)
459        .radius(tokens::RADIUS_SM)
460        .width(Size::Fixed(30.0))
461        .height(Size::Fixed(30.0))
462}
More examples
Hide additional examples
examples/polish_calibration.rs (line 356)
355fn icon_cell(label: &'static str) -> El {
356    El::new(Kind::Custom("icon_cell"))
357        .style_profile(StyleProfile::Surface)
358        .text(label)
359        .text_align(TextAlign::Center)
360        .caption()
361        .font_weight(FontWeight::Semibold)
362        .fill(tokens::MUTED)
363        .stroke(tokens::BORDER)
364        .radius(tokens::RADIUS_SM)
365        .width(Size::Fixed(26.0))
366        .height(Size::Fixed(26.0))
367}
examples/custom_paint.rs (line 56)
44fn graph_cell(lane: u8, selected: bool) -> El {
45    let lane_color = lane_palette(lane);
46    let ring_color = if selected {
47        Color::rgb(245, 245, 250)
48    } else {
49        lane_color
50    };
51    let ring_w = if selected { 2.5 } else { 1.5 };
52    let radius = 5.0;
53    let line_w = 2.0;
54    let lane_frac = (lane as f32 + 0.5) / LANE_COUNT as f32;
55
56    El::new(Kind::Custom("graph_cell"))
57        .width(Size::Fixed(GRAPH_WIDTH))
58        .height(Size::Fixed(ROW_HEIGHT))
59        .shader(
60            ShaderBinding::custom("commit_node")
61                .color("vec_a", tokens::BACKGROUND)
62                .color("vec_b", ring_color)
63                .vec4("vec_c", [radius, ring_w, line_w, lane_frac]),
64        )
65        .fill(lane_color)
66}
examples/settings_calibration.rs (line 136)
134fn settings_nav_item(label: &'static str, selected: bool) -> El {
135    let mut item = row([
136        El::new(Kind::Custom("nav-dot"))
137            .fill(tokens::MUTED_FOREGROUND)
138            .radius(tokens::RADIUS_PILL)
139            .width(Size::Fixed(6.0))
140            .height(Size::Fixed(6.0)),
141        text(label)
142            .font_weight(FontWeight::Medium)
143            .ellipsis()
144            .width(Size::Fill(1.0)),
145    ])
146    .key(if selected {
147        "metric:settings.nav.row".to_string()
148    } else {
149        format!("settings-nav-{label}")
150    })
151    .metrics_role(MetricsRole::ListItem)
152    .align(Align::Center)
153    .focusable();
154
155    if selected {
156        item = item.current();
157    } else {
158        item = item.color(tokens::MUTED_FOREGROUND);
159    }
160
161    item
162}
163
164fn settings_body() -> El {
165    column([
166        column([
167            h1("Account").heading().key("metric:section.title"),
168            text("Manage identity, workspace defaults, and security preferences.")
169                .muted()
170                .wrap_text()
171                .key("metric:page.subtitle"),
172        ])
173        .gap(tokens::SPACE_1)
174        .height(Size::Hug),
175        scroll([profile_card(), preferences_card()])
176            .key("settings-body-scroll")
177            .gap(tokens::SPACE_4)
178            .width(Size::Fill(1.0))
179            .height(Size::Fill(1.0)),
180    ])
181    .gap(tokens::SPACE_4)
182    .width(Size::Fill(1.0))
183    .height(Size::Fill(1.0))
184}
185
186fn profile_card() -> El {
187    card([
188        card_header([
189            card_title("Profile"),
190            card_description("This information appears in audit logs and shared documents."),
191        ]),
192        card_content([form([
193            row([
194                setting_field("Display name", "Alicia Koch", "display-name"),
195                setting_field("Email", "alicia@acme.co", "email"),
196            ])
197            .gap(tokens::SPACE_3),
198            row([
199                setting_select("Role", "Workspace admin", "role"),
200                setting_select("Region", "US East", "region"),
201            ])
202            .gap(tokens::SPACE_3),
203        ])]),
204    ])
205    .key("metric:profile.card")
206}
207
208fn setting_field(label: &'static str, value: &'static str, key: &'static str) -> El {
209    form_item([
210        form_label(label),
211        form_control(
212            text_input(value, &Selection::caret(key, value.len()), key).key(
213                if key == "display-name" {
214                    "metric:form.input"
215                } else {
216                    key
217                },
218            ),
219        ),
220    ])
221    .width(Size::Fill(1.0))
222}
223
224fn setting_select(label: &'static str, value: &'static str, key: &'static str) -> El {
225    form_item([form_label(label), form_control(select_trigger(key, value))]).width(Size::Fill(1.0))
226}
227
228fn preferences_card() -> El {
229    card([
230        card_header([
231            card_title("Preferences"),
232            card_description("Defaults used when creating new dashboards and exports."),
233        ]),
234        card_content([column([
235            preference_row(
236                "Compact navigation",
237                "Use tighter rows in the sidebar and command menus.",
238                switch(true).key("compact-navigation"),
239            ),
240            divider(),
241            preference_row(
242                "Email summaries",
243                "Send a daily digest when documents change.",
244                switch(false).key("email-summaries"),
245            ),
246            divider(),
247            preference_row(
248                "Require approval",
249                "Route external sharing through an owner review.",
250                checkbox(true).key("approval-required"),
251            ),
252        ])
253        .gap(0.0)
254        .width(Size::Fill(1.0))])
255        .padding(0.0),
256    ])
257    .key("metric:preferences.card")
258}
259
260fn preference_row(title: &'static str, description: &'static str, control: El) -> El {
261    row([
262        column([
263            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
264            text(description)
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0))
271        .height(Size::Hug),
272        control,
273    ])
274    .key(if title == "Compact navigation" {
275        "metric:preference.row".to_string()
276    } else {
277        format!("preference-{title}")
278    })
279    .metrics_role(MetricsRole::PreferenceRow)
280    .gap(tokens::SPACE_4)
281    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_3))
282    .align(Align::Center)
283}
284
285fn settings_aside() -> El {
286    column([security_card(), scale_card()])
287        .gap(tokens::SPACE_4)
288        .width(Size::Fixed(300.0))
289        .height(Size::Fill(1.0))
290}
291
292fn security_card() -> El {
293    card([
294        card_header([
295            card_title("Security"),
296            card_description("Two-factor authentication is enabled for all privileged users."),
297        ]),
298        card_content([
299            compact_stat("Passkeys", "2 registered", badge("On").success()),
300            compact_stat("Sessions", "3 active", button("Review").secondary()),
301        ]),
302    ])
303    .width(Size::Fill(1.0))
304}
305
306fn scale_card() -> El {
307    card([
308        card_header([
309            card_title("Interface scale"),
310            card_description("Reference captures keep browser zoom fixed and vary root UI scale."),
311        ]),
312        card_content([
313            row([text("Dense").caption(), spacer(), text("Default").caption()]),
314            slider(0.66, tokens::PRIMARY)
315                .key("interface-scale")
316                .width(Size::Fill(1.0)),
317        ]),
318    ])
319    .width(Size::Fill(1.0))
320}
321
322fn compact_stat(title: &'static str, detail: &'static str, control: El) -> El {
323    row([
324        column([
325            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
326            text(detail).caption().ellipsis().width(Size::Fill(1.0)),
327        ])
328        .gap(2.0)
329        .width(Size::Fill(1.0))
330        .height(Size::Hug),
331        control,
332    ])
333    .gap(tokens::SPACE_2)
334    .height(Size::Fixed(44.0))
335    .align(Align::Center)
336}
337
338fn section_label(label: &'static str) -> El {
339    text(label)
340        .caption()
341        .height(Size::Fixed(22.0))
342        .padding(Sides::xy(tokens::SPACE_2, 0.0))
343}
344
345fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
346    let mut item = row([
347        icon(icon_name)
348            .color(tokens::MUTED_FOREGROUND)
349            .icon_size(tokens::ICON_SM)
350            .width(Size::Fixed(tokens::ICON_SM)),
351        text(label)
352            .font_weight(FontWeight::Medium)
353            .ellipsis()
354            .width(Size::Fill(1.0)),
355    ])
356    .key(if selected {
357        "metric:sidebar.nav.row".to_string()
358    } else {
359        format!("side-item-{label}")
360    })
361    .metrics_role(MetricsRole::ListItem)
362    .gap(tokens::SPACE_2)
363    .padding(Sides::xy(tokens::SPACE_2, 0.0))
364    .height(Size::Fixed(32.0))
365    .align(Align::Center)
366    .focusable();
367
368    if selected {
369        item = item.current();
370    } else {
371        item = item.color(tokens::MUTED_FOREGROUND);
372    }
373
374    item
375}
376
377fn icon_slot(icon_name: &'static str) -> El {
378    El::new(Kind::Custom("icon_cell"))
379        .style_profile(StyleProfile::Surface)
380        .child(
381            icon(icon_name)
382                .color(tokens::FOREGROUND)
383                .icon_size(tokens::ICON_XS),
384        )
385        .align(Align::Center)
386        .justify(Justify::Center)
387        .fill(tokens::MUTED)
388        .stroke(tokens::BORDER)
389        .radius(tokens::RADIUS_SM)
390        .width(Size::Fixed(30.0))
391        .height(Size::Fixed(30.0))
392}
examples/palette_demo.rs (line 285)
282fn token_chip(token: TokenDef, palette: &Palette) -> El {
283    let resolved = palette.resolve(token.color);
284    row([
285        El::new(Kind::Custom("palette-swatch"))
286            .at(file!(), line!())
287            .fill(token.color)
288            .stroke(tokens::BORDER)
289            .radius(tokens::RADIUS_SM)
290            .width(Size::Fixed(42.0))
291            .height(Size::Fixed(34.0)),
292        column([
293            text(token.name)
294                .label()
295                .ellipsis()
296                .nowrap_text()
297                .width(Size::Fill(1.0)),
298            mono(rgba_label(resolved)).caption().muted(),
299        ])
300        .gap(0.0)
301        .width(Size::Fill(1.0))
302        .height(Size::Hug),
303    ])
304    .gap(tokens::SPACE_2)
305    .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_2))
306    .align(Align::Center)
307    .fill(tokens::CARD)
308    .stroke(tokens::BORDER)
309    .radius(tokens::RADIUS_MD)
310    .width(Size::Fill(1.0))
311    .height(Size::Fixed(54.0))
312}
Source

pub fn key(self, k: impl Into<String>) -> Self

Examples found in repository?
examples/polish_calibration.rs (line 37)
35fn polish_calibration() -> El {
36    row([sidebar(), main_panel()])
37        .key("metric:root")
38        .gap(0.0)
39        .fill_size()
40        .align(Align::Stretch)
41        .fill(tokens::BACKGROUND)
42}
43
44fn sidebar() -> El {
45    column([
46        column([h2("Aetna"), text("calibration").muted()])
47            .key("metric:sidebar.brand")
48            .gap(tokens::SPACE_1)
49            .height(Size::Hug),
50        spacer().height(Size::Fixed(tokens::SPACE_4)),
51        nav_item("01", "Overview", true),
52        nav_item("02", "Commands", false),
53        nav_item("03", "Tables", false),
54        nav_item("04", "Forms", false),
55        spacer(),
56        badge("dark theme").muted(),
57    ])
58    .gap(tokens::SPACE_2)
59    .padding(tokens::SPACE_5)
60    .key("metric:sidebar")
61    .width(Size::Fixed(220.0))
62    .height(Size::Fill(1.0))
63    .fill(tokens::CARD)
64    .stroke(tokens::BORDER)
65}
66
67fn nav_item(icon: &'static str, label: &'static str, selected: bool) -> El {
68    let mut item = row([
69        icon_cell(icon),
70        text(label)
71            .font_weight(FontWeight::Medium)
72            .ellipsis()
73            .width(Size::Fill(1.0)),
74    ])
75    .key(if selected {
76        "metric:sidebar.nav.row".to_string()
77    } else {
78        format!("nav-{label}")
79    })
80    .metrics_role(MetricsRole::ListItem)
81    .gap(tokens::SPACE_3)
82    .padding(Sides::xy(tokens::SPACE_2, 0.0))
83    .height(Size::Fixed(40.0))
84    .align(Align::Center)
85    .focusable();
86
87    if selected {
88        item = item.current();
89    }
90
91    item
92}
93
94fn main_panel() -> El {
95    column([
96        toolbar(),
97        column([
98            row([
99                kpi_card("Latency", "42 ms", "-18%", true),
100                kpi_card("Runs", "1,284", "+12%", true),
101                kpi_card("Errors", "7", "+2", false),
102            ])
103            .gap(tokens::SPACE_4),
104            row([table_card(), command_card()])
105                .gap(tokens::SPACE_4)
106                .height(Size::Fill(1.0))
107                .align(Align::Stretch),
108        ])
109        .gap(tokens::SPACE_4)
110        .height(Size::Fill(1.0))
111        .align(Align::Stretch),
112    ])
113    .padding(tokens::SPACE_7)
114    .gap(tokens::SPACE_2)
115    .width(Size::Fill(1.0))
116    .height(Size::Fill(1.0))
117}
118
119fn toolbar() -> El {
120    row([
121        column([
122            h1("Polish calibration").key("metric:page.title"),
123            text("A representative app surface for default tuning.")
124                .muted()
125                .key("metric:page.subtitle"),
126        ])
127        .gap(tokens::SPACE_2)
128        .height(Size::Hug),
129        spacer(),
130        button_with_icon("search", "Preview")
131            .secondary()
132            .key("metric:action.secondary"),
133        button_with_icon("upload", "Publish")
134            .primary()
135            .key("metric:action.primary"),
136    ])
137    .key("metric:header")
138    .gap(tokens::SPACE_4)
139    .height(Size::Hug)
140    .align(Align::Start)
141}
142
143fn kpi_card(label: &'static str, value: &'static str, delta: &'static str, positive: bool) -> El {
144    let delta_badge = if positive {
145        badge(delta).success()
146    } else {
147        badge(delta).destructive()
148    };
149    let delta_badge = if label == "Latency" {
150        delta_badge.key("metric:kpi.badge")
151    } else {
152        delta_badge
153    };
154    let value_text = h2(value).display();
155    let value_text = if label == "Latency" {
156        value_text.key("metric:kpi.value")
157    } else {
158        value_text
159    };
160    card([
161        card_header([card_title(label)]),
162        card_content([
163            row([value_text, spacer(), delta_badge]).align(Align::Center),
164            text(if positive {
165                "Moving in the expected direction"
166            } else {
167                "Needs visual attention"
168            })
169            .muted(),
170        ])
171        .gap(tokens::SPACE_6),
172    ])
173    .key(if label == "Latency" {
174        "metric:kpi.card"
175    } else {
176        label
177    })
178    .width(Size::Fill(1.0))
179}
180
181fn table_card() -> El {
182    card([
183        card_header([card_title("Reference rows")]),
184        card_content([table([
185            table_header([table_row([
186                table_head("Status").width(Size::Fixed(86.0)),
187                table_head("Surface").width(Size::Fill(1.0)),
188                table_head("Owner").width(Size::Fixed(110.0)),
189                table_head("State").width(Size::Fixed(86.0)),
190            ])
191            .key("metric:table.header")]),
192            divider(),
193            table_body([
194                data_row("OK", "Settings card", "core", "selected", true, "success"),
195                data_row(
196                    "WARN",
197                    "Command palette density",
198                    "widgets",
199                    "needs work",
200                    false,
201                    "warning",
202                ),
203                data_row(
204                    "ERR",
205                    "Disabled and invalid states",
206                    "style",
207                    "missing",
208                    false,
209                    "destructive",
210                ),
211                data_row(
212                    "INFO",
213                    "Token resolution",
214                    "theme",
215                    "planned",
216                    false,
217                    "info",
218                ),
219                data_row(
220                    "OK",
221                    "Popover elevation",
222                    "shader",
223                    "queued",
224                    false,
225                    "success",
226                ),
227            ])
228            .gap(tokens::SPACE_1)
229            .width(Size::Fill(1.0)),
230        ])]),
231    ])
232    .key("metric:table.card")
233    .width(Size::Fill(1.2))
234    .height(Size::Fill(1.0))
235}
236
237fn data_row(
238    status: &'static str,
239    title: &'static str,
240    owner: &'static str,
241    state: &'static str,
242    selected: bool,
243    tone: &'static str,
244) -> El {
245    let status_badge = match tone {
246        "success" => badge(status).success(),
247        "warning" => badge(status).warning(),
248        "destructive" => badge(status).destructive(),
249        _ => badge(status).info(),
250    };
251    let status_badge = if selected {
252        status_badge.key("metric:table.badge")
253    } else {
254        status_badge
255    };
256
257    let mut row = table_row([
258        table_cell(status_badge).width(Size::Fixed(70.0)),
259        column([
260            text(title)
261                .font_weight(FontWeight::Medium)
262                .ellipsis()
263                .width(Size::Fill(1.0)),
264            text("Default styling probe.")
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0)),
271        table_cell(text(owner).muted()).width(Size::Fixed(110.0)),
272        table_cell(text(state).label().small()).width(Size::Fixed(86.0)),
273    ])
274    .key(if selected {
275        "metric:table.row".to_string()
276    } else {
277        format!("row-{title}")
278    })
279    .focusable();
280
281    if selected {
282        row = row.selected();
283    }
284
285    row
286}
287
288fn command_card() -> El {
289    card([
290        card_header([card_title("Command surface")]),
291        card_content([
292            text_input(
293                "Search commands...",
294                &Selection::default(),
295                "command-search",
296            )
297            .key("metric:command.input")
298            .width(Size::Fill(1.0)),
299            popover_panel([
300                command_row("git-branch", "New branch", "Ctrl+B").key("metric:command.row"),
301                command_row("git-commit", "Commit staged files", "Ctrl+Enter")
302                    .key("command-row-commit"),
303                command_row("refresh-cw", "Refresh repository", "Ctrl+R")
304                    .key("command-row-refresh"),
305                command_row("alert-circle", "Force push", "Danger").key("command-row-force"),
306            ])
307            .width(Size::Fill(1.0)),
308            scroll([form_probe()]).key("form-probe-scroll"),
309        ])
310        .height(Size::Fill(1.0)),
311    ])
312    .key("metric:command.card")
313    .width(Size::Fill(0.8))
314    .height(Size::Fill(1.0))
315}
316
317fn form_probe() -> El {
318    form([
319        form_item([
320            form_label("Valid input"),
321            form_control(
322                text_input(
323                    "Valid input",
324                    &Selection::caret("valid-input", 11),
325                    "valid-input",
326                )
327                .key("metric:form.input"),
328            ),
329            form_description("Default field spacing and helper text."),
330        ]),
331        form_item([
332            form_label("Invalid input"),
333            form_control(
334                text_input(
335                    "Invalid input",
336                    &Selection::caret("invalid-input", 13),
337                    "invalid-input",
338                )
339                .invalid(),
340            ),
341            form_message("This field needs attention."),
342        ]),
343        row([
344            button("Disabled").secondary().disabled(),
345            button("Loading").primary().loading(),
346            spacer(),
347        ]),
348    ])
349    .padding(tokens::SPACE_3)
350    .fill(tokens::MUTED)
351    .stroke(tokens::BORDER)
352    .radius(tokens::RADIUS_MD)
353}
More examples
Hide additional examples
examples/settings_calibration.rs (line 34)
32fn settings_calibration() -> El {
33    row([settings_sidebar(), settings_main()])
34        .key("metric:root")
35        .gap(0.0)
36        .fill_size()
37        .align(Align::Stretch)
38        .fill(tokens::BACKGROUND)
39}
40
41fn settings_sidebar() -> El {
42    column([
43        row([
44            icon_slot("settings"),
45            column([
46                text("Workspace")
47                    .semibold()
48                    .ellipsis()
49                    .width(Size::Fill(1.0)),
50                text("Settings").caption().ellipsis().width(Size::Fill(1.0)),
51            ])
52            .gap(2.0)
53            .width(Size::Fill(1.0))
54            .height(Size::Hug),
55        ])
56        .gap(tokens::SPACE_2)
57        .height(Size::Fixed(44.0))
58        .align(Align::Center),
59        section_label("Personal"),
60        side_item("users", "Profile", false),
61        side_item("settings", "Account", true),
62        side_item("alert-circle", "Security", false),
63        side_item("bell", "Notifications", false),
64        spacer().height(Size::Fixed(tokens::SPACE_4)),
65        section_label("Workspace"),
66        side_item("file-text", "Billing", false),
67        side_item("bar-chart", "Appearance", false),
68        side_item("activity", "Integrations", false),
69        spacer(),
70        column([text("Changes sync after save.").caption().wrap_text()])
71            .padding(tokens::SPACE_2)
72            .fill(tokens::MUTED)
73            .radius(tokens::RADIUS_MD),
74    ])
75    .gap(tokens::SPACE_2)
76    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_2))
77    .key("metric:sidebar")
78    .width(Size::Fixed(244.0))
79    .height(Size::Fill(1.0))
80    .fill(tokens::CARD)
81    .stroke(tokens::BORDER)
82}
83
84fn settings_main() -> El {
85    column([
86        settings_header(),
87        row([settings_nav_card(), settings_body(), settings_aside()])
88            .gap(tokens::SPACE_4)
89            .padding(tokens::SPACE_4)
90            .height(Size::Fill(1.0))
91            .align(Align::Stretch),
92    ])
93    .width(Size::Fill(1.0))
94    .height(Size::Fill(1.0))
95}
96
97fn settings_header() -> El {
98    row([
99        icon_button("menu").ghost(),
100        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
101        h3("Settings").key("metric:page.title"),
102        spacer(),
103        button("Reset").secondary(),
104        button("Save changes").primary(),
105    ])
106    .key("metric:header")
107    .gap(tokens::SPACE_3)
108    .height(Size::Fixed(56.0))
109    .padding(Sides::xy(tokens::SPACE_4, 0.0))
110    .align(Align::Center)
111    .stroke(tokens::BORDER)
112}
113
114fn settings_nav_card() -> El {
115    column([
116        settings_nav_item("Account", true),
117        settings_nav_item("Security", false),
118        settings_nav_item("Notifications", false),
119        settings_nav_item("Appearance", false),
120        settings_nav_item("Billing", false),
121    ])
122    .gap(tokens::SPACE_1)
123    .padding(tokens::SPACE_1)
124    .width(Size::Fixed(220.0))
125    .height(Size::Fill(1.0))
126    .style_profile(StyleProfile::Surface)
127    .surface_role(SurfaceRole::Panel)
128    .fill(tokens::CARD)
129    .stroke(tokens::BORDER)
130    .radius(tokens::RADIUS_MD)
131    .shadow(tokens::SHADOW_MD)
132}
133
134fn settings_nav_item(label: &'static str, selected: bool) -> El {
135    let mut item = row([
136        El::new(Kind::Custom("nav-dot"))
137            .fill(tokens::MUTED_FOREGROUND)
138            .radius(tokens::RADIUS_PILL)
139            .width(Size::Fixed(6.0))
140            .height(Size::Fixed(6.0)),
141        text(label)
142            .font_weight(FontWeight::Medium)
143            .ellipsis()
144            .width(Size::Fill(1.0)),
145    ])
146    .key(if selected {
147        "metric:settings.nav.row".to_string()
148    } else {
149        format!("settings-nav-{label}")
150    })
151    .metrics_role(MetricsRole::ListItem)
152    .align(Align::Center)
153    .focusable();
154
155    if selected {
156        item = item.current();
157    } else {
158        item = item.color(tokens::MUTED_FOREGROUND);
159    }
160
161    item
162}
163
164fn settings_body() -> El {
165    column([
166        column([
167            h1("Account").heading().key("metric:section.title"),
168            text("Manage identity, workspace defaults, and security preferences.")
169                .muted()
170                .wrap_text()
171                .key("metric:page.subtitle"),
172        ])
173        .gap(tokens::SPACE_1)
174        .height(Size::Hug),
175        scroll([profile_card(), preferences_card()])
176            .key("settings-body-scroll")
177            .gap(tokens::SPACE_4)
178            .width(Size::Fill(1.0))
179            .height(Size::Fill(1.0)),
180    ])
181    .gap(tokens::SPACE_4)
182    .width(Size::Fill(1.0))
183    .height(Size::Fill(1.0))
184}
185
186fn profile_card() -> El {
187    card([
188        card_header([
189            card_title("Profile"),
190            card_description("This information appears in audit logs and shared documents."),
191        ]),
192        card_content([form([
193            row([
194                setting_field("Display name", "Alicia Koch", "display-name"),
195                setting_field("Email", "alicia@acme.co", "email"),
196            ])
197            .gap(tokens::SPACE_3),
198            row([
199                setting_select("Role", "Workspace admin", "role"),
200                setting_select("Region", "US East", "region"),
201            ])
202            .gap(tokens::SPACE_3),
203        ])]),
204    ])
205    .key("metric:profile.card")
206}
207
208fn setting_field(label: &'static str, value: &'static str, key: &'static str) -> El {
209    form_item([
210        form_label(label),
211        form_control(
212            text_input(value, &Selection::caret(key, value.len()), key).key(
213                if key == "display-name" {
214                    "metric:form.input"
215                } else {
216                    key
217                },
218            ),
219        ),
220    ])
221    .width(Size::Fill(1.0))
222}
223
224fn setting_select(label: &'static str, value: &'static str, key: &'static str) -> El {
225    form_item([form_label(label), form_control(select_trigger(key, value))]).width(Size::Fill(1.0))
226}
227
228fn preferences_card() -> El {
229    card([
230        card_header([
231            card_title("Preferences"),
232            card_description("Defaults used when creating new dashboards and exports."),
233        ]),
234        card_content([column([
235            preference_row(
236                "Compact navigation",
237                "Use tighter rows in the sidebar and command menus.",
238                switch(true).key("compact-navigation"),
239            ),
240            divider(),
241            preference_row(
242                "Email summaries",
243                "Send a daily digest when documents change.",
244                switch(false).key("email-summaries"),
245            ),
246            divider(),
247            preference_row(
248                "Require approval",
249                "Route external sharing through an owner review.",
250                checkbox(true).key("approval-required"),
251            ),
252        ])
253        .gap(0.0)
254        .width(Size::Fill(1.0))])
255        .padding(0.0),
256    ])
257    .key("metric:preferences.card")
258}
259
260fn preference_row(title: &'static str, description: &'static str, control: El) -> El {
261    row([
262        column([
263            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
264            text(description)
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0))
271        .height(Size::Hug),
272        control,
273    ])
274    .key(if title == "Compact navigation" {
275        "metric:preference.row".to_string()
276    } else {
277        format!("preference-{title}")
278    })
279    .metrics_role(MetricsRole::PreferenceRow)
280    .gap(tokens::SPACE_4)
281    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_3))
282    .align(Align::Center)
283}
284
285fn settings_aside() -> El {
286    column([security_card(), scale_card()])
287        .gap(tokens::SPACE_4)
288        .width(Size::Fixed(300.0))
289        .height(Size::Fill(1.0))
290}
291
292fn security_card() -> El {
293    card([
294        card_header([
295            card_title("Security"),
296            card_description("Two-factor authentication is enabled for all privileged users."),
297        ]),
298        card_content([
299            compact_stat("Passkeys", "2 registered", badge("On").success()),
300            compact_stat("Sessions", "3 active", button("Review").secondary()),
301        ]),
302    ])
303    .width(Size::Fill(1.0))
304}
305
306fn scale_card() -> El {
307    card([
308        card_header([
309            card_title("Interface scale"),
310            card_description("Reference captures keep browser zoom fixed and vary root UI scale."),
311        ]),
312        card_content([
313            row([text("Dense").caption(), spacer(), text("Default").caption()]),
314            slider(0.66, tokens::PRIMARY)
315                .key("interface-scale")
316                .width(Size::Fill(1.0)),
317        ]),
318    ])
319    .width(Size::Fill(1.0))
320}
321
322fn compact_stat(title: &'static str, detail: &'static str, control: El) -> El {
323    row([
324        column([
325            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
326            text(detail).caption().ellipsis().width(Size::Fill(1.0)),
327        ])
328        .gap(2.0)
329        .width(Size::Fill(1.0))
330        .height(Size::Hug),
331        control,
332    ])
333    .gap(tokens::SPACE_2)
334    .height(Size::Fixed(44.0))
335    .align(Align::Center)
336}
337
338fn section_label(label: &'static str) -> El {
339    text(label)
340        .caption()
341        .height(Size::Fixed(22.0))
342        .padding(Sides::xy(tokens::SPACE_2, 0.0))
343}
344
345fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
346    let mut item = row([
347        icon(icon_name)
348            .color(tokens::MUTED_FOREGROUND)
349            .icon_size(tokens::ICON_SM)
350            .width(Size::Fixed(tokens::ICON_SM)),
351        text(label)
352            .font_weight(FontWeight::Medium)
353            .ellipsis()
354            .width(Size::Fill(1.0)),
355    ])
356    .key(if selected {
357        "metric:sidebar.nav.row".to_string()
358    } else {
359        format!("side-item-{label}")
360    })
361    .metrics_role(MetricsRole::ListItem)
362    .gap(tokens::SPACE_2)
363    .padding(Sides::xy(tokens::SPACE_2, 0.0))
364    .height(Size::Fixed(32.0))
365    .align(Align::Center)
366    .focusable();
367
368    if selected {
369        item = item.current();
370    } else {
371        item = item.color(tokens::MUTED_FOREGROUND);
372    }
373
374    item
375}
examples/dashboard_01_calibration.rs (line 34)
32fn dashboard_01_calibration() -> El {
33    row([dashboard_sidebar(), dashboard_main()])
34        .key("metric:root")
35        .gap(0.0)
36        .fill_size()
37        .align(Align::Stretch)
38        .fill(tokens::BACKGROUND)
39}
40
41fn dashboard_sidebar() -> El {
42    column([
43        row([
44            icon_cell("A"),
45            column([
46                text("Acme Inc.")
47                    .semibold()
48                    .ellipsis()
49                    .width(Size::Fill(1.0)),
50                text("Enterprise")
51                    .caption()
52                    .ellipsis()
53                    .width(Size::Fill(1.0)),
54            ])
55            .gap(2.0)
56            .width(Size::Fill(1.0))
57            .height(Size::Hug),
58        ])
59        .gap(tokens::SPACE_2)
60        .height(Size::Fixed(44.0))
61        .align(Align::Center),
62        section_label("Platform"),
63        side_item("layout-dashboard", "Dashboard", true),
64        side_item("activity", "Lifecycle", false),
65        side_item("bar-chart", "Analytics", false),
66        side_item("folder", "Projects", false),
67        spacer().height(Size::Fixed(tokens::SPACE_4)),
68        section_label("Documents"),
69        side_item("file-text", "Data library", false),
70        side_item("download", "Reports", false),
71        side_item("users", "Team", false),
72        spacer(),
73        row([
74            icon_cell("AK"),
75            column([
76                text("Alicia Koch")
77                    .semibold()
78                    .ellipsis()
79                    .width(Size::Fill(1.0)),
80                text("alicia@example.com")
81                    .caption()
82                    .ellipsis()
83                    .width(Size::Fill(1.0)),
84            ])
85            .gap(2.0)
86            .width(Size::Fill(1.0))
87            .height(Size::Hug),
88        ])
89        .gap(tokens::SPACE_2)
90        .height(Size::Fixed(50.0))
91        .align(Align::Center),
92    ])
93    .gap(tokens::SPACE_2)
94    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_2))
95    .key("metric:sidebar")
96    .width(Size::Fixed(244.0))
97    .height(Size::Fill(1.0))
98    .fill(tokens::CARD)
99    .stroke(tokens::BORDER)
100}
101
102fn section_label(label: &'static str) -> El {
103    text(label)
104        .caption()
105        .height(Size::Fixed(22.0))
106        .padding(Sides::xy(tokens::SPACE_2, 0.0))
107}
108
109fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
110    let mut item = row([
111        icon(icon_name)
112            .color(tokens::MUTED_FOREGROUND)
113            .icon_size(tokens::ICON_SM)
114            .width(Size::Fixed(tokens::ICON_SM)),
115        text(label)
116            .font_weight(FontWeight::Medium)
117            .ellipsis()
118            .width(Size::Fill(1.0)),
119    ])
120    .key(if selected {
121        "metric:sidebar.nav.row".to_string()
122    } else {
123        format!("side-item-{label}")
124    })
125    .metrics_role(MetricsRole::ListItem)
126    .gap(tokens::SPACE_2)
127    .padding(Sides::xy(tokens::SPACE_2, 0.0))
128    .height(Size::Fixed(32.0))
129    .align(Align::Center)
130    .focusable();
131
132    if selected {
133        item = item.current();
134    } else {
135        item = item.color(tokens::MUTED_FOREGROUND);
136    }
137
138    item
139}
140
141fn dashboard_main() -> El {
142    column([
143        dashboard_header(),
144        column([
145            row([
146                metric_card(
147                    "bar-chart",
148                    "Total Revenue",
149                    "$1,250.00",
150                    "+12.5%",
151                    "Trending up this month",
152                    true,
153                ),
154                metric_card(
155                    "users",
156                    "New Customers",
157                    "1,234",
158                    "-20%",
159                    "Acquisition needs attention",
160                    false,
161                ),
162                metric_card(
163                    "folder",
164                    "Active Accounts",
165                    "45,678",
166                    "+12.5%",
167                    "Strong user retention",
168                    true,
169                ),
170                metric_card(
171                    "activity",
172                    "Growth Rate",
173                    "4.5%",
174                    "+4.5%",
175                    "Meets growth projections",
176                    true,
177                ),
178            ])
179            .gap(tokens::SPACE_4),
180            row([chart_card(), sales_card()])
181                .gap(tokens::SPACE_4)
182                .height(Size::Fixed(306.0))
183                .align(Align::Stretch),
184            documents_card(),
185        ])
186        .gap(tokens::SPACE_4)
187        .padding(tokens::SPACE_7)
188        .height(Size::Fill(1.0)),
189    ])
190    .width(Size::Fill(1.0))
191    .height(Size::Fill(1.0))
192}
193
194fn dashboard_header() -> El {
195    row([
196        icon_button("menu").ghost(),
197        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
198        h3("Documents").key("metric:page.title"),
199        spacer(),
200        text_input("Search...", &Selection::default(), "dashboard-search")
201            .key("metric:command.input")
202            .width(Size::Fixed(260.0)),
203        icon_button("plus").ghost(),
204        icon_button("bell").ghost(),
205    ])
206    .key("metric:header")
207    .gap(tokens::SPACE_3)
208    .height(Size::Fixed(56.0))
209    .padding(Sides::xy(tokens::SPACE_4, 0.0))
210    .align(Align::Center)
211    .stroke(tokens::BORDER)
212}
213
214fn metric_card(
215    icon_name: &'static str,
216    title: &'static str,
217    value: &'static str,
218    delta: &'static str,
219    note: &'static str,
220    positive: bool,
221) -> El {
222    let badge = if positive {
223        badge(delta).success()
224    } else {
225        badge(delta).warning()
226    };
227    let badge = if title == "Total Revenue" {
228        badge.key("metric:kpi.badge")
229    } else {
230        badge
231    };
232    let value = if title == "Total Revenue" {
233        h2(value).ellipsis().key("metric:kpi.value")
234    } else {
235        h2(value).ellipsis()
236    };
237    card([card_content([
238        row([
239            row([
240                icon(icon_name)
241                    .color(tokens::MUTED_FOREGROUND)
242                    .icon_size(tokens::ICON_XS),
243                text(title).muted().ellipsis().width(Size::Fill(1.0)),
244            ])
245            .gap(tokens::SPACE_1)
246            .width(Size::Fill(1.0))
247            .align(Align::Center),
248            badge,
249        ])
250        .gap(tokens::SPACE_2)
251        .align(Align::Center),
252        value,
253        text(note).caption().ellipsis().width(Size::Fill(1.0)),
254    ])
255    .padding(tokens::SPACE_4)
256    .gap(tokens::SPACE_2)])
257    .key(if title == "Total Revenue" {
258        "metric:kpi.card"
259    } else {
260        title
261    })
262    .width(Size::Fill(1.0))
263}
264
265fn chart_card() -> El {
266    card([
267        card_header([
268            card_title("Visitors for the last 6 months"),
269            card_description("Total visitors by channel."),
270        ])
271        .padding(tokens::SPACE_4),
272        card_content([row(chart_bars())
273            .gap(2.0)
274            .height(Size::Fill(1.0))
275            .align(Align::End)])
276        .padding(Sides {
277            left: tokens::SPACE_4,
278            right: tokens::SPACE_4,
279            top: 0.0,
280            bottom: tokens::SPACE_4,
281        })
282        .height(Size::Fill(1.0)),
283    ])
284    .key("metric:chart.card")
285    .width(Size::Fill(1.0))
286    .height(Size::Fill(1.0))
287}
288
289fn chart_bars() -> Vec<El> {
290    [
291        48.0, 72.0, 56.0, 90.0, 64.0, 80.0, 108.0, 84.0, 122.0, 96.0, 136.0, 118.0,
292    ]
293    .into_iter()
294    .flat_map(|height| {
295        [
296            bar(height, tokens::MUTED_FOREGROUND),
297            bar((height - 28.0_f32).max(24.0), tokens::INPUT),
298        ]
299    })
300    .collect()
301}
302
303fn bar(height: f32, color: Color) -> El {
304    El::new(Kind::Custom("chart_bar"))
305        .fill(color)
306        .radius(tokens::RADIUS_SM)
307        .width(Size::Fill(1.0))
308        .height(Size::Fixed(height))
309}
310
311fn sales_card() -> El {
312    card([
313        card_header([
314            card_title("Recent Sales"),
315            card_description("You made 265 sales this month."),
316        ])
317        .padding(tokens::SPACE_4),
318        card_content([
319            sale_row("OM", "Olivia Martin", "olivia@example.com", "+$1,999.00"),
320            sale_row("JL", "Jackson Lee", "jackson@example.com", "+$39.00"),
321            sale_row("IN", "Isabella Nguyen", "isabella@example.com", "+$299.00"),
322            sale_row("WK", "William Kim", "will@example.com", "+$99.00"),
323        ])
324        .gap(tokens::SPACE_2)
325        .padding(Sides {
326            left: tokens::SPACE_4,
327            right: tokens::SPACE_4,
328            top: 0.0,
329            bottom: tokens::SPACE_4,
330        }),
331    ])
332    .key("metric:sales.card")
333    .width(Size::Fixed(330.0))
334    .height(Size::Fill(1.0))
335}
336
337fn sale_row(
338    initials: &'static str,
339    name: &'static str,
340    email: &'static str,
341    amount: &'static str,
342) -> El {
343    row([
344        icon_cell(initials),
345        column([
346            text(name).semibold().ellipsis().width(Size::Fill(1.0)),
347            text(email).caption().ellipsis().width(Size::Fill(1.0)),
348        ])
349        .gap(2.0)
350        .height(Size::Hug)
351        .width(Size::Fill(1.0)),
352        text(amount).label().small(),
353    ])
354    .gap(tokens::SPACE_2)
355    .height(Size::Fixed(42.0))
356    .align(Align::Center)
357}
358
359fn documents_card() -> El {
360    card([
361        card_header([card_title("Documents")]).padding(tokens::SPACE_4),
362        card_content([scroll([table([
363            table_header([table_row([
364                table_head("").width(Size::Fixed(35.0)),
365                table_head("Header").width(Size::Fill(1.8)),
366                table_head("Section Type").width(Size::Fill(1.0)),
367                table_head("Status").width(Size::Fixed(104.0)),
368                table_head("Target").width(Size::Fixed(64.0)),
369                table_head("Limit").width(Size::Fixed(64.0)),
370                table_head("Reviewer").width(Size::Fixed(128.0)),
371                table_head("").width(Size::Fixed(32.0)),
372            ])
373            .padding(Sides::xy(tokens::SPACE_4, 0.0))
374            .key("metric:table.header")]),
375            divider(),
376            table_body([
377                document_row(
378                    "Cover page",
379                    "Cover page",
380                    "In Process",
381                    "18",
382                    "5",
383                    "Eddie Lake",
384                    "info",
385                ),
386                document_row(
387                    "Table of contents",
388                    "Table of contents",
389                    "Done",
390                    "29",
391                    "24",
392                    "Eddie Lake",
393                    "success",
394                ),
395            ]),
396        ])])
397        .height(Size::Fill(1.0))])
398        .gap(0.0)
399        .padding(0.0)
400        .height(Size::Fill(1.0)),
401    ])
402    .key("metric:table.card")
403    .height(Size::Fill(1.0))
404}
405
406fn document_row(
407    header: &'static str,
408    section: &'static str,
409    status: &'static str,
410    target: &'static str,
411    limit: &'static str,
412    reviewer: &'static str,
413    tone: &'static str,
414) -> El {
415    let status_badge = match tone {
416        "success" => badge(status).success(),
417        _ => badge(status).info(),
418    };
419    table_row([
420        table_utility_cell("::"),
421        table_cell(text(header).label().small()).width(Size::Fill(1.8)),
422        table_cell(text(section).muted()).width(Size::Fill(1.0)),
423        table_cell(status_badge).width(Size::Fixed(104.0)),
424        table_cell(text(target).label().small()).width(Size::Fixed(64.0)),
425        table_cell(text(limit).label().small()).width(Size::Fixed(64.0)),
426        table_cell(text(reviewer).muted()).width(Size::Fixed(128.0)),
427        table_action_cell(),
428    ])
429    .padding(Sides::xy(tokens::SPACE_4, 0.0))
430    .key(if header == "Cover page" {
431        "metric:table.row"
432    } else {
433        header
434    })
435}
examples/custom_paint.rs (line 76)
68fn build_row(c: &FakeCommit, idx: usize, selected: bool) -> El {
69    row([
70        graph_cell(c.lane, selected),
71        text(c.sha).mono().muted(),
72        text(c.subject),
73        spacer(),
74        text(format!("{} · {}", c.author, c.when)).muted(),
75    ])
76    .key(format!("commit-{idx}"))
77    .gap(tokens::SPACE_3)
78    .padding(Sides::xy(tokens::SPACE_2, 0.0))
79    .height(Size::Fixed(ROW_HEIGHT))
80    .align(Align::Center)
81}
examples/virtual_list.rs (line 33)
18fn build_row(i: usize) -> El {
19    let badge_el = match i % 5 {
20        0 => badge("info").muted(),
21        1 => badge("warn").warning(),
22        2 => badge("ok").success(),
23        3 => badge("err").destructive(),
24        _ => spacer(),
25    };
26    row([
27        text(format!("#{i:05}")).mono(),
28        spacer(),
29        text(format!("entry {i}")),
30        spacer(),
31        badge_el,
32    ])
33    .key(format!("row-{i}"))
34    .gap(tokens::SPACE_3)
35    .padding(Sides::xy(tokens::SPACE_3, tokens::SPACE_2))
36    .height(Size::Fixed(ROW_HEIGHT))
37}
38
39fn fixture() -> El {
40    column([
41        h1("Virtualized list"),
42        paragraph(format!(
43            "{ROW_COUNT} rows × {ROW_HEIGHT}px in a windowed viewport. \
44             Only the rows intersecting the viewport are realized — see \
45             `tree.txt` for proof."
46        ))
47        .muted(),
48        virtual_list(ROW_COUNT, ROW_HEIGHT, build_row)
49            .key("entries")
50            .height(Size::Fill(1.0)),
51    ])
52    .gap(tokens::SPACE_4)
53    .padding(tokens::SPACE_7)
54}
examples/scroll_list.rs (line 31)
15fn scroll_list_fixture() -> El {
16    let rows: Vec<El> = (0..20)
17        .map(|i| {
18            row([
19                badge(format!("#{i}")).info(),
20                text(format!("Notification {i}")).bold(),
21                spacer(),
22                text(format!("{}m ago", i + 1)).muted(),
23            ])
24            .gap(tokens::SPACE_2)
25            .height(Size::Fixed(44.0))
26            .padding(Sides::xy(tokens::SPACE_3, tokens::SPACE_2))
27        })
28        .collect();
29
30    let list = scroll(rows)
31        .key("notifications")
32        .height(Size::Fixed(420.0))
33        .padding(tokens::SPACE_2);
34
35    column([
36        h2("Notifications"),
37        text("Roll the wheel inside the panel to scroll. The content is taller than the viewport.")
38            .muted(),
39        list,
40    ])
41    .gap(tokens::SPACE_4)
42    .padding(tokens::SPACE_7)
43}
Source

pub fn block_pointer(self) -> Self

Source

pub fn hit_overflow(self, outset: impl Into<Sides>) -> Self

Expand this node’s pointer hit target without changing layout or paint. Hover, press, cursor, tooltip, and click routing all use the expanded target; UiEvent::target_rect still reports the node’s transformed visual rect from layout.

Keep this conservative. It is for controls whose effective interaction region is intentionally larger than their drawn chrome, not for making unrelated gutters activate nearby UI.

Source

pub fn focusable(self) -> Self

Examples found in repository?
examples/polish_calibration.rs (line 85)
67fn nav_item(icon: &'static str, label: &'static str, selected: bool) -> El {
68    let mut item = row([
69        icon_cell(icon),
70        text(label)
71            .font_weight(FontWeight::Medium)
72            .ellipsis()
73            .width(Size::Fill(1.0)),
74    ])
75    .key(if selected {
76        "metric:sidebar.nav.row".to_string()
77    } else {
78        format!("nav-{label}")
79    })
80    .metrics_role(MetricsRole::ListItem)
81    .gap(tokens::SPACE_3)
82    .padding(Sides::xy(tokens::SPACE_2, 0.0))
83    .height(Size::Fixed(40.0))
84    .align(Align::Center)
85    .focusable();
86
87    if selected {
88        item = item.current();
89    }
90
91    item
92}
93
94fn main_panel() -> El {
95    column([
96        toolbar(),
97        column([
98            row([
99                kpi_card("Latency", "42 ms", "-18%", true),
100                kpi_card("Runs", "1,284", "+12%", true),
101                kpi_card("Errors", "7", "+2", false),
102            ])
103            .gap(tokens::SPACE_4),
104            row([table_card(), command_card()])
105                .gap(tokens::SPACE_4)
106                .height(Size::Fill(1.0))
107                .align(Align::Stretch),
108        ])
109        .gap(tokens::SPACE_4)
110        .height(Size::Fill(1.0))
111        .align(Align::Stretch),
112    ])
113    .padding(tokens::SPACE_7)
114    .gap(tokens::SPACE_2)
115    .width(Size::Fill(1.0))
116    .height(Size::Fill(1.0))
117}
118
119fn toolbar() -> El {
120    row([
121        column([
122            h1("Polish calibration").key("metric:page.title"),
123            text("A representative app surface for default tuning.")
124                .muted()
125                .key("metric:page.subtitle"),
126        ])
127        .gap(tokens::SPACE_2)
128        .height(Size::Hug),
129        spacer(),
130        button_with_icon("search", "Preview")
131            .secondary()
132            .key("metric:action.secondary"),
133        button_with_icon("upload", "Publish")
134            .primary()
135            .key("metric:action.primary"),
136    ])
137    .key("metric:header")
138    .gap(tokens::SPACE_4)
139    .height(Size::Hug)
140    .align(Align::Start)
141}
142
143fn kpi_card(label: &'static str, value: &'static str, delta: &'static str, positive: bool) -> El {
144    let delta_badge = if positive {
145        badge(delta).success()
146    } else {
147        badge(delta).destructive()
148    };
149    let delta_badge = if label == "Latency" {
150        delta_badge.key("metric:kpi.badge")
151    } else {
152        delta_badge
153    };
154    let value_text = h2(value).display();
155    let value_text = if label == "Latency" {
156        value_text.key("metric:kpi.value")
157    } else {
158        value_text
159    };
160    card([
161        card_header([card_title(label)]),
162        card_content([
163            row([value_text, spacer(), delta_badge]).align(Align::Center),
164            text(if positive {
165                "Moving in the expected direction"
166            } else {
167                "Needs visual attention"
168            })
169            .muted(),
170        ])
171        .gap(tokens::SPACE_6),
172    ])
173    .key(if label == "Latency" {
174        "metric:kpi.card"
175    } else {
176        label
177    })
178    .width(Size::Fill(1.0))
179}
180
181fn table_card() -> El {
182    card([
183        card_header([card_title("Reference rows")]),
184        card_content([table([
185            table_header([table_row([
186                table_head("Status").width(Size::Fixed(86.0)),
187                table_head("Surface").width(Size::Fill(1.0)),
188                table_head("Owner").width(Size::Fixed(110.0)),
189                table_head("State").width(Size::Fixed(86.0)),
190            ])
191            .key("metric:table.header")]),
192            divider(),
193            table_body([
194                data_row("OK", "Settings card", "core", "selected", true, "success"),
195                data_row(
196                    "WARN",
197                    "Command palette density",
198                    "widgets",
199                    "needs work",
200                    false,
201                    "warning",
202                ),
203                data_row(
204                    "ERR",
205                    "Disabled and invalid states",
206                    "style",
207                    "missing",
208                    false,
209                    "destructive",
210                ),
211                data_row(
212                    "INFO",
213                    "Token resolution",
214                    "theme",
215                    "planned",
216                    false,
217                    "info",
218                ),
219                data_row(
220                    "OK",
221                    "Popover elevation",
222                    "shader",
223                    "queued",
224                    false,
225                    "success",
226                ),
227            ])
228            .gap(tokens::SPACE_1)
229            .width(Size::Fill(1.0)),
230        ])]),
231    ])
232    .key("metric:table.card")
233    .width(Size::Fill(1.2))
234    .height(Size::Fill(1.0))
235}
236
237fn data_row(
238    status: &'static str,
239    title: &'static str,
240    owner: &'static str,
241    state: &'static str,
242    selected: bool,
243    tone: &'static str,
244) -> El {
245    let status_badge = match tone {
246        "success" => badge(status).success(),
247        "warning" => badge(status).warning(),
248        "destructive" => badge(status).destructive(),
249        _ => badge(status).info(),
250    };
251    let status_badge = if selected {
252        status_badge.key("metric:table.badge")
253    } else {
254        status_badge
255    };
256
257    let mut row = table_row([
258        table_cell(status_badge).width(Size::Fixed(70.0)),
259        column([
260            text(title)
261                .font_weight(FontWeight::Medium)
262                .ellipsis()
263                .width(Size::Fill(1.0)),
264            text("Default styling probe.")
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0)),
271        table_cell(text(owner).muted()).width(Size::Fixed(110.0)),
272        table_cell(text(state).label().small()).width(Size::Fixed(86.0)),
273    ])
274    .key(if selected {
275        "metric:table.row".to_string()
276    } else {
277        format!("row-{title}")
278    })
279    .focusable();
280
281    if selected {
282        row = row.selected();
283    }
284
285    row
286}
More examples
Hide additional examples
examples/settings_calibration.rs (line 153)
134fn settings_nav_item(label: &'static str, selected: bool) -> El {
135    let mut item = row([
136        El::new(Kind::Custom("nav-dot"))
137            .fill(tokens::MUTED_FOREGROUND)
138            .radius(tokens::RADIUS_PILL)
139            .width(Size::Fixed(6.0))
140            .height(Size::Fixed(6.0)),
141        text(label)
142            .font_weight(FontWeight::Medium)
143            .ellipsis()
144            .width(Size::Fill(1.0)),
145    ])
146    .key(if selected {
147        "metric:settings.nav.row".to_string()
148    } else {
149        format!("settings-nav-{label}")
150    })
151    .metrics_role(MetricsRole::ListItem)
152    .align(Align::Center)
153    .focusable();
154
155    if selected {
156        item = item.current();
157    } else {
158        item = item.color(tokens::MUTED_FOREGROUND);
159    }
160
161    item
162}
163
164fn settings_body() -> El {
165    column([
166        column([
167            h1("Account").heading().key("metric:section.title"),
168            text("Manage identity, workspace defaults, and security preferences.")
169                .muted()
170                .wrap_text()
171                .key("metric:page.subtitle"),
172        ])
173        .gap(tokens::SPACE_1)
174        .height(Size::Hug),
175        scroll([profile_card(), preferences_card()])
176            .key("settings-body-scroll")
177            .gap(tokens::SPACE_4)
178            .width(Size::Fill(1.0))
179            .height(Size::Fill(1.0)),
180    ])
181    .gap(tokens::SPACE_4)
182    .width(Size::Fill(1.0))
183    .height(Size::Fill(1.0))
184}
185
186fn profile_card() -> El {
187    card([
188        card_header([
189            card_title("Profile"),
190            card_description("This information appears in audit logs and shared documents."),
191        ]),
192        card_content([form([
193            row([
194                setting_field("Display name", "Alicia Koch", "display-name"),
195                setting_field("Email", "alicia@acme.co", "email"),
196            ])
197            .gap(tokens::SPACE_3),
198            row([
199                setting_select("Role", "Workspace admin", "role"),
200                setting_select("Region", "US East", "region"),
201            ])
202            .gap(tokens::SPACE_3),
203        ])]),
204    ])
205    .key("metric:profile.card")
206}
207
208fn setting_field(label: &'static str, value: &'static str, key: &'static str) -> El {
209    form_item([
210        form_label(label),
211        form_control(
212            text_input(value, &Selection::caret(key, value.len()), key).key(
213                if key == "display-name" {
214                    "metric:form.input"
215                } else {
216                    key
217                },
218            ),
219        ),
220    ])
221    .width(Size::Fill(1.0))
222}
223
224fn setting_select(label: &'static str, value: &'static str, key: &'static str) -> El {
225    form_item([form_label(label), form_control(select_trigger(key, value))]).width(Size::Fill(1.0))
226}
227
228fn preferences_card() -> El {
229    card([
230        card_header([
231            card_title("Preferences"),
232            card_description("Defaults used when creating new dashboards and exports."),
233        ]),
234        card_content([column([
235            preference_row(
236                "Compact navigation",
237                "Use tighter rows in the sidebar and command menus.",
238                switch(true).key("compact-navigation"),
239            ),
240            divider(),
241            preference_row(
242                "Email summaries",
243                "Send a daily digest when documents change.",
244                switch(false).key("email-summaries"),
245            ),
246            divider(),
247            preference_row(
248                "Require approval",
249                "Route external sharing through an owner review.",
250                checkbox(true).key("approval-required"),
251            ),
252        ])
253        .gap(0.0)
254        .width(Size::Fill(1.0))])
255        .padding(0.0),
256    ])
257    .key("metric:preferences.card")
258}
259
260fn preference_row(title: &'static str, description: &'static str, control: El) -> El {
261    row([
262        column([
263            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
264            text(description)
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0))
271        .height(Size::Hug),
272        control,
273    ])
274    .key(if title == "Compact navigation" {
275        "metric:preference.row".to_string()
276    } else {
277        format!("preference-{title}")
278    })
279    .metrics_role(MetricsRole::PreferenceRow)
280    .gap(tokens::SPACE_4)
281    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_3))
282    .align(Align::Center)
283}
284
285fn settings_aside() -> El {
286    column([security_card(), scale_card()])
287        .gap(tokens::SPACE_4)
288        .width(Size::Fixed(300.0))
289        .height(Size::Fill(1.0))
290}
291
292fn security_card() -> El {
293    card([
294        card_header([
295            card_title("Security"),
296            card_description("Two-factor authentication is enabled for all privileged users."),
297        ]),
298        card_content([
299            compact_stat("Passkeys", "2 registered", badge("On").success()),
300            compact_stat("Sessions", "3 active", button("Review").secondary()),
301        ]),
302    ])
303    .width(Size::Fill(1.0))
304}
305
306fn scale_card() -> El {
307    card([
308        card_header([
309            card_title("Interface scale"),
310            card_description("Reference captures keep browser zoom fixed and vary root UI scale."),
311        ]),
312        card_content([
313            row([text("Dense").caption(), spacer(), text("Default").caption()]),
314            slider(0.66, tokens::PRIMARY)
315                .key("interface-scale")
316                .width(Size::Fill(1.0)),
317        ]),
318    ])
319    .width(Size::Fill(1.0))
320}
321
322fn compact_stat(title: &'static str, detail: &'static str, control: El) -> El {
323    row([
324        column([
325            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
326            text(detail).caption().ellipsis().width(Size::Fill(1.0)),
327        ])
328        .gap(2.0)
329        .width(Size::Fill(1.0))
330        .height(Size::Hug),
331        control,
332    ])
333    .gap(tokens::SPACE_2)
334    .height(Size::Fixed(44.0))
335    .align(Align::Center)
336}
337
338fn section_label(label: &'static str) -> El {
339    text(label)
340        .caption()
341        .height(Size::Fixed(22.0))
342        .padding(Sides::xy(tokens::SPACE_2, 0.0))
343}
344
345fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
346    let mut item = row([
347        icon(icon_name)
348            .color(tokens::MUTED_FOREGROUND)
349            .icon_size(tokens::ICON_SM)
350            .width(Size::Fixed(tokens::ICON_SM)),
351        text(label)
352            .font_weight(FontWeight::Medium)
353            .ellipsis()
354            .width(Size::Fill(1.0)),
355    ])
356    .key(if selected {
357        "metric:sidebar.nav.row".to_string()
358    } else {
359        format!("side-item-{label}")
360    })
361    .metrics_role(MetricsRole::ListItem)
362    .gap(tokens::SPACE_2)
363    .padding(Sides::xy(tokens::SPACE_2, 0.0))
364    .height(Size::Fixed(32.0))
365    .align(Align::Center)
366    .focusable();
367
368    if selected {
369        item = item.current();
370    } else {
371        item = item.color(tokens::MUTED_FOREGROUND);
372    }
373
374    item
375}
examples/dashboard_01_calibration.rs (line 130)
109fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
110    let mut item = row([
111        icon(icon_name)
112            .color(tokens::MUTED_FOREGROUND)
113            .icon_size(tokens::ICON_SM)
114            .width(Size::Fixed(tokens::ICON_SM)),
115        text(label)
116            .font_weight(FontWeight::Medium)
117            .ellipsis()
118            .width(Size::Fill(1.0)),
119    ])
120    .key(if selected {
121        "metric:sidebar.nav.row".to_string()
122    } else {
123        format!("side-item-{label}")
124    })
125    .metrics_role(MetricsRole::ListItem)
126    .gap(tokens::SPACE_2)
127    .padding(Sides::xy(tokens::SPACE_2, 0.0))
128    .height(Size::Fixed(32.0))
129    .align(Align::Center)
130    .focusable();
131
132    if selected {
133        item = item.current();
134    } else {
135        item = item.color(tokens::MUTED_FOREGROUND);
136    }
137
138    item
139}
Source

pub fn always_show_focus_ring(self) -> Self

Show the focus ring on this node even when focus arrived via pointer click. Default focus-ring behavior follows the web platform’s :focus-visible rule — ring on Tab, no ring on click. Widgets where the ring is meaningful regardless of source — text input, text area — opt in here so clicking into the field still raises the “now active” affordance. Implies nothing about focusability; pair with .focusable().

Source

pub fn selectable(self) -> Self

Opt this node into the library’s text-selection system. The node must also carry an explicit .key(...); selection requires stable identity across rebuilds the same way focus does.

Source

pub fn selection_source(self, source: SelectionSource) -> Self

Attach source-backed copy/hit-test text for this selectable node. The node still needs .selectable().key(...); this only changes how selection offsets map to copied text.

Source

pub fn capture_keys(self) -> Self

Opt this node into raw key capture when focused. While this node is the focused target, the library’s traversal/activation defaults are bypassed and raw KeyDown events are delivered for the widget to interpret. Escape is still treated as “exit editing”: the raw KeyDown is delivered first, then focus is cleared. Implies focusable.

Source

pub fn alpha_follows_focused_ancestor(self) -> Self

Multiply this element’s paint opacity by the nearest focusable ancestor’s focus envelope.

Multiply this node’s paint opacity by the runtime’s caret blink alpha.

Source

pub fn state_follows_interactive_ancestor(self) -> Self

Borrow hover and press visual envelopes from the nearest focusable ancestor.

Source

pub fn hover_alpha(self, rest: f32, peak: f32) -> Self

Bind this element’s paint opacity to the subtree interaction envelope — the max of hover, focus, and press for the subtree rooted at this element.

At rest (no descendant is the active hover, focus, or press target) the element paints at rest. At full envelope it paints at peak. Both are clamped to [0.0, 1.0], with linear interpolation in between following the eased envelope.

“Subtree” matches CSS :hover semantics: hovering, focusing, or pressing any descendant keeps the element revealed. A hover-revealed close icon stays visible while the cursor moves across the tab body or while the tab is keyboard-focused; an action pill stays visible while the cursor moves between focusable buttons inside it. The trigger isn’t strictly “hover” — focus and press also count — but hover is the dominant case and the name reflects it.

Layout-neutral — the element keeps its computed rect at all times. Use for hover-revealed close buttons, secondary actions on list rows, hover-only validation icons, and other “show on interaction” patterns where the surrounding layout shouldn’t shift.

§Beyond alpha

For the other common hover affordances — Material-style lift (translate_y), button-pop (scale), tint shift (fill) — drive the prop from app code using crate::BuildCx::is_hovering_within plus Self::animate:

fn build(&self, cx: &BuildCx) -> El {
    let lifted = cx.is_hovering_within("card");
    card([...])
        .key("card")
        .focusable()
        .translate(0.0, if lifted { -2.0 } else { 0.0 })
        .scale(if lifted { 1.02 } else { 1.0 })
        .animate(Timing::SPRING_QUICK)
}

is_hovering_within reads the same subtree predicate hover_alpha consumes (CSS :hover-style cascade). animate eases the prop between the two build values across frames, so the transition is smooth without per-channel declarative API. hover_alpha itself is the alpha-channel shorthand — it skips the boolean-to-value conversion and the per-node animate allocation, since alpha is the dominant hover affordance.

Source

pub fn at(self, file: &'static str, line: u32) -> Self

Examples found in repository?
examples/palette_demo.rs (line 286)
282fn token_chip(token: TokenDef, palette: &Palette) -> El {
283    let resolved = palette.resolve(token.color);
284    row([
285        El::new(Kind::Custom("palette-swatch"))
286            .at(file!(), line!())
287            .fill(token.color)
288            .stroke(tokens::BORDER)
289            .radius(tokens::RADIUS_SM)
290            .width(Size::Fixed(42.0))
291            .height(Size::Fixed(34.0)),
292        column([
293            text(token.name)
294                .label()
295                .ellipsis()
296                .nowrap_text()
297                .width(Size::Fill(1.0)),
298            mono(rgba_label(resolved)).caption().muted(),
299        ])
300        .gap(0.0)
301        .width(Size::Fill(1.0))
302        .height(Size::Hug),
303    ])
304    .gap(tokens::SPACE_2)
305    .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_2))
306    .align(Align::Center)
307    .fill(tokens::CARD)
308    .stroke(tokens::BORDER)
309    .radius(tokens::RADIUS_MD)
310    .width(Size::Fill(1.0))
311    .height(Size::Fixed(54.0))
312}
Source

pub fn at_loc(self, loc: &'static Location<'static>) -> Self

Set source from a Location (used internally by #[track_caller] constructors).

Source

pub fn from_library(self) -> Self

Mark this El as constructed inside an aetna library closure where #[track_caller] doesn’t reach user code (e.g. the .map(|item| ...) body inside tabs_list, radio_group, etc.). The lint pass uses this flag to walk blame attribution upward to the nearest user-source ancestor instead of pointing findings at aetna-core internals. User code never needs to call this.

Source§

impl El

Source

pub fn width(self, w: Size) -> Self

Examples found in repository?
examples/polish_calibration.rs (line 61)
44fn sidebar() -> El {
45    column([
46        column([h2("Aetna"), text("calibration").muted()])
47            .key("metric:sidebar.brand")
48            .gap(tokens::SPACE_1)
49            .height(Size::Hug),
50        spacer().height(Size::Fixed(tokens::SPACE_4)),
51        nav_item("01", "Overview", true),
52        nav_item("02", "Commands", false),
53        nav_item("03", "Tables", false),
54        nav_item("04", "Forms", false),
55        spacer(),
56        badge("dark theme").muted(),
57    ])
58    .gap(tokens::SPACE_2)
59    .padding(tokens::SPACE_5)
60    .key("metric:sidebar")
61    .width(Size::Fixed(220.0))
62    .height(Size::Fill(1.0))
63    .fill(tokens::CARD)
64    .stroke(tokens::BORDER)
65}
66
67fn nav_item(icon: &'static str, label: &'static str, selected: bool) -> El {
68    let mut item = row([
69        icon_cell(icon),
70        text(label)
71            .font_weight(FontWeight::Medium)
72            .ellipsis()
73            .width(Size::Fill(1.0)),
74    ])
75    .key(if selected {
76        "metric:sidebar.nav.row".to_string()
77    } else {
78        format!("nav-{label}")
79    })
80    .metrics_role(MetricsRole::ListItem)
81    .gap(tokens::SPACE_3)
82    .padding(Sides::xy(tokens::SPACE_2, 0.0))
83    .height(Size::Fixed(40.0))
84    .align(Align::Center)
85    .focusable();
86
87    if selected {
88        item = item.current();
89    }
90
91    item
92}
93
94fn main_panel() -> El {
95    column([
96        toolbar(),
97        column([
98            row([
99                kpi_card("Latency", "42 ms", "-18%", true),
100                kpi_card("Runs", "1,284", "+12%", true),
101                kpi_card("Errors", "7", "+2", false),
102            ])
103            .gap(tokens::SPACE_4),
104            row([table_card(), command_card()])
105                .gap(tokens::SPACE_4)
106                .height(Size::Fill(1.0))
107                .align(Align::Stretch),
108        ])
109        .gap(tokens::SPACE_4)
110        .height(Size::Fill(1.0))
111        .align(Align::Stretch),
112    ])
113    .padding(tokens::SPACE_7)
114    .gap(tokens::SPACE_2)
115    .width(Size::Fill(1.0))
116    .height(Size::Fill(1.0))
117}
118
119fn toolbar() -> El {
120    row([
121        column([
122            h1("Polish calibration").key("metric:page.title"),
123            text("A representative app surface for default tuning.")
124                .muted()
125                .key("metric:page.subtitle"),
126        ])
127        .gap(tokens::SPACE_2)
128        .height(Size::Hug),
129        spacer(),
130        button_with_icon("search", "Preview")
131            .secondary()
132            .key("metric:action.secondary"),
133        button_with_icon("upload", "Publish")
134            .primary()
135            .key("metric:action.primary"),
136    ])
137    .key("metric:header")
138    .gap(tokens::SPACE_4)
139    .height(Size::Hug)
140    .align(Align::Start)
141}
142
143fn kpi_card(label: &'static str, value: &'static str, delta: &'static str, positive: bool) -> El {
144    let delta_badge = if positive {
145        badge(delta).success()
146    } else {
147        badge(delta).destructive()
148    };
149    let delta_badge = if label == "Latency" {
150        delta_badge.key("metric:kpi.badge")
151    } else {
152        delta_badge
153    };
154    let value_text = h2(value).display();
155    let value_text = if label == "Latency" {
156        value_text.key("metric:kpi.value")
157    } else {
158        value_text
159    };
160    card([
161        card_header([card_title(label)]),
162        card_content([
163            row([value_text, spacer(), delta_badge]).align(Align::Center),
164            text(if positive {
165                "Moving in the expected direction"
166            } else {
167                "Needs visual attention"
168            })
169            .muted(),
170        ])
171        .gap(tokens::SPACE_6),
172    ])
173    .key(if label == "Latency" {
174        "metric:kpi.card"
175    } else {
176        label
177    })
178    .width(Size::Fill(1.0))
179}
180
181fn table_card() -> El {
182    card([
183        card_header([card_title("Reference rows")]),
184        card_content([table([
185            table_header([table_row([
186                table_head("Status").width(Size::Fixed(86.0)),
187                table_head("Surface").width(Size::Fill(1.0)),
188                table_head("Owner").width(Size::Fixed(110.0)),
189                table_head("State").width(Size::Fixed(86.0)),
190            ])
191            .key("metric:table.header")]),
192            divider(),
193            table_body([
194                data_row("OK", "Settings card", "core", "selected", true, "success"),
195                data_row(
196                    "WARN",
197                    "Command palette density",
198                    "widgets",
199                    "needs work",
200                    false,
201                    "warning",
202                ),
203                data_row(
204                    "ERR",
205                    "Disabled and invalid states",
206                    "style",
207                    "missing",
208                    false,
209                    "destructive",
210                ),
211                data_row(
212                    "INFO",
213                    "Token resolution",
214                    "theme",
215                    "planned",
216                    false,
217                    "info",
218                ),
219                data_row(
220                    "OK",
221                    "Popover elevation",
222                    "shader",
223                    "queued",
224                    false,
225                    "success",
226                ),
227            ])
228            .gap(tokens::SPACE_1)
229            .width(Size::Fill(1.0)),
230        ])]),
231    ])
232    .key("metric:table.card")
233    .width(Size::Fill(1.2))
234    .height(Size::Fill(1.0))
235}
236
237fn data_row(
238    status: &'static str,
239    title: &'static str,
240    owner: &'static str,
241    state: &'static str,
242    selected: bool,
243    tone: &'static str,
244) -> El {
245    let status_badge = match tone {
246        "success" => badge(status).success(),
247        "warning" => badge(status).warning(),
248        "destructive" => badge(status).destructive(),
249        _ => badge(status).info(),
250    };
251    let status_badge = if selected {
252        status_badge.key("metric:table.badge")
253    } else {
254        status_badge
255    };
256
257    let mut row = table_row([
258        table_cell(status_badge).width(Size::Fixed(70.0)),
259        column([
260            text(title)
261                .font_weight(FontWeight::Medium)
262                .ellipsis()
263                .width(Size::Fill(1.0)),
264            text("Default styling probe.")
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0)),
271        table_cell(text(owner).muted()).width(Size::Fixed(110.0)),
272        table_cell(text(state).label().small()).width(Size::Fixed(86.0)),
273    ])
274    .key(if selected {
275        "metric:table.row".to_string()
276    } else {
277        format!("row-{title}")
278    })
279    .focusable();
280
281    if selected {
282        row = row.selected();
283    }
284
285    row
286}
287
288fn command_card() -> El {
289    card([
290        card_header([card_title("Command surface")]),
291        card_content([
292            text_input(
293                "Search commands...",
294                &Selection::default(),
295                "command-search",
296            )
297            .key("metric:command.input")
298            .width(Size::Fill(1.0)),
299            popover_panel([
300                command_row("git-branch", "New branch", "Ctrl+B").key("metric:command.row"),
301                command_row("git-commit", "Commit staged files", "Ctrl+Enter")
302                    .key("command-row-commit"),
303                command_row("refresh-cw", "Refresh repository", "Ctrl+R")
304                    .key("command-row-refresh"),
305                command_row("alert-circle", "Force push", "Danger").key("command-row-force"),
306            ])
307            .width(Size::Fill(1.0)),
308            scroll([form_probe()]).key("form-probe-scroll"),
309        ])
310        .height(Size::Fill(1.0)),
311    ])
312    .key("metric:command.card")
313    .width(Size::Fill(0.8))
314    .height(Size::Fill(1.0))
315}
316
317fn form_probe() -> El {
318    form([
319        form_item([
320            form_label("Valid input"),
321            form_control(
322                text_input(
323                    "Valid input",
324                    &Selection::caret("valid-input", 11),
325                    "valid-input",
326                )
327                .key("metric:form.input"),
328            ),
329            form_description("Default field spacing and helper text."),
330        ]),
331        form_item([
332            form_label("Invalid input"),
333            form_control(
334                text_input(
335                    "Invalid input",
336                    &Selection::caret("invalid-input", 13),
337                    "invalid-input",
338                )
339                .invalid(),
340            ),
341            form_message("This field needs attention."),
342        ]),
343        row([
344            button("Disabled").secondary().disabled(),
345            button("Loading").primary().loading(),
346            spacer(),
347        ]),
348    ])
349    .padding(tokens::SPACE_3)
350    .fill(tokens::MUTED)
351    .stroke(tokens::BORDER)
352    .radius(tokens::RADIUS_MD)
353}
354
355fn icon_cell(label: &'static str) -> El {
356    El::new(Kind::Custom("icon_cell"))
357        .style_profile(StyleProfile::Surface)
358        .text(label)
359        .text_align(TextAlign::Center)
360        .caption()
361        .font_weight(FontWeight::Semibold)
362        .fill(tokens::MUTED)
363        .stroke(tokens::BORDER)
364        .radius(tokens::RADIUS_SM)
365        .width(Size::Fixed(26.0))
366        .height(Size::Fixed(26.0))
367}
More examples
Hide additional examples
examples/custom_paint.rs (line 57)
44fn graph_cell(lane: u8, selected: bool) -> El {
45    let lane_color = lane_palette(lane);
46    let ring_color = if selected {
47        Color::rgb(245, 245, 250)
48    } else {
49        lane_color
50    };
51    let ring_w = if selected { 2.5 } else { 1.5 };
52    let radius = 5.0;
53    let line_w = 2.0;
54    let lane_frac = (lane as f32 + 0.5) / LANE_COUNT as f32;
55
56    El::new(Kind::Custom("graph_cell"))
57        .width(Size::Fixed(GRAPH_WIDTH))
58        .height(Size::Fixed(ROW_HEIGHT))
59        .shader(
60            ShaderBinding::custom("commit_node")
61                .color("vec_a", tokens::BACKGROUND)
62                .color("vec_b", ring_color)
63                .vec4("vec_c", [radius, ring_w, line_w, lane_frac]),
64        )
65        .fill(lane_color)
66}
examples/circular_layout.rs (line 77)
48fn fixture() -> El {
49    let centre = h2("Compass").center_text();
50    let dirs = [
51        ("North", "n"),
52        ("NE", "ne"),
53        ("East", "e"),
54        ("SE", "se"),
55        ("South", "s"),
56        ("SW", "sw"),
57        ("West", "w"),
58        ("NW", "nw"),
59    ];
60
61    let mut children: Vec<El> = vec![centre];
62    for (label, k) in dirs {
63        children.push(button(label).key(k).primary());
64    }
65
66    column([
67        h1("Custom layout — circular"),
68        paragraph(
69            "Eight buttons positioned on a circle by an author-supplied \
70             LayoutFn. Stock paint, automatic hover/press, and hit-test \
71             all keep working — only the rect distribution changed.",
72        )
73        .muted(),
74        stack(children)
75            .key("compass")
76            .layout(circular)
77            .width(Size::Fill(1.0))
78            .height(Size::Fixed(360.0)),
79    ])
80    .gap(tokens::SPACE_4)
81    .padding(tokens::SPACE_7)
82}
examples/toast.rs (line 54)
31fn fixture() -> El {
32    // Apps wrap their main view in `overlays(main, [])` so the
33    // runtime can append the synthesized toast layer as an overlay
34    // sibling — same convention as for popovers and modals.
35    overlays(
36        column([
37            h2("Toasts"),
38            paragraph(
39                "Apps queue toasts by returning ToastSpec values from \
40                 App::drain_toasts. The runtime stamps each with a TTL, \
41                 stacks them at the bottom-right corner, and dismisses \
42                 them on click or auto-expiry.",
43            )
44            .muted(),
45            row([
46                button("Save changes").key("save"),
47                button("Trigger error").key("err"),
48                button("Show info").key("info"),
49            ])
50            .gap(tokens::SPACE_2),
51        ])
52        .gap(tokens::SPACE_4)
53        .padding(tokens::SPACE_7)
54        .width(Size::Fill(1.0))
55        .height(Size::Fill(1.0)),
56        [],
57    )
58}
examples/inline_runs.rs (line 37)
18fn fixture() -> El {
19    column([
20        h2("Inline runs"),
21        text_runs([
22            text("Aetna's attributed-text path lets you compose runs with "),
23            text("bold").bold(),
24            text(", "),
25            text("italic").italic(),
26            text(", "),
27            text("colored").color(tokens::DESTRUCTIVE),
28            text(", and "),
29            text("inline code").code(),
30            text(" segments inside one wrapping paragraph."),
31            hard_break(),
32            text("Hard breaks act like ").muted(),
33            text("<br>").code(),
34            text(" — they end the current line without breaking out of the run.").muted(),
35        ])
36        .wrap_text()
37        .width(Size::Fill(1.0))
38        .height(Size::Hug),
39        paragraph(
40            "All of the above flows through one cosmic-text rich-text shape — \
41             wrapping decisions cross run boundaries the way real prose wraps.",
42        )
43        .muted(),
44    ])
45    .gap(tokens::SPACE_4)
46    .padding(tokens::SPACE_7)
47    .width(Size::Fixed(640.0))
48}
examples/palette_demo.rs (line 98)
77fn palette_demo(label: &'static str, palette: &Palette) -> El {
78    column([
79        row([
80            column([
81                h1("Palette demo"),
82                text("Aetna and Radix palettes rendered through Aetna tokens.").muted(),
83            ])
84            .gap(tokens::SPACE_2)
85            .height(Size::Hug),
86            spacer(),
87            badge(label).muted(),
88        ])
89        .align(Align::Start)
90        .height(Size::Hug),
91        row([
92            token_section(
93                "Core tokens",
94                "The shadcn-shaped semantic vocabulary.",
95                &CORE_TOKENS,
96                palette,
97            )
98            .width(Size::Fill(1.25)),
99            column([
100                token_section(
101                    "Aetna extensions",
102                    "Component and status tokens layered on top.",
103                    &EXTENSION_TOKENS,
104                    palette,
105                ),
106                component_section(),
107            ])
108            .gap(tokens::SPACE_4)
109            .width(Size::Fill(1.0))
110            .height(Size::Fill(1.0)),
111        ])
112        .gap(tokens::SPACE_4)
113        .align(Align::Stretch)
114        .height(Size::Fill(1.0)),
115    ])
116    .padding(tokens::SPACE_8)
117    .gap(tokens::SPACE_6)
118    .fill_size()
119    .fill(tokens::BACKGROUND)
120}
121
122const CORE_TOKENS: [TokenDef; 19] = [
123    TokenDef {
124        name: "background",
125        color: tokens::BACKGROUND,
126    },
127    TokenDef {
128        name: "foreground",
129        color: tokens::FOREGROUND,
130    },
131    TokenDef {
132        name: "card",
133        color: tokens::CARD,
134    },
135    TokenDef {
136        name: "card-foreground",
137        color: tokens::CARD_FOREGROUND,
138    },
139    TokenDef {
140        name: "popover",
141        color: tokens::POPOVER,
142    },
143    TokenDef {
144        name: "popover-foreground",
145        color: tokens::POPOVER_FOREGROUND,
146    },
147    TokenDef {
148        name: "primary",
149        color: tokens::PRIMARY,
150    },
151    TokenDef {
152        name: "primary-foreground",
153        color: tokens::PRIMARY_FOREGROUND,
154    },
155    TokenDef {
156        name: "secondary",
157        color: tokens::SECONDARY,
158    },
159    TokenDef {
160        name: "secondary-foreground",
161        color: tokens::SECONDARY_FOREGROUND,
162    },
163    TokenDef {
164        name: "muted",
165        color: tokens::MUTED,
166    },
167    TokenDef {
168        name: "muted-foreground",
169        color: tokens::MUTED_FOREGROUND,
170    },
171    TokenDef {
172        name: "accent",
173        color: tokens::ACCENT,
174    },
175    TokenDef {
176        name: "accent-foreground",
177        color: tokens::ACCENT_FOREGROUND,
178    },
179    TokenDef {
180        name: "destructive",
181        color: tokens::DESTRUCTIVE,
182    },
183    TokenDef {
184        name: "destructive-foreground",
185        color: tokens::DESTRUCTIVE_FOREGROUND,
186    },
187    TokenDef {
188        name: "border",
189        color: tokens::BORDER,
190    },
191    TokenDef {
192        name: "input",
193        color: tokens::INPUT,
194    },
195    TokenDef {
196        name: "ring",
197        color: tokens::RING,
198    },
199];
200
201const EXTENSION_TOKENS: [TokenDef; 12] = [
202    TokenDef {
203        name: "success",
204        color: tokens::SUCCESS,
205    },
206    TokenDef {
207        name: "success-foreground",
208        color: tokens::SUCCESS_FOREGROUND,
209    },
210    TokenDef {
211        name: "warning",
212        color: tokens::WARNING,
213    },
214    TokenDef {
215        name: "warning-foreground",
216        color: tokens::WARNING_FOREGROUND,
217    },
218    TokenDef {
219        name: "info",
220        color: tokens::INFO,
221    },
222    TokenDef {
223        name: "info-foreground",
224        color: tokens::INFO_FOREGROUND,
225    },
226    TokenDef {
227        name: "link-foreground",
228        color: tokens::LINK_FOREGROUND,
229    },
230    TokenDef {
231        name: "overlay-scrim",
232        color: tokens::OVERLAY_SCRIM,
233    },
234    TokenDef {
235        name: "scrollbar-thumb",
236        color: tokens::SCROLLBAR_THUMB_FILL,
237    },
238    TokenDef {
239        name: "scrollbar-thumb-active",
240        color: tokens::SCROLLBAR_THUMB_FILL_ACTIVE,
241    },
242    TokenDef {
243        name: "selection-bg",
244        color: tokens::SELECTION_BG,
245    },
246    TokenDef {
247        name: "selection-bg-unfocused",
248        color: tokens::SELECTION_BG_UNFOCUSED,
249    },
250];
251
252fn token_section(
253    title: &'static str,
254    description: &'static str,
255    tokens: &'static [TokenDef],
256    palette: &Palette,
257) -> El {
258    card([
259        card_header([card_title(title), card_description(description)]),
260        card_content([swatch_grid(tokens, palette)]),
261    ])
262    .height(Size::Fill(1.0))
263}
264
265fn swatch_grid(defs: &'static [TokenDef], palette: &Palette) -> El {
266    let rows = defs
267        .chunks(2)
268        .map(|chunk| {
269            row(chunk.iter().map(|token| token_chip(*token, palette)))
270                .gap(tokens::SPACE_3)
271                .align(Align::Center)
272                .width(Size::Fill(1.0))
273        })
274        .collect::<Vec<_>>();
275
276    column(rows)
277        .gap(tokens::SPACE_3)
278        .width(Size::Fill(1.0))
279        .height(Size::Hug)
280}
281
282fn token_chip(token: TokenDef, palette: &Palette) -> El {
283    let resolved = palette.resolve(token.color);
284    row([
285        El::new(Kind::Custom("palette-swatch"))
286            .at(file!(), line!())
287            .fill(token.color)
288            .stroke(tokens::BORDER)
289            .radius(tokens::RADIUS_SM)
290            .width(Size::Fixed(42.0))
291            .height(Size::Fixed(34.0)),
292        column([
293            text(token.name)
294                .label()
295                .ellipsis()
296                .nowrap_text()
297                .width(Size::Fill(1.0)),
298            mono(rgba_label(resolved)).caption().muted(),
299        ])
300        .gap(0.0)
301        .width(Size::Fill(1.0))
302        .height(Size::Hug),
303    ])
304    .gap(tokens::SPACE_2)
305    .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_2))
306    .align(Align::Center)
307    .fill(tokens::CARD)
308    .stroke(tokens::BORDER)
309    .radius(tokens::RADIUS_MD)
310    .width(Size::Fill(1.0))
311    .height(Size::Fixed(54.0))
312}
313
314fn component_section() -> El {
315    card([
316        card_header([
317            card_title("Stock widgets"),
318            card_description("The same palette applied to regular component constructors."),
319        ]),
320        card_content([
321            row([
322                button("Primary").primary(),
323                button("Secondary").secondary(),
324                button("Outline").outline(),
325                button("Ghost").ghost(),
326            ])
327            .gap(tokens::SPACE_2)
328            .align(Align::Center),
329            row([
330                badge("success").success(),
331                badge("warning").warning(),
332                badge("destructive").destructive(),
333                badge("info").info(),
334                badge("muted").muted(),
335            ])
336            .gap(tokens::SPACE_2)
337            .align(Align::Center),
338            row([
339                text_input("palette search", &Selection::default(), "palette:search")
340                    .width(Size::Fill(1.0)),
341                button_with_icon("settings", "Tune").secondary(),
342            ])
343            .gap(tokens::SPACE_2)
344            .align(Align::Center),
345            row([
346                surface_sample("Card", tokens::CARD),
347                surface_sample("Muted", tokens::MUTED),
348                surface_sample("Popover", tokens::POPOVER),
349            ])
350            .gap(tokens::SPACE_3)
351            .align(Align::Stretch),
352        ])
353        .gap(tokens::SPACE_4),
354    ])
355    .height(Size::Hug)
356}
357
358fn surface_sample(title: &'static str, fill: Color) -> El {
359    column([
360        text(title).label(),
361        text("surface sample").caption().muted(),
362    ])
363    .gap(tokens::SPACE_1)
364    .padding(tokens::SPACE_3)
365    .fill(fill)
366    .stroke(tokens::BORDER)
367    .radius(tokens::RADIUS_MD)
368    .width(Size::Fill(1.0))
369    .height(Size::Fixed(76.0))
370}
Source

pub fn height(self, h: Size) -> Self

Examples found in repository?
examples/scrollbar.rs (line 29)
20fn list_rows() -> Vec<El> {
21    (0..40)
22        .map(|i| {
23            row([
24                text(format!("{i:02}.")).mono().muted(),
25                text(format!("scrollable list item {i}")),
26            ])
27            .gap(tokens::SPACE_2)
28            .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_1))
29            .height(Size::Fixed(28.0))
30            .align(Align::Center)
31        })
32        .collect()
33}
34
35fn fixture() -> El {
36    column([
37        h2("Scrollbar"),
38        text("scroll() and virtual_list() show a draggable thumb by default.").muted(),
39        row([
40            // 1) scroll() — default-on scrollbar.
41            column([
42                text("scroll() — default").bold(),
43                scroll(list_rows())
44                    .height(Size::Fixed(240.0))
45                    .padding(tokens::SPACE_2)
46                    .stroke(tokens::BORDER)
47                    .stroke_width(1.0)
48                    .radius(tokens::RADIUS_MD),
49            ])
50            .gap(tokens::SPACE_2)
51            .width(Size::Fill(1.0))
52            .height(Size::Hug),
53            // 2) virtual_list — thumb scales to content size.
54            column([
55                text("virtual_list(200, 28)").bold(),
56                virtual_list(200, 28.0, |i| {
57                    row([
58                        text(format!("{i:03}")).mono().muted(),
59                        text(format!("row {i}")),
60                    ])
61                    .gap(tokens::SPACE_2)
62                    .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_1))
63                    .height(Size::Fixed(28.0))
64                    .align(Align::Center)
65                })
66                .height(Size::Fixed(240.0))
67                .padding(tokens::SPACE_2)
68                .stroke(tokens::BORDER)
69                .stroke_width(1.0)
70                .radius(tokens::RADIUS_MD),
71            ])
72            .gap(tokens::SPACE_2)
73            .width(Size::Fill(1.0))
74            .height(Size::Hug),
75            // 3) Opt-out: same content, no thumb.
76            column([
77                text("scroll().no_scrollbar()").bold(),
78                scroll(list_rows())
79                    .no_scrollbar()
80                    .height(Size::Fixed(240.0))
81                    .padding(tokens::SPACE_2)
82                    .stroke(tokens::BORDER)
83                    .stroke_width(1.0)
84                    .radius(tokens::RADIUS_MD),
85            ])
86            .gap(tokens::SPACE_2)
87            .width(Size::Fill(1.0))
88            .height(Size::Hug),
89        ])
90        .gap(tokens::SPACE_4)
91        .width(Size::Fill(1.0)),
92    ])
93    .gap(tokens::SPACE_4)
94    .padding(tokens::SPACE_7)
95}
More examples
Hide additional examples
examples/virtual_list.rs (line 36)
18fn build_row(i: usize) -> El {
19    let badge_el = match i % 5 {
20        0 => badge("info").muted(),
21        1 => badge("warn").warning(),
22        2 => badge("ok").success(),
23        3 => badge("err").destructive(),
24        _ => spacer(),
25    };
26    row([
27        text(format!("#{i:05}")).mono(),
28        spacer(),
29        text(format!("entry {i}")),
30        spacer(),
31        badge_el,
32    ])
33    .key(format!("row-{i}"))
34    .gap(tokens::SPACE_3)
35    .padding(Sides::xy(tokens::SPACE_3, tokens::SPACE_2))
36    .height(Size::Fixed(ROW_HEIGHT))
37}
38
39fn fixture() -> El {
40    column([
41        h1("Virtualized list"),
42        paragraph(format!(
43            "{ROW_COUNT} rows × {ROW_HEIGHT}px in a windowed viewport. \
44             Only the rows intersecting the viewport are realized — see \
45             `tree.txt` for proof."
46        ))
47        .muted(),
48        virtual_list(ROW_COUNT, ROW_HEIGHT, build_row)
49            .key("entries")
50            .height(Size::Fill(1.0)),
51    ])
52    .gap(tokens::SPACE_4)
53    .padding(tokens::SPACE_7)
54}
examples/polish_calibration.rs (line 49)
44fn sidebar() -> El {
45    column([
46        column([h2("Aetna"), text("calibration").muted()])
47            .key("metric:sidebar.brand")
48            .gap(tokens::SPACE_1)
49            .height(Size::Hug),
50        spacer().height(Size::Fixed(tokens::SPACE_4)),
51        nav_item("01", "Overview", true),
52        nav_item("02", "Commands", false),
53        nav_item("03", "Tables", false),
54        nav_item("04", "Forms", false),
55        spacer(),
56        badge("dark theme").muted(),
57    ])
58    .gap(tokens::SPACE_2)
59    .padding(tokens::SPACE_5)
60    .key("metric:sidebar")
61    .width(Size::Fixed(220.0))
62    .height(Size::Fill(1.0))
63    .fill(tokens::CARD)
64    .stroke(tokens::BORDER)
65}
66
67fn nav_item(icon: &'static str, label: &'static str, selected: bool) -> El {
68    let mut item = row([
69        icon_cell(icon),
70        text(label)
71            .font_weight(FontWeight::Medium)
72            .ellipsis()
73            .width(Size::Fill(1.0)),
74    ])
75    .key(if selected {
76        "metric:sidebar.nav.row".to_string()
77    } else {
78        format!("nav-{label}")
79    })
80    .metrics_role(MetricsRole::ListItem)
81    .gap(tokens::SPACE_3)
82    .padding(Sides::xy(tokens::SPACE_2, 0.0))
83    .height(Size::Fixed(40.0))
84    .align(Align::Center)
85    .focusable();
86
87    if selected {
88        item = item.current();
89    }
90
91    item
92}
93
94fn main_panel() -> El {
95    column([
96        toolbar(),
97        column([
98            row([
99                kpi_card("Latency", "42 ms", "-18%", true),
100                kpi_card("Runs", "1,284", "+12%", true),
101                kpi_card("Errors", "7", "+2", false),
102            ])
103            .gap(tokens::SPACE_4),
104            row([table_card(), command_card()])
105                .gap(tokens::SPACE_4)
106                .height(Size::Fill(1.0))
107                .align(Align::Stretch),
108        ])
109        .gap(tokens::SPACE_4)
110        .height(Size::Fill(1.0))
111        .align(Align::Stretch),
112    ])
113    .padding(tokens::SPACE_7)
114    .gap(tokens::SPACE_2)
115    .width(Size::Fill(1.0))
116    .height(Size::Fill(1.0))
117}
118
119fn toolbar() -> El {
120    row([
121        column([
122            h1("Polish calibration").key("metric:page.title"),
123            text("A representative app surface for default tuning.")
124                .muted()
125                .key("metric:page.subtitle"),
126        ])
127        .gap(tokens::SPACE_2)
128        .height(Size::Hug),
129        spacer(),
130        button_with_icon("search", "Preview")
131            .secondary()
132            .key("metric:action.secondary"),
133        button_with_icon("upload", "Publish")
134            .primary()
135            .key("metric:action.primary"),
136    ])
137    .key("metric:header")
138    .gap(tokens::SPACE_4)
139    .height(Size::Hug)
140    .align(Align::Start)
141}
142
143fn kpi_card(label: &'static str, value: &'static str, delta: &'static str, positive: bool) -> El {
144    let delta_badge = if positive {
145        badge(delta).success()
146    } else {
147        badge(delta).destructive()
148    };
149    let delta_badge = if label == "Latency" {
150        delta_badge.key("metric:kpi.badge")
151    } else {
152        delta_badge
153    };
154    let value_text = h2(value).display();
155    let value_text = if label == "Latency" {
156        value_text.key("metric:kpi.value")
157    } else {
158        value_text
159    };
160    card([
161        card_header([card_title(label)]),
162        card_content([
163            row([value_text, spacer(), delta_badge]).align(Align::Center),
164            text(if positive {
165                "Moving in the expected direction"
166            } else {
167                "Needs visual attention"
168            })
169            .muted(),
170        ])
171        .gap(tokens::SPACE_6),
172    ])
173    .key(if label == "Latency" {
174        "metric:kpi.card"
175    } else {
176        label
177    })
178    .width(Size::Fill(1.0))
179}
180
181fn table_card() -> El {
182    card([
183        card_header([card_title("Reference rows")]),
184        card_content([table([
185            table_header([table_row([
186                table_head("Status").width(Size::Fixed(86.0)),
187                table_head("Surface").width(Size::Fill(1.0)),
188                table_head("Owner").width(Size::Fixed(110.0)),
189                table_head("State").width(Size::Fixed(86.0)),
190            ])
191            .key("metric:table.header")]),
192            divider(),
193            table_body([
194                data_row("OK", "Settings card", "core", "selected", true, "success"),
195                data_row(
196                    "WARN",
197                    "Command palette density",
198                    "widgets",
199                    "needs work",
200                    false,
201                    "warning",
202                ),
203                data_row(
204                    "ERR",
205                    "Disabled and invalid states",
206                    "style",
207                    "missing",
208                    false,
209                    "destructive",
210                ),
211                data_row(
212                    "INFO",
213                    "Token resolution",
214                    "theme",
215                    "planned",
216                    false,
217                    "info",
218                ),
219                data_row(
220                    "OK",
221                    "Popover elevation",
222                    "shader",
223                    "queued",
224                    false,
225                    "success",
226                ),
227            ])
228            .gap(tokens::SPACE_1)
229            .width(Size::Fill(1.0)),
230        ])]),
231    ])
232    .key("metric:table.card")
233    .width(Size::Fill(1.2))
234    .height(Size::Fill(1.0))
235}
236
237fn data_row(
238    status: &'static str,
239    title: &'static str,
240    owner: &'static str,
241    state: &'static str,
242    selected: bool,
243    tone: &'static str,
244) -> El {
245    let status_badge = match tone {
246        "success" => badge(status).success(),
247        "warning" => badge(status).warning(),
248        "destructive" => badge(status).destructive(),
249        _ => badge(status).info(),
250    };
251    let status_badge = if selected {
252        status_badge.key("metric:table.badge")
253    } else {
254        status_badge
255    };
256
257    let mut row = table_row([
258        table_cell(status_badge).width(Size::Fixed(70.0)),
259        column([
260            text(title)
261                .font_weight(FontWeight::Medium)
262                .ellipsis()
263                .width(Size::Fill(1.0)),
264            text("Default styling probe.")
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0)),
271        table_cell(text(owner).muted()).width(Size::Fixed(110.0)),
272        table_cell(text(state).label().small()).width(Size::Fixed(86.0)),
273    ])
274    .key(if selected {
275        "metric:table.row".to_string()
276    } else {
277        format!("row-{title}")
278    })
279    .focusable();
280
281    if selected {
282        row = row.selected();
283    }
284
285    row
286}
287
288fn command_card() -> El {
289    card([
290        card_header([card_title("Command surface")]),
291        card_content([
292            text_input(
293                "Search commands...",
294                &Selection::default(),
295                "command-search",
296            )
297            .key("metric:command.input")
298            .width(Size::Fill(1.0)),
299            popover_panel([
300                command_row("git-branch", "New branch", "Ctrl+B").key("metric:command.row"),
301                command_row("git-commit", "Commit staged files", "Ctrl+Enter")
302                    .key("command-row-commit"),
303                command_row("refresh-cw", "Refresh repository", "Ctrl+R")
304                    .key("command-row-refresh"),
305                command_row("alert-circle", "Force push", "Danger").key("command-row-force"),
306            ])
307            .width(Size::Fill(1.0)),
308            scroll([form_probe()]).key("form-probe-scroll"),
309        ])
310        .height(Size::Fill(1.0)),
311    ])
312    .key("metric:command.card")
313    .width(Size::Fill(0.8))
314    .height(Size::Fill(1.0))
315}
316
317fn form_probe() -> El {
318    form([
319        form_item([
320            form_label("Valid input"),
321            form_control(
322                text_input(
323                    "Valid input",
324                    &Selection::caret("valid-input", 11),
325                    "valid-input",
326                )
327                .key("metric:form.input"),
328            ),
329            form_description("Default field spacing and helper text."),
330        ]),
331        form_item([
332            form_label("Invalid input"),
333            form_control(
334                text_input(
335                    "Invalid input",
336                    &Selection::caret("invalid-input", 13),
337                    "invalid-input",
338                )
339                .invalid(),
340            ),
341            form_message("This field needs attention."),
342        ]),
343        row([
344            button("Disabled").secondary().disabled(),
345            button("Loading").primary().loading(),
346            spacer(),
347        ]),
348    ])
349    .padding(tokens::SPACE_3)
350    .fill(tokens::MUTED)
351    .stroke(tokens::BORDER)
352    .radius(tokens::RADIUS_MD)
353}
354
355fn icon_cell(label: &'static str) -> El {
356    El::new(Kind::Custom("icon_cell"))
357        .style_profile(StyleProfile::Surface)
358        .text(label)
359        .text_align(TextAlign::Center)
360        .caption()
361        .font_weight(FontWeight::Semibold)
362        .fill(tokens::MUTED)
363        .stroke(tokens::BORDER)
364        .radius(tokens::RADIUS_SM)
365        .width(Size::Fixed(26.0))
366        .height(Size::Fixed(26.0))
367}
examples/custom_paint.rs (line 58)
44fn graph_cell(lane: u8, selected: bool) -> El {
45    let lane_color = lane_palette(lane);
46    let ring_color = if selected {
47        Color::rgb(245, 245, 250)
48    } else {
49        lane_color
50    };
51    let ring_w = if selected { 2.5 } else { 1.5 };
52    let radius = 5.0;
53    let line_w = 2.0;
54    let lane_frac = (lane as f32 + 0.5) / LANE_COUNT as f32;
55
56    El::new(Kind::Custom("graph_cell"))
57        .width(Size::Fixed(GRAPH_WIDTH))
58        .height(Size::Fixed(ROW_HEIGHT))
59        .shader(
60            ShaderBinding::custom("commit_node")
61                .color("vec_a", tokens::BACKGROUND)
62                .color("vec_b", ring_color)
63                .vec4("vec_c", [radius, ring_w, line_w, lane_frac]),
64        )
65        .fill(lane_color)
66}
67
68fn build_row(c: &FakeCommit, idx: usize, selected: bool) -> El {
69    row([
70        graph_cell(c.lane, selected),
71        text(c.sha).mono().muted(),
72        text(c.subject),
73        spacer(),
74        text(format!("{} · {}", c.author, c.when)).muted(),
75    ])
76    .key(format!("commit-{idx}"))
77    .gap(tokens::SPACE_3)
78    .padding(Sides::xy(tokens::SPACE_2, 0.0))
79    .height(Size::Fixed(ROW_HEIGHT))
80    .align(Align::Center)
81}
examples/scroll_list.rs (line 25)
15fn scroll_list_fixture() -> El {
16    let rows: Vec<El> = (0..20)
17        .map(|i| {
18            row([
19                badge(format!("#{i}")).info(),
20                text(format!("Notification {i}")).bold(),
21                spacer(),
22                text(format!("{}m ago", i + 1)).muted(),
23            ])
24            .gap(tokens::SPACE_2)
25            .height(Size::Fixed(44.0))
26            .padding(Sides::xy(tokens::SPACE_3, tokens::SPACE_2))
27        })
28        .collect();
29
30    let list = scroll(rows)
31        .key("notifications")
32        .height(Size::Fixed(420.0))
33        .padding(tokens::SPACE_2);
34
35    column([
36        h2("Notifications"),
37        text("Roll the wheel inside the panel to scroll. The content is taller than the viewport.")
38            .muted(),
39        list,
40    ])
41    .gap(tokens::SPACE_4)
42    .padding(tokens::SPACE_7)
43}
examples/circular_layout.rs (line 78)
48fn fixture() -> El {
49    let centre = h2("Compass").center_text();
50    let dirs = [
51        ("North", "n"),
52        ("NE", "ne"),
53        ("East", "e"),
54        ("SE", "se"),
55        ("South", "s"),
56        ("SW", "sw"),
57        ("West", "w"),
58        ("NW", "nw"),
59    ];
60
61    let mut children: Vec<El> = vec![centre];
62    for (label, k) in dirs {
63        children.push(button(label).key(k).primary());
64    }
65
66    column([
67        h1("Custom layout — circular"),
68        paragraph(
69            "Eight buttons positioned on a circle by an author-supplied \
70             LayoutFn. Stock paint, automatic hover/press, and hit-test \
71             all keep working — only the rect distribution changed.",
72        )
73        .muted(),
74        stack(children)
75            .key("compass")
76            .layout(circular)
77            .width(Size::Fill(1.0))
78            .height(Size::Fixed(360.0)),
79    ])
80    .gap(tokens::SPACE_4)
81    .padding(tokens::SPACE_7)
82}
Source

pub fn hug(self) -> Self

Source

pub fn fill_size(self) -> Self

Examples found in repository?
examples/polish_calibration.rs (line 39)
35fn polish_calibration() -> El {
36    row([sidebar(), main_panel()])
37        .key("metric:root")
38        .gap(0.0)
39        .fill_size()
40        .align(Align::Stretch)
41        .fill(tokens::BACKGROUND)
42}
More examples
Hide additional examples
examples/settings_calibration.rs (line 36)
32fn settings_calibration() -> El {
33    row([settings_sidebar(), settings_main()])
34        .key("metric:root")
35        .gap(0.0)
36        .fill_size()
37        .align(Align::Stretch)
38        .fill(tokens::BACKGROUND)
39}
examples/dashboard_01_calibration.rs (line 36)
32fn dashboard_01_calibration() -> El {
33    row([dashboard_sidebar(), dashboard_main()])
34        .key("metric:root")
35        .gap(0.0)
36        .fill_size()
37        .align(Align::Stretch)
38        .fill(tokens::BACKGROUND)
39}
examples/palette_demo.rs (line 118)
77fn palette_demo(label: &'static str, palette: &Palette) -> El {
78    column([
79        row([
80            column([
81                h1("Palette demo"),
82                text("Aetna and Radix palettes rendered through Aetna tokens.").muted(),
83            ])
84            .gap(tokens::SPACE_2)
85            .height(Size::Hug),
86            spacer(),
87            badge(label).muted(),
88        ])
89        .align(Align::Start)
90        .height(Size::Hug),
91        row([
92            token_section(
93                "Core tokens",
94                "The shadcn-shaped semantic vocabulary.",
95                &CORE_TOKENS,
96                palette,
97            )
98            .width(Size::Fill(1.25)),
99            column([
100                token_section(
101                    "Aetna extensions",
102                    "Component and status tokens layered on top.",
103                    &EXTENSION_TOKENS,
104                    palette,
105                ),
106                component_section(),
107            ])
108            .gap(tokens::SPACE_4)
109            .width(Size::Fill(1.0))
110            .height(Size::Fill(1.0)),
111        ])
112        .gap(tokens::SPACE_4)
113        .align(Align::Stretch)
114        .height(Size::Fill(1.0)),
115    ])
116    .padding(tokens::SPACE_8)
117    .gap(tokens::SPACE_6)
118    .fill_size()
119    .fill(tokens::BACKGROUND)
120}
Source

pub fn size(self, size: ComponentSize) -> Self

Set the t-shirt size for stock controls.

Source

pub fn medium(self) -> Self

Source

pub fn large(self) -> Self

Source

pub fn metrics_role(self, role: MetricsRole) -> Self

Set the theme-facing stock metrics role for this widget.

Examples found in repository?
examples/polish_calibration.rs (line 80)
67fn nav_item(icon: &'static str, label: &'static str, selected: bool) -> El {
68    let mut item = row([
69        icon_cell(icon),
70        text(label)
71            .font_weight(FontWeight::Medium)
72            .ellipsis()
73            .width(Size::Fill(1.0)),
74    ])
75    .key(if selected {
76        "metric:sidebar.nav.row".to_string()
77    } else {
78        format!("nav-{label}")
79    })
80    .metrics_role(MetricsRole::ListItem)
81    .gap(tokens::SPACE_3)
82    .padding(Sides::xy(tokens::SPACE_2, 0.0))
83    .height(Size::Fixed(40.0))
84    .align(Align::Center)
85    .focusable();
86
87    if selected {
88        item = item.current();
89    }
90
91    item
92}
More examples
Hide additional examples
examples/settings_calibration.rs (line 151)
134fn settings_nav_item(label: &'static str, selected: bool) -> El {
135    let mut item = row([
136        El::new(Kind::Custom("nav-dot"))
137            .fill(tokens::MUTED_FOREGROUND)
138            .radius(tokens::RADIUS_PILL)
139            .width(Size::Fixed(6.0))
140            .height(Size::Fixed(6.0)),
141        text(label)
142            .font_weight(FontWeight::Medium)
143            .ellipsis()
144            .width(Size::Fill(1.0)),
145    ])
146    .key(if selected {
147        "metric:settings.nav.row".to_string()
148    } else {
149        format!("settings-nav-{label}")
150    })
151    .metrics_role(MetricsRole::ListItem)
152    .align(Align::Center)
153    .focusable();
154
155    if selected {
156        item = item.current();
157    } else {
158        item = item.color(tokens::MUTED_FOREGROUND);
159    }
160
161    item
162}
163
164fn settings_body() -> El {
165    column([
166        column([
167            h1("Account").heading().key("metric:section.title"),
168            text("Manage identity, workspace defaults, and security preferences.")
169                .muted()
170                .wrap_text()
171                .key("metric:page.subtitle"),
172        ])
173        .gap(tokens::SPACE_1)
174        .height(Size::Hug),
175        scroll([profile_card(), preferences_card()])
176            .key("settings-body-scroll")
177            .gap(tokens::SPACE_4)
178            .width(Size::Fill(1.0))
179            .height(Size::Fill(1.0)),
180    ])
181    .gap(tokens::SPACE_4)
182    .width(Size::Fill(1.0))
183    .height(Size::Fill(1.0))
184}
185
186fn profile_card() -> El {
187    card([
188        card_header([
189            card_title("Profile"),
190            card_description("This information appears in audit logs and shared documents."),
191        ]),
192        card_content([form([
193            row([
194                setting_field("Display name", "Alicia Koch", "display-name"),
195                setting_field("Email", "alicia@acme.co", "email"),
196            ])
197            .gap(tokens::SPACE_3),
198            row([
199                setting_select("Role", "Workspace admin", "role"),
200                setting_select("Region", "US East", "region"),
201            ])
202            .gap(tokens::SPACE_3),
203        ])]),
204    ])
205    .key("metric:profile.card")
206}
207
208fn setting_field(label: &'static str, value: &'static str, key: &'static str) -> El {
209    form_item([
210        form_label(label),
211        form_control(
212            text_input(value, &Selection::caret(key, value.len()), key).key(
213                if key == "display-name" {
214                    "metric:form.input"
215                } else {
216                    key
217                },
218            ),
219        ),
220    ])
221    .width(Size::Fill(1.0))
222}
223
224fn setting_select(label: &'static str, value: &'static str, key: &'static str) -> El {
225    form_item([form_label(label), form_control(select_trigger(key, value))]).width(Size::Fill(1.0))
226}
227
228fn preferences_card() -> El {
229    card([
230        card_header([
231            card_title("Preferences"),
232            card_description("Defaults used when creating new dashboards and exports."),
233        ]),
234        card_content([column([
235            preference_row(
236                "Compact navigation",
237                "Use tighter rows in the sidebar and command menus.",
238                switch(true).key("compact-navigation"),
239            ),
240            divider(),
241            preference_row(
242                "Email summaries",
243                "Send a daily digest when documents change.",
244                switch(false).key("email-summaries"),
245            ),
246            divider(),
247            preference_row(
248                "Require approval",
249                "Route external sharing through an owner review.",
250                checkbox(true).key("approval-required"),
251            ),
252        ])
253        .gap(0.0)
254        .width(Size::Fill(1.0))])
255        .padding(0.0),
256    ])
257    .key("metric:preferences.card")
258}
259
260fn preference_row(title: &'static str, description: &'static str, control: El) -> El {
261    row([
262        column([
263            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
264            text(description)
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0))
271        .height(Size::Hug),
272        control,
273    ])
274    .key(if title == "Compact navigation" {
275        "metric:preference.row".to_string()
276    } else {
277        format!("preference-{title}")
278    })
279    .metrics_role(MetricsRole::PreferenceRow)
280    .gap(tokens::SPACE_4)
281    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_3))
282    .align(Align::Center)
283}
284
285fn settings_aside() -> El {
286    column([security_card(), scale_card()])
287        .gap(tokens::SPACE_4)
288        .width(Size::Fixed(300.0))
289        .height(Size::Fill(1.0))
290}
291
292fn security_card() -> El {
293    card([
294        card_header([
295            card_title("Security"),
296            card_description("Two-factor authentication is enabled for all privileged users."),
297        ]),
298        card_content([
299            compact_stat("Passkeys", "2 registered", badge("On").success()),
300            compact_stat("Sessions", "3 active", button("Review").secondary()),
301        ]),
302    ])
303    .width(Size::Fill(1.0))
304}
305
306fn scale_card() -> El {
307    card([
308        card_header([
309            card_title("Interface scale"),
310            card_description("Reference captures keep browser zoom fixed and vary root UI scale."),
311        ]),
312        card_content([
313            row([text("Dense").caption(), spacer(), text("Default").caption()]),
314            slider(0.66, tokens::PRIMARY)
315                .key("interface-scale")
316                .width(Size::Fill(1.0)),
317        ]),
318    ])
319    .width(Size::Fill(1.0))
320}
321
322fn compact_stat(title: &'static str, detail: &'static str, control: El) -> El {
323    row([
324        column([
325            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
326            text(detail).caption().ellipsis().width(Size::Fill(1.0)),
327        ])
328        .gap(2.0)
329        .width(Size::Fill(1.0))
330        .height(Size::Hug),
331        control,
332    ])
333    .gap(tokens::SPACE_2)
334    .height(Size::Fixed(44.0))
335    .align(Align::Center)
336}
337
338fn section_label(label: &'static str) -> El {
339    text(label)
340        .caption()
341        .height(Size::Fixed(22.0))
342        .padding(Sides::xy(tokens::SPACE_2, 0.0))
343}
344
345fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
346    let mut item = row([
347        icon(icon_name)
348            .color(tokens::MUTED_FOREGROUND)
349            .icon_size(tokens::ICON_SM)
350            .width(Size::Fixed(tokens::ICON_SM)),
351        text(label)
352            .font_weight(FontWeight::Medium)
353            .ellipsis()
354            .width(Size::Fill(1.0)),
355    ])
356    .key(if selected {
357        "metric:sidebar.nav.row".to_string()
358    } else {
359        format!("side-item-{label}")
360    })
361    .metrics_role(MetricsRole::ListItem)
362    .gap(tokens::SPACE_2)
363    .padding(Sides::xy(tokens::SPACE_2, 0.0))
364    .height(Size::Fixed(32.0))
365    .align(Align::Center)
366    .focusable();
367
368    if selected {
369        item = item.current();
370    } else {
371        item = item.color(tokens::MUTED_FOREGROUND);
372    }
373
374    item
375}
examples/dashboard_01_calibration.rs (line 125)
109fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
110    let mut item = row([
111        icon(icon_name)
112            .color(tokens::MUTED_FOREGROUND)
113            .icon_size(tokens::ICON_SM)
114            .width(Size::Fixed(tokens::ICON_SM)),
115        text(label)
116            .font_weight(FontWeight::Medium)
117            .ellipsis()
118            .width(Size::Fill(1.0)),
119    ])
120    .key(if selected {
121        "metric:sidebar.nav.row".to_string()
122    } else {
123        format!("side-item-{label}")
124    })
125    .metrics_role(MetricsRole::ListItem)
126    .gap(tokens::SPACE_2)
127    .padding(Sides::xy(tokens::SPACE_2, 0.0))
128    .height(Size::Fixed(32.0))
129    .align(Align::Center)
130    .focusable();
131
132    if selected {
133        item = item.current();
134    } else {
135        item = item.color(tokens::MUTED_FOREGROUND);
136    }
137
138    item
139}
Source

pub fn padding(self, p: impl Into<Sides>) -> Self

Examples found in repository?
examples/scrollbar.rs (line 28)
20fn list_rows() -> Vec<El> {
21    (0..40)
22        .map(|i| {
23            row([
24                text(format!("{i:02}.")).mono().muted(),
25                text(format!("scrollable list item {i}")),
26            ])
27            .gap(tokens::SPACE_2)
28            .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_1))
29            .height(Size::Fixed(28.0))
30            .align(Align::Center)
31        })
32        .collect()
33}
34
35fn fixture() -> El {
36    column([
37        h2("Scrollbar"),
38        text("scroll() and virtual_list() show a draggable thumb by default.").muted(),
39        row([
40            // 1) scroll() — default-on scrollbar.
41            column([
42                text("scroll() — default").bold(),
43                scroll(list_rows())
44                    .height(Size::Fixed(240.0))
45                    .padding(tokens::SPACE_2)
46                    .stroke(tokens::BORDER)
47                    .stroke_width(1.0)
48                    .radius(tokens::RADIUS_MD),
49            ])
50            .gap(tokens::SPACE_2)
51            .width(Size::Fill(1.0))
52            .height(Size::Hug),
53            // 2) virtual_list — thumb scales to content size.
54            column([
55                text("virtual_list(200, 28)").bold(),
56                virtual_list(200, 28.0, |i| {
57                    row([
58                        text(format!("{i:03}")).mono().muted(),
59                        text(format!("row {i}")),
60                    ])
61                    .gap(tokens::SPACE_2)
62                    .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_1))
63                    .height(Size::Fixed(28.0))
64                    .align(Align::Center)
65                })
66                .height(Size::Fixed(240.0))
67                .padding(tokens::SPACE_2)
68                .stroke(tokens::BORDER)
69                .stroke_width(1.0)
70                .radius(tokens::RADIUS_MD),
71            ])
72            .gap(tokens::SPACE_2)
73            .width(Size::Fill(1.0))
74            .height(Size::Hug),
75            // 3) Opt-out: same content, no thumb.
76            column([
77                text("scroll().no_scrollbar()").bold(),
78                scroll(list_rows())
79                    .no_scrollbar()
80                    .height(Size::Fixed(240.0))
81                    .padding(tokens::SPACE_2)
82                    .stroke(tokens::BORDER)
83                    .stroke_width(1.0)
84                    .radius(tokens::RADIUS_MD),
85            ])
86            .gap(tokens::SPACE_2)
87            .width(Size::Fill(1.0))
88            .height(Size::Hug),
89        ])
90        .gap(tokens::SPACE_4)
91        .width(Size::Fill(1.0)),
92    ])
93    .gap(tokens::SPACE_4)
94    .padding(tokens::SPACE_7)
95}
More examples
Hide additional examples
examples/custom_paint.rs (line 78)
68fn build_row(c: &FakeCommit, idx: usize, selected: bool) -> El {
69    row([
70        graph_cell(c.lane, selected),
71        text(c.sha).mono().muted(),
72        text(c.subject),
73        spacer(),
74        text(format!("{} · {}", c.author, c.when)).muted(),
75    ])
76    .key(format!("commit-{idx}"))
77    .gap(tokens::SPACE_3)
78    .padding(Sides::xy(tokens::SPACE_2, 0.0))
79    .height(Size::Fixed(ROW_HEIGHT))
80    .align(Align::Center)
81}
82
83fn fixture() -> El {
84    #[rustfmt::skip]
85    let commits = [
86        FakeCommit { sha: "8a3f1c9", subject: "fix race condition in scheduler", author: "ada",     when: "12m", lane: 0 },
87        FakeCommit { sha: "1b07d4e", subject: "tweak token tooltip wording",     author: "linus",   when: "1h",  lane: 0 },
88        FakeCommit { sha: "9f2e4a1", subject: "wire avatar fallback identicon",  author: "joelle",  when: "3h",  lane: 1 },
89        FakeCommit { sha: "44ab8d2", subject: "diff: word-level highlight pass", author: "raphael", when: "5h",  lane: 1 },
90        FakeCommit { sha: "61c0fe7", subject: "ci: bump rust toolchain to 1.85", author: "mei",     when: "7h",  lane: 2 },
91        FakeCommit { sha: "a90215b", subject: "switch logging to env_logger",    author: "isabel",  when: "1d",  lane: 2 },
92        FakeCommit { sha: "0d7e3c4", subject: "drop unused commit_detail cache", author: "noor",    when: "1d",  lane: 1 },
93        FakeCommit { sha: "33b2118", subject: "context-menu spacing pass",       author: "kira",    when: "2d",  lane: 3 },
94    ];
95    let selected_idx = 3;
96    let rows = commits
97        .iter()
98        .enumerate()
99        .map(|(i, c)| build_row(c, i, i == selected_idx))
100        .collect::<Vec<_>>();
101
102    column([
103        h2("Custom-painted commit graph"),
104        text("8 commits · custom shader paints lane line + circle node").muted(),
105        column(rows).gap(0.0),
106    ])
107    .padding(tokens::SPACE_4)
108    .gap(tokens::SPACE_2)
109}
examples/virtual_list.rs (line 35)
18fn build_row(i: usize) -> El {
19    let badge_el = match i % 5 {
20        0 => badge("info").muted(),
21        1 => badge("warn").warning(),
22        2 => badge("ok").success(),
23        3 => badge("err").destructive(),
24        _ => spacer(),
25    };
26    row([
27        text(format!("#{i:05}")).mono(),
28        spacer(),
29        text(format!("entry {i}")),
30        spacer(),
31        badge_el,
32    ])
33    .key(format!("row-{i}"))
34    .gap(tokens::SPACE_3)
35    .padding(Sides::xy(tokens::SPACE_3, tokens::SPACE_2))
36    .height(Size::Fixed(ROW_HEIGHT))
37}
38
39fn fixture() -> El {
40    column([
41        h1("Virtualized list"),
42        paragraph(format!(
43            "{ROW_COUNT} rows × {ROW_HEIGHT}px in a windowed viewport. \
44             Only the rows intersecting the viewport are realized — see \
45             `tree.txt` for proof."
46        ))
47        .muted(),
48        virtual_list(ROW_COUNT, ROW_HEIGHT, build_row)
49            .key("entries")
50            .height(Size::Fill(1.0)),
51    ])
52    .gap(tokens::SPACE_4)
53    .padding(tokens::SPACE_7)
54}
examples/polish_calibration.rs (line 59)
44fn sidebar() -> El {
45    column([
46        column([h2("Aetna"), text("calibration").muted()])
47            .key("metric:sidebar.brand")
48            .gap(tokens::SPACE_1)
49            .height(Size::Hug),
50        spacer().height(Size::Fixed(tokens::SPACE_4)),
51        nav_item("01", "Overview", true),
52        nav_item("02", "Commands", false),
53        nav_item("03", "Tables", false),
54        nav_item("04", "Forms", false),
55        spacer(),
56        badge("dark theme").muted(),
57    ])
58    .gap(tokens::SPACE_2)
59    .padding(tokens::SPACE_5)
60    .key("metric:sidebar")
61    .width(Size::Fixed(220.0))
62    .height(Size::Fill(1.0))
63    .fill(tokens::CARD)
64    .stroke(tokens::BORDER)
65}
66
67fn nav_item(icon: &'static str, label: &'static str, selected: bool) -> El {
68    let mut item = row([
69        icon_cell(icon),
70        text(label)
71            .font_weight(FontWeight::Medium)
72            .ellipsis()
73            .width(Size::Fill(1.0)),
74    ])
75    .key(if selected {
76        "metric:sidebar.nav.row".to_string()
77    } else {
78        format!("nav-{label}")
79    })
80    .metrics_role(MetricsRole::ListItem)
81    .gap(tokens::SPACE_3)
82    .padding(Sides::xy(tokens::SPACE_2, 0.0))
83    .height(Size::Fixed(40.0))
84    .align(Align::Center)
85    .focusable();
86
87    if selected {
88        item = item.current();
89    }
90
91    item
92}
93
94fn main_panel() -> El {
95    column([
96        toolbar(),
97        column([
98            row([
99                kpi_card("Latency", "42 ms", "-18%", true),
100                kpi_card("Runs", "1,284", "+12%", true),
101                kpi_card("Errors", "7", "+2", false),
102            ])
103            .gap(tokens::SPACE_4),
104            row([table_card(), command_card()])
105                .gap(tokens::SPACE_4)
106                .height(Size::Fill(1.0))
107                .align(Align::Stretch),
108        ])
109        .gap(tokens::SPACE_4)
110        .height(Size::Fill(1.0))
111        .align(Align::Stretch),
112    ])
113    .padding(tokens::SPACE_7)
114    .gap(tokens::SPACE_2)
115    .width(Size::Fill(1.0))
116    .height(Size::Fill(1.0))
117}
118
119fn toolbar() -> El {
120    row([
121        column([
122            h1("Polish calibration").key("metric:page.title"),
123            text("A representative app surface for default tuning.")
124                .muted()
125                .key("metric:page.subtitle"),
126        ])
127        .gap(tokens::SPACE_2)
128        .height(Size::Hug),
129        spacer(),
130        button_with_icon("search", "Preview")
131            .secondary()
132            .key("metric:action.secondary"),
133        button_with_icon("upload", "Publish")
134            .primary()
135            .key("metric:action.primary"),
136    ])
137    .key("metric:header")
138    .gap(tokens::SPACE_4)
139    .height(Size::Hug)
140    .align(Align::Start)
141}
142
143fn kpi_card(label: &'static str, value: &'static str, delta: &'static str, positive: bool) -> El {
144    let delta_badge = if positive {
145        badge(delta).success()
146    } else {
147        badge(delta).destructive()
148    };
149    let delta_badge = if label == "Latency" {
150        delta_badge.key("metric:kpi.badge")
151    } else {
152        delta_badge
153    };
154    let value_text = h2(value).display();
155    let value_text = if label == "Latency" {
156        value_text.key("metric:kpi.value")
157    } else {
158        value_text
159    };
160    card([
161        card_header([card_title(label)]),
162        card_content([
163            row([value_text, spacer(), delta_badge]).align(Align::Center),
164            text(if positive {
165                "Moving in the expected direction"
166            } else {
167                "Needs visual attention"
168            })
169            .muted(),
170        ])
171        .gap(tokens::SPACE_6),
172    ])
173    .key(if label == "Latency" {
174        "metric:kpi.card"
175    } else {
176        label
177    })
178    .width(Size::Fill(1.0))
179}
180
181fn table_card() -> El {
182    card([
183        card_header([card_title("Reference rows")]),
184        card_content([table([
185            table_header([table_row([
186                table_head("Status").width(Size::Fixed(86.0)),
187                table_head("Surface").width(Size::Fill(1.0)),
188                table_head("Owner").width(Size::Fixed(110.0)),
189                table_head("State").width(Size::Fixed(86.0)),
190            ])
191            .key("metric:table.header")]),
192            divider(),
193            table_body([
194                data_row("OK", "Settings card", "core", "selected", true, "success"),
195                data_row(
196                    "WARN",
197                    "Command palette density",
198                    "widgets",
199                    "needs work",
200                    false,
201                    "warning",
202                ),
203                data_row(
204                    "ERR",
205                    "Disabled and invalid states",
206                    "style",
207                    "missing",
208                    false,
209                    "destructive",
210                ),
211                data_row(
212                    "INFO",
213                    "Token resolution",
214                    "theme",
215                    "planned",
216                    false,
217                    "info",
218                ),
219                data_row(
220                    "OK",
221                    "Popover elevation",
222                    "shader",
223                    "queued",
224                    false,
225                    "success",
226                ),
227            ])
228            .gap(tokens::SPACE_1)
229            .width(Size::Fill(1.0)),
230        ])]),
231    ])
232    .key("metric:table.card")
233    .width(Size::Fill(1.2))
234    .height(Size::Fill(1.0))
235}
236
237fn data_row(
238    status: &'static str,
239    title: &'static str,
240    owner: &'static str,
241    state: &'static str,
242    selected: bool,
243    tone: &'static str,
244) -> El {
245    let status_badge = match tone {
246        "success" => badge(status).success(),
247        "warning" => badge(status).warning(),
248        "destructive" => badge(status).destructive(),
249        _ => badge(status).info(),
250    };
251    let status_badge = if selected {
252        status_badge.key("metric:table.badge")
253    } else {
254        status_badge
255    };
256
257    let mut row = table_row([
258        table_cell(status_badge).width(Size::Fixed(70.0)),
259        column([
260            text(title)
261                .font_weight(FontWeight::Medium)
262                .ellipsis()
263                .width(Size::Fill(1.0)),
264            text("Default styling probe.")
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0)),
271        table_cell(text(owner).muted()).width(Size::Fixed(110.0)),
272        table_cell(text(state).label().small()).width(Size::Fixed(86.0)),
273    ])
274    .key(if selected {
275        "metric:table.row".to_string()
276    } else {
277        format!("row-{title}")
278    })
279    .focusable();
280
281    if selected {
282        row = row.selected();
283    }
284
285    row
286}
287
288fn command_card() -> El {
289    card([
290        card_header([card_title("Command surface")]),
291        card_content([
292            text_input(
293                "Search commands...",
294                &Selection::default(),
295                "command-search",
296            )
297            .key("metric:command.input")
298            .width(Size::Fill(1.0)),
299            popover_panel([
300                command_row("git-branch", "New branch", "Ctrl+B").key("metric:command.row"),
301                command_row("git-commit", "Commit staged files", "Ctrl+Enter")
302                    .key("command-row-commit"),
303                command_row("refresh-cw", "Refresh repository", "Ctrl+R")
304                    .key("command-row-refresh"),
305                command_row("alert-circle", "Force push", "Danger").key("command-row-force"),
306            ])
307            .width(Size::Fill(1.0)),
308            scroll([form_probe()]).key("form-probe-scroll"),
309        ])
310        .height(Size::Fill(1.0)),
311    ])
312    .key("metric:command.card")
313    .width(Size::Fill(0.8))
314    .height(Size::Fill(1.0))
315}
316
317fn form_probe() -> El {
318    form([
319        form_item([
320            form_label("Valid input"),
321            form_control(
322                text_input(
323                    "Valid input",
324                    &Selection::caret("valid-input", 11),
325                    "valid-input",
326                )
327                .key("metric:form.input"),
328            ),
329            form_description("Default field spacing and helper text."),
330        ]),
331        form_item([
332            form_label("Invalid input"),
333            form_control(
334                text_input(
335                    "Invalid input",
336                    &Selection::caret("invalid-input", 13),
337                    "invalid-input",
338                )
339                .invalid(),
340            ),
341            form_message("This field needs attention."),
342        ]),
343        row([
344            button("Disabled").secondary().disabled(),
345            button("Loading").primary().loading(),
346            spacer(),
347        ]),
348    ])
349    .padding(tokens::SPACE_3)
350    .fill(tokens::MUTED)
351    .stroke(tokens::BORDER)
352    .radius(tokens::RADIUS_MD)
353}
examples/scroll_list.rs (line 26)
15fn scroll_list_fixture() -> El {
16    let rows: Vec<El> = (0..20)
17        .map(|i| {
18            row([
19                badge(format!("#{i}")).info(),
20                text(format!("Notification {i}")).bold(),
21                spacer(),
22                text(format!("{}m ago", i + 1)).muted(),
23            ])
24            .gap(tokens::SPACE_2)
25            .height(Size::Fixed(44.0))
26            .padding(Sides::xy(tokens::SPACE_3, tokens::SPACE_2))
27        })
28        .collect();
29
30    let list = scroll(rows)
31        .key("notifications")
32        .height(Size::Fixed(420.0))
33        .padding(tokens::SPACE_2);
34
35    column([
36        h2("Notifications"),
37        text("Roll the wheel inside the panel to scroll. The content is taller than the viewport.")
38            .muted(),
39        list,
40    ])
41    .gap(tokens::SPACE_4)
42    .padding(tokens::SPACE_7)
43}
examples/circular_layout.rs (line 81)
48fn fixture() -> El {
49    let centre = h2("Compass").center_text();
50    let dirs = [
51        ("North", "n"),
52        ("NE", "ne"),
53        ("East", "e"),
54        ("SE", "se"),
55        ("South", "s"),
56        ("SW", "sw"),
57        ("West", "w"),
58        ("NW", "nw"),
59    ];
60
61    let mut children: Vec<El> = vec![centre];
62    for (label, k) in dirs {
63        children.push(button(label).key(k).primary());
64    }
65
66    column([
67        h1("Custom layout — circular"),
68        paragraph(
69            "Eight buttons positioned on a circle by an author-supplied \
70             LayoutFn. Stock paint, automatic hover/press, and hit-test \
71             all keep working — only the rect distribution changed.",
72        )
73        .muted(),
74        stack(children)
75            .key("compass")
76            .layout(circular)
77            .width(Size::Fill(1.0))
78            .height(Size::Fixed(360.0)),
79    ])
80    .gap(tokens::SPACE_4)
81    .padding(tokens::SPACE_7)
82}
Source

pub fn pt(self, v: f32) -> Self

Override only the top padding side, preserving the other three sides at their current value (whether from a constructor’s default_padding or a previous explicit .padding(...)). Mirrors Tailwind’s pt-N. Marks the padding as explicit, so the metrics pass will not stamp a density-driven value over it.

Source

pub fn pb(self, v: f32) -> Self

Override only the bottom padding side. Mirrors Tailwind’s pb-N. See Self::pt for composition semantics.

Source

pub fn pl(self, v: f32) -> Self

Override only the left padding side. Mirrors Tailwind’s pl-N. See Self::pt for composition semantics.

Source

pub fn pr(self, v: f32) -> Self

Override only the right padding side. Mirrors Tailwind’s pr-N. See Self::pt for composition semantics.

Source

pub fn px(self, v: f32) -> Self

Override the horizontal padding sides (left + right), preserving top and bottom. Mirrors Tailwind’s px-N. See Self::pt for composition semantics.

Source

pub fn py(self, v: f32) -> Self

Override the vertical padding sides (top + bottom), preserving left and right. Mirrors Tailwind’s py-N. See Self::pt for composition semantics.

Source

pub fn gap(self, g: f32) -> Self

Examples found in repository?
examples/polish_calibration.rs (line 38)
35fn polish_calibration() -> El {
36    row([sidebar(), main_panel()])
37        .key("metric:root")
38        .gap(0.0)
39        .fill_size()
40        .align(Align::Stretch)
41        .fill(tokens::BACKGROUND)
42}
43
44fn sidebar() -> El {
45    column([
46        column([h2("Aetna"), text("calibration").muted()])
47            .key("metric:sidebar.brand")
48            .gap(tokens::SPACE_1)
49            .height(Size::Hug),
50        spacer().height(Size::Fixed(tokens::SPACE_4)),
51        nav_item("01", "Overview", true),
52        nav_item("02", "Commands", false),
53        nav_item("03", "Tables", false),
54        nav_item("04", "Forms", false),
55        spacer(),
56        badge("dark theme").muted(),
57    ])
58    .gap(tokens::SPACE_2)
59    .padding(tokens::SPACE_5)
60    .key("metric:sidebar")
61    .width(Size::Fixed(220.0))
62    .height(Size::Fill(1.0))
63    .fill(tokens::CARD)
64    .stroke(tokens::BORDER)
65}
66
67fn nav_item(icon: &'static str, label: &'static str, selected: bool) -> El {
68    let mut item = row([
69        icon_cell(icon),
70        text(label)
71            .font_weight(FontWeight::Medium)
72            .ellipsis()
73            .width(Size::Fill(1.0)),
74    ])
75    .key(if selected {
76        "metric:sidebar.nav.row".to_string()
77    } else {
78        format!("nav-{label}")
79    })
80    .metrics_role(MetricsRole::ListItem)
81    .gap(tokens::SPACE_3)
82    .padding(Sides::xy(tokens::SPACE_2, 0.0))
83    .height(Size::Fixed(40.0))
84    .align(Align::Center)
85    .focusable();
86
87    if selected {
88        item = item.current();
89    }
90
91    item
92}
93
94fn main_panel() -> El {
95    column([
96        toolbar(),
97        column([
98            row([
99                kpi_card("Latency", "42 ms", "-18%", true),
100                kpi_card("Runs", "1,284", "+12%", true),
101                kpi_card("Errors", "7", "+2", false),
102            ])
103            .gap(tokens::SPACE_4),
104            row([table_card(), command_card()])
105                .gap(tokens::SPACE_4)
106                .height(Size::Fill(1.0))
107                .align(Align::Stretch),
108        ])
109        .gap(tokens::SPACE_4)
110        .height(Size::Fill(1.0))
111        .align(Align::Stretch),
112    ])
113    .padding(tokens::SPACE_7)
114    .gap(tokens::SPACE_2)
115    .width(Size::Fill(1.0))
116    .height(Size::Fill(1.0))
117}
118
119fn toolbar() -> El {
120    row([
121        column([
122            h1("Polish calibration").key("metric:page.title"),
123            text("A representative app surface for default tuning.")
124                .muted()
125                .key("metric:page.subtitle"),
126        ])
127        .gap(tokens::SPACE_2)
128        .height(Size::Hug),
129        spacer(),
130        button_with_icon("search", "Preview")
131            .secondary()
132            .key("metric:action.secondary"),
133        button_with_icon("upload", "Publish")
134            .primary()
135            .key("metric:action.primary"),
136    ])
137    .key("metric:header")
138    .gap(tokens::SPACE_4)
139    .height(Size::Hug)
140    .align(Align::Start)
141}
142
143fn kpi_card(label: &'static str, value: &'static str, delta: &'static str, positive: bool) -> El {
144    let delta_badge = if positive {
145        badge(delta).success()
146    } else {
147        badge(delta).destructive()
148    };
149    let delta_badge = if label == "Latency" {
150        delta_badge.key("metric:kpi.badge")
151    } else {
152        delta_badge
153    };
154    let value_text = h2(value).display();
155    let value_text = if label == "Latency" {
156        value_text.key("metric:kpi.value")
157    } else {
158        value_text
159    };
160    card([
161        card_header([card_title(label)]),
162        card_content([
163            row([value_text, spacer(), delta_badge]).align(Align::Center),
164            text(if positive {
165                "Moving in the expected direction"
166            } else {
167                "Needs visual attention"
168            })
169            .muted(),
170        ])
171        .gap(tokens::SPACE_6),
172    ])
173    .key(if label == "Latency" {
174        "metric:kpi.card"
175    } else {
176        label
177    })
178    .width(Size::Fill(1.0))
179}
180
181fn table_card() -> El {
182    card([
183        card_header([card_title("Reference rows")]),
184        card_content([table([
185            table_header([table_row([
186                table_head("Status").width(Size::Fixed(86.0)),
187                table_head("Surface").width(Size::Fill(1.0)),
188                table_head("Owner").width(Size::Fixed(110.0)),
189                table_head("State").width(Size::Fixed(86.0)),
190            ])
191            .key("metric:table.header")]),
192            divider(),
193            table_body([
194                data_row("OK", "Settings card", "core", "selected", true, "success"),
195                data_row(
196                    "WARN",
197                    "Command palette density",
198                    "widgets",
199                    "needs work",
200                    false,
201                    "warning",
202                ),
203                data_row(
204                    "ERR",
205                    "Disabled and invalid states",
206                    "style",
207                    "missing",
208                    false,
209                    "destructive",
210                ),
211                data_row(
212                    "INFO",
213                    "Token resolution",
214                    "theme",
215                    "planned",
216                    false,
217                    "info",
218                ),
219                data_row(
220                    "OK",
221                    "Popover elevation",
222                    "shader",
223                    "queued",
224                    false,
225                    "success",
226                ),
227            ])
228            .gap(tokens::SPACE_1)
229            .width(Size::Fill(1.0)),
230        ])]),
231    ])
232    .key("metric:table.card")
233    .width(Size::Fill(1.2))
234    .height(Size::Fill(1.0))
235}
236
237fn data_row(
238    status: &'static str,
239    title: &'static str,
240    owner: &'static str,
241    state: &'static str,
242    selected: bool,
243    tone: &'static str,
244) -> El {
245    let status_badge = match tone {
246        "success" => badge(status).success(),
247        "warning" => badge(status).warning(),
248        "destructive" => badge(status).destructive(),
249        _ => badge(status).info(),
250    };
251    let status_badge = if selected {
252        status_badge.key("metric:table.badge")
253    } else {
254        status_badge
255    };
256
257    let mut row = table_row([
258        table_cell(status_badge).width(Size::Fixed(70.0)),
259        column([
260            text(title)
261                .font_weight(FontWeight::Medium)
262                .ellipsis()
263                .width(Size::Fill(1.0)),
264            text("Default styling probe.")
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0)),
271        table_cell(text(owner).muted()).width(Size::Fixed(110.0)),
272        table_cell(text(state).label().small()).width(Size::Fixed(86.0)),
273    ])
274    .key(if selected {
275        "metric:table.row".to_string()
276    } else {
277        format!("row-{title}")
278    })
279    .focusable();
280
281    if selected {
282        row = row.selected();
283    }
284
285    row
286}
More examples
Hide additional examples
examples/settings_calibration.rs (line 35)
32fn settings_calibration() -> El {
33    row([settings_sidebar(), settings_main()])
34        .key("metric:root")
35        .gap(0.0)
36        .fill_size()
37        .align(Align::Stretch)
38        .fill(tokens::BACKGROUND)
39}
40
41fn settings_sidebar() -> El {
42    column([
43        row([
44            icon_slot("settings"),
45            column([
46                text("Workspace")
47                    .semibold()
48                    .ellipsis()
49                    .width(Size::Fill(1.0)),
50                text("Settings").caption().ellipsis().width(Size::Fill(1.0)),
51            ])
52            .gap(2.0)
53            .width(Size::Fill(1.0))
54            .height(Size::Hug),
55        ])
56        .gap(tokens::SPACE_2)
57        .height(Size::Fixed(44.0))
58        .align(Align::Center),
59        section_label("Personal"),
60        side_item("users", "Profile", false),
61        side_item("settings", "Account", true),
62        side_item("alert-circle", "Security", false),
63        side_item("bell", "Notifications", false),
64        spacer().height(Size::Fixed(tokens::SPACE_4)),
65        section_label("Workspace"),
66        side_item("file-text", "Billing", false),
67        side_item("bar-chart", "Appearance", false),
68        side_item("activity", "Integrations", false),
69        spacer(),
70        column([text("Changes sync after save.").caption().wrap_text()])
71            .padding(tokens::SPACE_2)
72            .fill(tokens::MUTED)
73            .radius(tokens::RADIUS_MD),
74    ])
75    .gap(tokens::SPACE_2)
76    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_2))
77    .key("metric:sidebar")
78    .width(Size::Fixed(244.0))
79    .height(Size::Fill(1.0))
80    .fill(tokens::CARD)
81    .stroke(tokens::BORDER)
82}
83
84fn settings_main() -> El {
85    column([
86        settings_header(),
87        row([settings_nav_card(), settings_body(), settings_aside()])
88            .gap(tokens::SPACE_4)
89            .padding(tokens::SPACE_4)
90            .height(Size::Fill(1.0))
91            .align(Align::Stretch),
92    ])
93    .width(Size::Fill(1.0))
94    .height(Size::Fill(1.0))
95}
96
97fn settings_header() -> El {
98    row([
99        icon_button("menu").ghost(),
100        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
101        h3("Settings").key("metric:page.title"),
102        spacer(),
103        button("Reset").secondary(),
104        button("Save changes").primary(),
105    ])
106    .key("metric:header")
107    .gap(tokens::SPACE_3)
108    .height(Size::Fixed(56.0))
109    .padding(Sides::xy(tokens::SPACE_4, 0.0))
110    .align(Align::Center)
111    .stroke(tokens::BORDER)
112}
113
114fn settings_nav_card() -> El {
115    column([
116        settings_nav_item("Account", true),
117        settings_nav_item("Security", false),
118        settings_nav_item("Notifications", false),
119        settings_nav_item("Appearance", false),
120        settings_nav_item("Billing", false),
121    ])
122    .gap(tokens::SPACE_1)
123    .padding(tokens::SPACE_1)
124    .width(Size::Fixed(220.0))
125    .height(Size::Fill(1.0))
126    .style_profile(StyleProfile::Surface)
127    .surface_role(SurfaceRole::Panel)
128    .fill(tokens::CARD)
129    .stroke(tokens::BORDER)
130    .radius(tokens::RADIUS_MD)
131    .shadow(tokens::SHADOW_MD)
132}
133
134fn settings_nav_item(label: &'static str, selected: bool) -> El {
135    let mut item = row([
136        El::new(Kind::Custom("nav-dot"))
137            .fill(tokens::MUTED_FOREGROUND)
138            .radius(tokens::RADIUS_PILL)
139            .width(Size::Fixed(6.0))
140            .height(Size::Fixed(6.0)),
141        text(label)
142            .font_weight(FontWeight::Medium)
143            .ellipsis()
144            .width(Size::Fill(1.0)),
145    ])
146    .key(if selected {
147        "metric:settings.nav.row".to_string()
148    } else {
149        format!("settings-nav-{label}")
150    })
151    .metrics_role(MetricsRole::ListItem)
152    .align(Align::Center)
153    .focusable();
154
155    if selected {
156        item = item.current();
157    } else {
158        item = item.color(tokens::MUTED_FOREGROUND);
159    }
160
161    item
162}
163
164fn settings_body() -> El {
165    column([
166        column([
167            h1("Account").heading().key("metric:section.title"),
168            text("Manage identity, workspace defaults, and security preferences.")
169                .muted()
170                .wrap_text()
171                .key("metric:page.subtitle"),
172        ])
173        .gap(tokens::SPACE_1)
174        .height(Size::Hug),
175        scroll([profile_card(), preferences_card()])
176            .key("settings-body-scroll")
177            .gap(tokens::SPACE_4)
178            .width(Size::Fill(1.0))
179            .height(Size::Fill(1.0)),
180    ])
181    .gap(tokens::SPACE_4)
182    .width(Size::Fill(1.0))
183    .height(Size::Fill(1.0))
184}
185
186fn profile_card() -> El {
187    card([
188        card_header([
189            card_title("Profile"),
190            card_description("This information appears in audit logs and shared documents."),
191        ]),
192        card_content([form([
193            row([
194                setting_field("Display name", "Alicia Koch", "display-name"),
195                setting_field("Email", "alicia@acme.co", "email"),
196            ])
197            .gap(tokens::SPACE_3),
198            row([
199                setting_select("Role", "Workspace admin", "role"),
200                setting_select("Region", "US East", "region"),
201            ])
202            .gap(tokens::SPACE_3),
203        ])]),
204    ])
205    .key("metric:profile.card")
206}
207
208fn setting_field(label: &'static str, value: &'static str, key: &'static str) -> El {
209    form_item([
210        form_label(label),
211        form_control(
212            text_input(value, &Selection::caret(key, value.len()), key).key(
213                if key == "display-name" {
214                    "metric:form.input"
215                } else {
216                    key
217                },
218            ),
219        ),
220    ])
221    .width(Size::Fill(1.0))
222}
223
224fn setting_select(label: &'static str, value: &'static str, key: &'static str) -> El {
225    form_item([form_label(label), form_control(select_trigger(key, value))]).width(Size::Fill(1.0))
226}
227
228fn preferences_card() -> El {
229    card([
230        card_header([
231            card_title("Preferences"),
232            card_description("Defaults used when creating new dashboards and exports."),
233        ]),
234        card_content([column([
235            preference_row(
236                "Compact navigation",
237                "Use tighter rows in the sidebar and command menus.",
238                switch(true).key("compact-navigation"),
239            ),
240            divider(),
241            preference_row(
242                "Email summaries",
243                "Send a daily digest when documents change.",
244                switch(false).key("email-summaries"),
245            ),
246            divider(),
247            preference_row(
248                "Require approval",
249                "Route external sharing through an owner review.",
250                checkbox(true).key("approval-required"),
251            ),
252        ])
253        .gap(0.0)
254        .width(Size::Fill(1.0))])
255        .padding(0.0),
256    ])
257    .key("metric:preferences.card")
258}
259
260fn preference_row(title: &'static str, description: &'static str, control: El) -> El {
261    row([
262        column([
263            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
264            text(description)
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0))
271        .height(Size::Hug),
272        control,
273    ])
274    .key(if title == "Compact navigation" {
275        "metric:preference.row".to_string()
276    } else {
277        format!("preference-{title}")
278    })
279    .metrics_role(MetricsRole::PreferenceRow)
280    .gap(tokens::SPACE_4)
281    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_3))
282    .align(Align::Center)
283}
284
285fn settings_aside() -> El {
286    column([security_card(), scale_card()])
287        .gap(tokens::SPACE_4)
288        .width(Size::Fixed(300.0))
289        .height(Size::Fill(1.0))
290}
291
292fn security_card() -> El {
293    card([
294        card_header([
295            card_title("Security"),
296            card_description("Two-factor authentication is enabled for all privileged users."),
297        ]),
298        card_content([
299            compact_stat("Passkeys", "2 registered", badge("On").success()),
300            compact_stat("Sessions", "3 active", button("Review").secondary()),
301        ]),
302    ])
303    .width(Size::Fill(1.0))
304}
305
306fn scale_card() -> El {
307    card([
308        card_header([
309            card_title("Interface scale"),
310            card_description("Reference captures keep browser zoom fixed and vary root UI scale."),
311        ]),
312        card_content([
313            row([text("Dense").caption(), spacer(), text("Default").caption()]),
314            slider(0.66, tokens::PRIMARY)
315                .key("interface-scale")
316                .width(Size::Fill(1.0)),
317        ]),
318    ])
319    .width(Size::Fill(1.0))
320}
321
322fn compact_stat(title: &'static str, detail: &'static str, control: El) -> El {
323    row([
324        column([
325            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
326            text(detail).caption().ellipsis().width(Size::Fill(1.0)),
327        ])
328        .gap(2.0)
329        .width(Size::Fill(1.0))
330        .height(Size::Hug),
331        control,
332    ])
333    .gap(tokens::SPACE_2)
334    .height(Size::Fixed(44.0))
335    .align(Align::Center)
336}
337
338fn section_label(label: &'static str) -> El {
339    text(label)
340        .caption()
341        .height(Size::Fixed(22.0))
342        .padding(Sides::xy(tokens::SPACE_2, 0.0))
343}
344
345fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
346    let mut item = row([
347        icon(icon_name)
348            .color(tokens::MUTED_FOREGROUND)
349            .icon_size(tokens::ICON_SM)
350            .width(Size::Fixed(tokens::ICON_SM)),
351        text(label)
352            .font_weight(FontWeight::Medium)
353            .ellipsis()
354            .width(Size::Fill(1.0)),
355    ])
356    .key(if selected {
357        "metric:sidebar.nav.row".to_string()
358    } else {
359        format!("side-item-{label}")
360    })
361    .metrics_role(MetricsRole::ListItem)
362    .gap(tokens::SPACE_2)
363    .padding(Sides::xy(tokens::SPACE_2, 0.0))
364    .height(Size::Fixed(32.0))
365    .align(Align::Center)
366    .focusable();
367
368    if selected {
369        item = item.current();
370    } else {
371        item = item.color(tokens::MUTED_FOREGROUND);
372    }
373
374    item
375}
examples/dashboard_01_calibration.rs (line 35)
32fn dashboard_01_calibration() -> El {
33    row([dashboard_sidebar(), dashboard_main()])
34        .key("metric:root")
35        .gap(0.0)
36        .fill_size()
37        .align(Align::Stretch)
38        .fill(tokens::BACKGROUND)
39}
40
41fn dashboard_sidebar() -> El {
42    column([
43        row([
44            icon_cell("A"),
45            column([
46                text("Acme Inc.")
47                    .semibold()
48                    .ellipsis()
49                    .width(Size::Fill(1.0)),
50                text("Enterprise")
51                    .caption()
52                    .ellipsis()
53                    .width(Size::Fill(1.0)),
54            ])
55            .gap(2.0)
56            .width(Size::Fill(1.0))
57            .height(Size::Hug),
58        ])
59        .gap(tokens::SPACE_2)
60        .height(Size::Fixed(44.0))
61        .align(Align::Center),
62        section_label("Platform"),
63        side_item("layout-dashboard", "Dashboard", true),
64        side_item("activity", "Lifecycle", false),
65        side_item("bar-chart", "Analytics", false),
66        side_item("folder", "Projects", false),
67        spacer().height(Size::Fixed(tokens::SPACE_4)),
68        section_label("Documents"),
69        side_item("file-text", "Data library", false),
70        side_item("download", "Reports", false),
71        side_item("users", "Team", false),
72        spacer(),
73        row([
74            icon_cell("AK"),
75            column([
76                text("Alicia Koch")
77                    .semibold()
78                    .ellipsis()
79                    .width(Size::Fill(1.0)),
80                text("alicia@example.com")
81                    .caption()
82                    .ellipsis()
83                    .width(Size::Fill(1.0)),
84            ])
85            .gap(2.0)
86            .width(Size::Fill(1.0))
87            .height(Size::Hug),
88        ])
89        .gap(tokens::SPACE_2)
90        .height(Size::Fixed(50.0))
91        .align(Align::Center),
92    ])
93    .gap(tokens::SPACE_2)
94    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_2))
95    .key("metric:sidebar")
96    .width(Size::Fixed(244.0))
97    .height(Size::Fill(1.0))
98    .fill(tokens::CARD)
99    .stroke(tokens::BORDER)
100}
101
102fn section_label(label: &'static str) -> El {
103    text(label)
104        .caption()
105        .height(Size::Fixed(22.0))
106        .padding(Sides::xy(tokens::SPACE_2, 0.0))
107}
108
109fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
110    let mut item = row([
111        icon(icon_name)
112            .color(tokens::MUTED_FOREGROUND)
113            .icon_size(tokens::ICON_SM)
114            .width(Size::Fixed(tokens::ICON_SM)),
115        text(label)
116            .font_weight(FontWeight::Medium)
117            .ellipsis()
118            .width(Size::Fill(1.0)),
119    ])
120    .key(if selected {
121        "metric:sidebar.nav.row".to_string()
122    } else {
123        format!("side-item-{label}")
124    })
125    .metrics_role(MetricsRole::ListItem)
126    .gap(tokens::SPACE_2)
127    .padding(Sides::xy(tokens::SPACE_2, 0.0))
128    .height(Size::Fixed(32.0))
129    .align(Align::Center)
130    .focusable();
131
132    if selected {
133        item = item.current();
134    } else {
135        item = item.color(tokens::MUTED_FOREGROUND);
136    }
137
138    item
139}
140
141fn dashboard_main() -> El {
142    column([
143        dashboard_header(),
144        column([
145            row([
146                metric_card(
147                    "bar-chart",
148                    "Total Revenue",
149                    "$1,250.00",
150                    "+12.5%",
151                    "Trending up this month",
152                    true,
153                ),
154                metric_card(
155                    "users",
156                    "New Customers",
157                    "1,234",
158                    "-20%",
159                    "Acquisition needs attention",
160                    false,
161                ),
162                metric_card(
163                    "folder",
164                    "Active Accounts",
165                    "45,678",
166                    "+12.5%",
167                    "Strong user retention",
168                    true,
169                ),
170                metric_card(
171                    "activity",
172                    "Growth Rate",
173                    "4.5%",
174                    "+4.5%",
175                    "Meets growth projections",
176                    true,
177                ),
178            ])
179            .gap(tokens::SPACE_4),
180            row([chart_card(), sales_card()])
181                .gap(tokens::SPACE_4)
182                .height(Size::Fixed(306.0))
183                .align(Align::Stretch),
184            documents_card(),
185        ])
186        .gap(tokens::SPACE_4)
187        .padding(tokens::SPACE_7)
188        .height(Size::Fill(1.0)),
189    ])
190    .width(Size::Fill(1.0))
191    .height(Size::Fill(1.0))
192}
193
194fn dashboard_header() -> El {
195    row([
196        icon_button("menu").ghost(),
197        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
198        h3("Documents").key("metric:page.title"),
199        spacer(),
200        text_input("Search...", &Selection::default(), "dashboard-search")
201            .key("metric:command.input")
202            .width(Size::Fixed(260.0)),
203        icon_button("plus").ghost(),
204        icon_button("bell").ghost(),
205    ])
206    .key("metric:header")
207    .gap(tokens::SPACE_3)
208    .height(Size::Fixed(56.0))
209    .padding(Sides::xy(tokens::SPACE_4, 0.0))
210    .align(Align::Center)
211    .stroke(tokens::BORDER)
212}
213
214fn metric_card(
215    icon_name: &'static str,
216    title: &'static str,
217    value: &'static str,
218    delta: &'static str,
219    note: &'static str,
220    positive: bool,
221) -> El {
222    let badge = if positive {
223        badge(delta).success()
224    } else {
225        badge(delta).warning()
226    };
227    let badge = if title == "Total Revenue" {
228        badge.key("metric:kpi.badge")
229    } else {
230        badge
231    };
232    let value = if title == "Total Revenue" {
233        h2(value).ellipsis().key("metric:kpi.value")
234    } else {
235        h2(value).ellipsis()
236    };
237    card([card_content([
238        row([
239            row([
240                icon(icon_name)
241                    .color(tokens::MUTED_FOREGROUND)
242                    .icon_size(tokens::ICON_XS),
243                text(title).muted().ellipsis().width(Size::Fill(1.0)),
244            ])
245            .gap(tokens::SPACE_1)
246            .width(Size::Fill(1.0))
247            .align(Align::Center),
248            badge,
249        ])
250        .gap(tokens::SPACE_2)
251        .align(Align::Center),
252        value,
253        text(note).caption().ellipsis().width(Size::Fill(1.0)),
254    ])
255    .padding(tokens::SPACE_4)
256    .gap(tokens::SPACE_2)])
257    .key(if title == "Total Revenue" {
258        "metric:kpi.card"
259    } else {
260        title
261    })
262    .width(Size::Fill(1.0))
263}
264
265fn chart_card() -> El {
266    card([
267        card_header([
268            card_title("Visitors for the last 6 months"),
269            card_description("Total visitors by channel."),
270        ])
271        .padding(tokens::SPACE_4),
272        card_content([row(chart_bars())
273            .gap(2.0)
274            .height(Size::Fill(1.0))
275            .align(Align::End)])
276        .padding(Sides {
277            left: tokens::SPACE_4,
278            right: tokens::SPACE_4,
279            top: 0.0,
280            bottom: tokens::SPACE_4,
281        })
282        .height(Size::Fill(1.0)),
283    ])
284    .key("metric:chart.card")
285    .width(Size::Fill(1.0))
286    .height(Size::Fill(1.0))
287}
288
289fn chart_bars() -> Vec<El> {
290    [
291        48.0, 72.0, 56.0, 90.0, 64.0, 80.0, 108.0, 84.0, 122.0, 96.0, 136.0, 118.0,
292    ]
293    .into_iter()
294    .flat_map(|height| {
295        [
296            bar(height, tokens::MUTED_FOREGROUND),
297            bar((height - 28.0_f32).max(24.0), tokens::INPUT),
298        ]
299    })
300    .collect()
301}
302
303fn bar(height: f32, color: Color) -> El {
304    El::new(Kind::Custom("chart_bar"))
305        .fill(color)
306        .radius(tokens::RADIUS_SM)
307        .width(Size::Fill(1.0))
308        .height(Size::Fixed(height))
309}
310
311fn sales_card() -> El {
312    card([
313        card_header([
314            card_title("Recent Sales"),
315            card_description("You made 265 sales this month."),
316        ])
317        .padding(tokens::SPACE_4),
318        card_content([
319            sale_row("OM", "Olivia Martin", "olivia@example.com", "+$1,999.00"),
320            sale_row("JL", "Jackson Lee", "jackson@example.com", "+$39.00"),
321            sale_row("IN", "Isabella Nguyen", "isabella@example.com", "+$299.00"),
322            sale_row("WK", "William Kim", "will@example.com", "+$99.00"),
323        ])
324        .gap(tokens::SPACE_2)
325        .padding(Sides {
326            left: tokens::SPACE_4,
327            right: tokens::SPACE_4,
328            top: 0.0,
329            bottom: tokens::SPACE_4,
330        }),
331    ])
332    .key("metric:sales.card")
333    .width(Size::Fixed(330.0))
334    .height(Size::Fill(1.0))
335}
336
337fn sale_row(
338    initials: &'static str,
339    name: &'static str,
340    email: &'static str,
341    amount: &'static str,
342) -> El {
343    row([
344        icon_cell(initials),
345        column([
346            text(name).semibold().ellipsis().width(Size::Fill(1.0)),
347            text(email).caption().ellipsis().width(Size::Fill(1.0)),
348        ])
349        .gap(2.0)
350        .height(Size::Hug)
351        .width(Size::Fill(1.0)),
352        text(amount).label().small(),
353    ])
354    .gap(tokens::SPACE_2)
355    .height(Size::Fixed(42.0))
356    .align(Align::Center)
357}
358
359fn documents_card() -> El {
360    card([
361        card_header([card_title("Documents")]).padding(tokens::SPACE_4),
362        card_content([scroll([table([
363            table_header([table_row([
364                table_head("").width(Size::Fixed(35.0)),
365                table_head("Header").width(Size::Fill(1.8)),
366                table_head("Section Type").width(Size::Fill(1.0)),
367                table_head("Status").width(Size::Fixed(104.0)),
368                table_head("Target").width(Size::Fixed(64.0)),
369                table_head("Limit").width(Size::Fixed(64.0)),
370                table_head("Reviewer").width(Size::Fixed(128.0)),
371                table_head("").width(Size::Fixed(32.0)),
372            ])
373            .padding(Sides::xy(tokens::SPACE_4, 0.0))
374            .key("metric:table.header")]),
375            divider(),
376            table_body([
377                document_row(
378                    "Cover page",
379                    "Cover page",
380                    "In Process",
381                    "18",
382                    "5",
383                    "Eddie Lake",
384                    "info",
385                ),
386                document_row(
387                    "Table of contents",
388                    "Table of contents",
389                    "Done",
390                    "29",
391                    "24",
392                    "Eddie Lake",
393                    "success",
394                ),
395            ]),
396        ])])
397        .height(Size::Fill(1.0))])
398        .gap(0.0)
399        .padding(0.0)
400        .height(Size::Fill(1.0)),
401    ])
402    .key("metric:table.card")
403    .height(Size::Fill(1.0))
404}
examples/scrollbar.rs (line 27)
20fn list_rows() -> Vec<El> {
21    (0..40)
22        .map(|i| {
23            row([
24                text(format!("{i:02}.")).mono().muted(),
25                text(format!("scrollable list item {i}")),
26            ])
27            .gap(tokens::SPACE_2)
28            .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_1))
29            .height(Size::Fixed(28.0))
30            .align(Align::Center)
31        })
32        .collect()
33}
34
35fn fixture() -> El {
36    column([
37        h2("Scrollbar"),
38        text("scroll() and virtual_list() show a draggable thumb by default.").muted(),
39        row([
40            // 1) scroll() — default-on scrollbar.
41            column([
42                text("scroll() — default").bold(),
43                scroll(list_rows())
44                    .height(Size::Fixed(240.0))
45                    .padding(tokens::SPACE_2)
46                    .stroke(tokens::BORDER)
47                    .stroke_width(1.0)
48                    .radius(tokens::RADIUS_MD),
49            ])
50            .gap(tokens::SPACE_2)
51            .width(Size::Fill(1.0))
52            .height(Size::Hug),
53            // 2) virtual_list — thumb scales to content size.
54            column([
55                text("virtual_list(200, 28)").bold(),
56                virtual_list(200, 28.0, |i| {
57                    row([
58                        text(format!("{i:03}")).mono().muted(),
59                        text(format!("row {i}")),
60                    ])
61                    .gap(tokens::SPACE_2)
62                    .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_1))
63                    .height(Size::Fixed(28.0))
64                    .align(Align::Center)
65                })
66                .height(Size::Fixed(240.0))
67                .padding(tokens::SPACE_2)
68                .stroke(tokens::BORDER)
69                .stroke_width(1.0)
70                .radius(tokens::RADIUS_MD),
71            ])
72            .gap(tokens::SPACE_2)
73            .width(Size::Fill(1.0))
74            .height(Size::Hug),
75            // 3) Opt-out: same content, no thumb.
76            column([
77                text("scroll().no_scrollbar()").bold(),
78                scroll(list_rows())
79                    .no_scrollbar()
80                    .height(Size::Fixed(240.0))
81                    .padding(tokens::SPACE_2)
82                    .stroke(tokens::BORDER)
83                    .stroke_width(1.0)
84                    .radius(tokens::RADIUS_MD),
85            ])
86            .gap(tokens::SPACE_2)
87            .width(Size::Fill(1.0))
88            .height(Size::Hug),
89        ])
90        .gap(tokens::SPACE_4)
91        .width(Size::Fill(1.0)),
92    ])
93    .gap(tokens::SPACE_4)
94    .padding(tokens::SPACE_7)
95}
examples/custom_paint.rs (line 77)
68fn build_row(c: &FakeCommit, idx: usize, selected: bool) -> El {
69    row([
70        graph_cell(c.lane, selected),
71        text(c.sha).mono().muted(),
72        text(c.subject),
73        spacer(),
74        text(format!("{} · {}", c.author, c.when)).muted(),
75    ])
76    .key(format!("commit-{idx}"))
77    .gap(tokens::SPACE_3)
78    .padding(Sides::xy(tokens::SPACE_2, 0.0))
79    .height(Size::Fixed(ROW_HEIGHT))
80    .align(Align::Center)
81}
82
83fn fixture() -> El {
84    #[rustfmt::skip]
85    let commits = [
86        FakeCommit { sha: "8a3f1c9", subject: "fix race condition in scheduler", author: "ada",     when: "12m", lane: 0 },
87        FakeCommit { sha: "1b07d4e", subject: "tweak token tooltip wording",     author: "linus",   when: "1h",  lane: 0 },
88        FakeCommit { sha: "9f2e4a1", subject: "wire avatar fallback identicon",  author: "joelle",  when: "3h",  lane: 1 },
89        FakeCommit { sha: "44ab8d2", subject: "diff: word-level highlight pass", author: "raphael", when: "5h",  lane: 1 },
90        FakeCommit { sha: "61c0fe7", subject: "ci: bump rust toolchain to 1.85", author: "mei",     when: "7h",  lane: 2 },
91        FakeCommit { sha: "a90215b", subject: "switch logging to env_logger",    author: "isabel",  when: "1d",  lane: 2 },
92        FakeCommit { sha: "0d7e3c4", subject: "drop unused commit_detail cache", author: "noor",    when: "1d",  lane: 1 },
93        FakeCommit { sha: "33b2118", subject: "context-menu spacing pass",       author: "kira",    when: "2d",  lane: 3 },
94    ];
95    let selected_idx = 3;
96    let rows = commits
97        .iter()
98        .enumerate()
99        .map(|(i, c)| build_row(c, i, i == selected_idx))
100        .collect::<Vec<_>>();
101
102    column([
103        h2("Custom-painted commit graph"),
104        text("8 commits · custom shader paints lane line + circle node").muted(),
105        column(rows).gap(0.0),
106    ])
107    .padding(tokens::SPACE_4)
108    .gap(tokens::SPACE_2)
109}
examples/virtual_list.rs (line 34)
18fn build_row(i: usize) -> El {
19    let badge_el = match i % 5 {
20        0 => badge("info").muted(),
21        1 => badge("warn").warning(),
22        2 => badge("ok").success(),
23        3 => badge("err").destructive(),
24        _ => spacer(),
25    };
26    row([
27        text(format!("#{i:05}")).mono(),
28        spacer(),
29        text(format!("entry {i}")),
30        spacer(),
31        badge_el,
32    ])
33    .key(format!("row-{i}"))
34    .gap(tokens::SPACE_3)
35    .padding(Sides::xy(tokens::SPACE_3, tokens::SPACE_2))
36    .height(Size::Fixed(ROW_HEIGHT))
37}
38
39fn fixture() -> El {
40    column([
41        h1("Virtualized list"),
42        paragraph(format!(
43            "{ROW_COUNT} rows × {ROW_HEIGHT}px in a windowed viewport. \
44             Only the rows intersecting the viewport are realized — see \
45             `tree.txt` for proof."
46        ))
47        .muted(),
48        virtual_list(ROW_COUNT, ROW_HEIGHT, build_row)
49            .key("entries")
50            .height(Size::Fill(1.0)),
51    ])
52    .gap(tokens::SPACE_4)
53    .padding(tokens::SPACE_7)
54}
Source

pub fn align(self, a: Align) -> Self

Examples found in repository?
examples/polish_calibration.rs (line 40)
35fn polish_calibration() -> El {
36    row([sidebar(), main_panel()])
37        .key("metric:root")
38        .gap(0.0)
39        .fill_size()
40        .align(Align::Stretch)
41        .fill(tokens::BACKGROUND)
42}
43
44fn sidebar() -> El {
45    column([
46        column([h2("Aetna"), text("calibration").muted()])
47            .key("metric:sidebar.brand")
48            .gap(tokens::SPACE_1)
49            .height(Size::Hug),
50        spacer().height(Size::Fixed(tokens::SPACE_4)),
51        nav_item("01", "Overview", true),
52        nav_item("02", "Commands", false),
53        nav_item("03", "Tables", false),
54        nav_item("04", "Forms", false),
55        spacer(),
56        badge("dark theme").muted(),
57    ])
58    .gap(tokens::SPACE_2)
59    .padding(tokens::SPACE_5)
60    .key("metric:sidebar")
61    .width(Size::Fixed(220.0))
62    .height(Size::Fill(1.0))
63    .fill(tokens::CARD)
64    .stroke(tokens::BORDER)
65}
66
67fn nav_item(icon: &'static str, label: &'static str, selected: bool) -> El {
68    let mut item = row([
69        icon_cell(icon),
70        text(label)
71            .font_weight(FontWeight::Medium)
72            .ellipsis()
73            .width(Size::Fill(1.0)),
74    ])
75    .key(if selected {
76        "metric:sidebar.nav.row".to_string()
77    } else {
78        format!("nav-{label}")
79    })
80    .metrics_role(MetricsRole::ListItem)
81    .gap(tokens::SPACE_3)
82    .padding(Sides::xy(tokens::SPACE_2, 0.0))
83    .height(Size::Fixed(40.0))
84    .align(Align::Center)
85    .focusable();
86
87    if selected {
88        item = item.current();
89    }
90
91    item
92}
93
94fn main_panel() -> El {
95    column([
96        toolbar(),
97        column([
98            row([
99                kpi_card("Latency", "42 ms", "-18%", true),
100                kpi_card("Runs", "1,284", "+12%", true),
101                kpi_card("Errors", "7", "+2", false),
102            ])
103            .gap(tokens::SPACE_4),
104            row([table_card(), command_card()])
105                .gap(tokens::SPACE_4)
106                .height(Size::Fill(1.0))
107                .align(Align::Stretch),
108        ])
109        .gap(tokens::SPACE_4)
110        .height(Size::Fill(1.0))
111        .align(Align::Stretch),
112    ])
113    .padding(tokens::SPACE_7)
114    .gap(tokens::SPACE_2)
115    .width(Size::Fill(1.0))
116    .height(Size::Fill(1.0))
117}
118
119fn toolbar() -> El {
120    row([
121        column([
122            h1("Polish calibration").key("metric:page.title"),
123            text("A representative app surface for default tuning.")
124                .muted()
125                .key("metric:page.subtitle"),
126        ])
127        .gap(tokens::SPACE_2)
128        .height(Size::Hug),
129        spacer(),
130        button_with_icon("search", "Preview")
131            .secondary()
132            .key("metric:action.secondary"),
133        button_with_icon("upload", "Publish")
134            .primary()
135            .key("metric:action.primary"),
136    ])
137    .key("metric:header")
138    .gap(tokens::SPACE_4)
139    .height(Size::Hug)
140    .align(Align::Start)
141}
142
143fn kpi_card(label: &'static str, value: &'static str, delta: &'static str, positive: bool) -> El {
144    let delta_badge = if positive {
145        badge(delta).success()
146    } else {
147        badge(delta).destructive()
148    };
149    let delta_badge = if label == "Latency" {
150        delta_badge.key("metric:kpi.badge")
151    } else {
152        delta_badge
153    };
154    let value_text = h2(value).display();
155    let value_text = if label == "Latency" {
156        value_text.key("metric:kpi.value")
157    } else {
158        value_text
159    };
160    card([
161        card_header([card_title(label)]),
162        card_content([
163            row([value_text, spacer(), delta_badge]).align(Align::Center),
164            text(if positive {
165                "Moving in the expected direction"
166            } else {
167                "Needs visual attention"
168            })
169            .muted(),
170        ])
171        .gap(tokens::SPACE_6),
172    ])
173    .key(if label == "Latency" {
174        "metric:kpi.card"
175    } else {
176        label
177    })
178    .width(Size::Fill(1.0))
179}
More examples
Hide additional examples
examples/settings_calibration.rs (line 37)
32fn settings_calibration() -> El {
33    row([settings_sidebar(), settings_main()])
34        .key("metric:root")
35        .gap(0.0)
36        .fill_size()
37        .align(Align::Stretch)
38        .fill(tokens::BACKGROUND)
39}
40
41fn settings_sidebar() -> El {
42    column([
43        row([
44            icon_slot("settings"),
45            column([
46                text("Workspace")
47                    .semibold()
48                    .ellipsis()
49                    .width(Size::Fill(1.0)),
50                text("Settings").caption().ellipsis().width(Size::Fill(1.0)),
51            ])
52            .gap(2.0)
53            .width(Size::Fill(1.0))
54            .height(Size::Hug),
55        ])
56        .gap(tokens::SPACE_2)
57        .height(Size::Fixed(44.0))
58        .align(Align::Center),
59        section_label("Personal"),
60        side_item("users", "Profile", false),
61        side_item("settings", "Account", true),
62        side_item("alert-circle", "Security", false),
63        side_item("bell", "Notifications", false),
64        spacer().height(Size::Fixed(tokens::SPACE_4)),
65        section_label("Workspace"),
66        side_item("file-text", "Billing", false),
67        side_item("bar-chart", "Appearance", false),
68        side_item("activity", "Integrations", false),
69        spacer(),
70        column([text("Changes sync after save.").caption().wrap_text()])
71            .padding(tokens::SPACE_2)
72            .fill(tokens::MUTED)
73            .radius(tokens::RADIUS_MD),
74    ])
75    .gap(tokens::SPACE_2)
76    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_2))
77    .key("metric:sidebar")
78    .width(Size::Fixed(244.0))
79    .height(Size::Fill(1.0))
80    .fill(tokens::CARD)
81    .stroke(tokens::BORDER)
82}
83
84fn settings_main() -> El {
85    column([
86        settings_header(),
87        row([settings_nav_card(), settings_body(), settings_aside()])
88            .gap(tokens::SPACE_4)
89            .padding(tokens::SPACE_4)
90            .height(Size::Fill(1.0))
91            .align(Align::Stretch),
92    ])
93    .width(Size::Fill(1.0))
94    .height(Size::Fill(1.0))
95}
96
97fn settings_header() -> El {
98    row([
99        icon_button("menu").ghost(),
100        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
101        h3("Settings").key("metric:page.title"),
102        spacer(),
103        button("Reset").secondary(),
104        button("Save changes").primary(),
105    ])
106    .key("metric:header")
107    .gap(tokens::SPACE_3)
108    .height(Size::Fixed(56.0))
109    .padding(Sides::xy(tokens::SPACE_4, 0.0))
110    .align(Align::Center)
111    .stroke(tokens::BORDER)
112}
113
114fn settings_nav_card() -> El {
115    column([
116        settings_nav_item("Account", true),
117        settings_nav_item("Security", false),
118        settings_nav_item("Notifications", false),
119        settings_nav_item("Appearance", false),
120        settings_nav_item("Billing", false),
121    ])
122    .gap(tokens::SPACE_1)
123    .padding(tokens::SPACE_1)
124    .width(Size::Fixed(220.0))
125    .height(Size::Fill(1.0))
126    .style_profile(StyleProfile::Surface)
127    .surface_role(SurfaceRole::Panel)
128    .fill(tokens::CARD)
129    .stroke(tokens::BORDER)
130    .radius(tokens::RADIUS_MD)
131    .shadow(tokens::SHADOW_MD)
132}
133
134fn settings_nav_item(label: &'static str, selected: bool) -> El {
135    let mut item = row([
136        El::new(Kind::Custom("nav-dot"))
137            .fill(tokens::MUTED_FOREGROUND)
138            .radius(tokens::RADIUS_PILL)
139            .width(Size::Fixed(6.0))
140            .height(Size::Fixed(6.0)),
141        text(label)
142            .font_weight(FontWeight::Medium)
143            .ellipsis()
144            .width(Size::Fill(1.0)),
145    ])
146    .key(if selected {
147        "metric:settings.nav.row".to_string()
148    } else {
149        format!("settings-nav-{label}")
150    })
151    .metrics_role(MetricsRole::ListItem)
152    .align(Align::Center)
153    .focusable();
154
155    if selected {
156        item = item.current();
157    } else {
158        item = item.color(tokens::MUTED_FOREGROUND);
159    }
160
161    item
162}
163
164fn settings_body() -> El {
165    column([
166        column([
167            h1("Account").heading().key("metric:section.title"),
168            text("Manage identity, workspace defaults, and security preferences.")
169                .muted()
170                .wrap_text()
171                .key("metric:page.subtitle"),
172        ])
173        .gap(tokens::SPACE_1)
174        .height(Size::Hug),
175        scroll([profile_card(), preferences_card()])
176            .key("settings-body-scroll")
177            .gap(tokens::SPACE_4)
178            .width(Size::Fill(1.0))
179            .height(Size::Fill(1.0)),
180    ])
181    .gap(tokens::SPACE_4)
182    .width(Size::Fill(1.0))
183    .height(Size::Fill(1.0))
184}
185
186fn profile_card() -> El {
187    card([
188        card_header([
189            card_title("Profile"),
190            card_description("This information appears in audit logs and shared documents."),
191        ]),
192        card_content([form([
193            row([
194                setting_field("Display name", "Alicia Koch", "display-name"),
195                setting_field("Email", "alicia@acme.co", "email"),
196            ])
197            .gap(tokens::SPACE_3),
198            row([
199                setting_select("Role", "Workspace admin", "role"),
200                setting_select("Region", "US East", "region"),
201            ])
202            .gap(tokens::SPACE_3),
203        ])]),
204    ])
205    .key("metric:profile.card")
206}
207
208fn setting_field(label: &'static str, value: &'static str, key: &'static str) -> El {
209    form_item([
210        form_label(label),
211        form_control(
212            text_input(value, &Selection::caret(key, value.len()), key).key(
213                if key == "display-name" {
214                    "metric:form.input"
215                } else {
216                    key
217                },
218            ),
219        ),
220    ])
221    .width(Size::Fill(1.0))
222}
223
224fn setting_select(label: &'static str, value: &'static str, key: &'static str) -> El {
225    form_item([form_label(label), form_control(select_trigger(key, value))]).width(Size::Fill(1.0))
226}
227
228fn preferences_card() -> El {
229    card([
230        card_header([
231            card_title("Preferences"),
232            card_description("Defaults used when creating new dashboards and exports."),
233        ]),
234        card_content([column([
235            preference_row(
236                "Compact navigation",
237                "Use tighter rows in the sidebar and command menus.",
238                switch(true).key("compact-navigation"),
239            ),
240            divider(),
241            preference_row(
242                "Email summaries",
243                "Send a daily digest when documents change.",
244                switch(false).key("email-summaries"),
245            ),
246            divider(),
247            preference_row(
248                "Require approval",
249                "Route external sharing through an owner review.",
250                checkbox(true).key("approval-required"),
251            ),
252        ])
253        .gap(0.0)
254        .width(Size::Fill(1.0))])
255        .padding(0.0),
256    ])
257    .key("metric:preferences.card")
258}
259
260fn preference_row(title: &'static str, description: &'static str, control: El) -> El {
261    row([
262        column([
263            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
264            text(description)
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0))
271        .height(Size::Hug),
272        control,
273    ])
274    .key(if title == "Compact navigation" {
275        "metric:preference.row".to_string()
276    } else {
277        format!("preference-{title}")
278    })
279    .metrics_role(MetricsRole::PreferenceRow)
280    .gap(tokens::SPACE_4)
281    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_3))
282    .align(Align::Center)
283}
284
285fn settings_aside() -> El {
286    column([security_card(), scale_card()])
287        .gap(tokens::SPACE_4)
288        .width(Size::Fixed(300.0))
289        .height(Size::Fill(1.0))
290}
291
292fn security_card() -> El {
293    card([
294        card_header([
295            card_title("Security"),
296            card_description("Two-factor authentication is enabled for all privileged users."),
297        ]),
298        card_content([
299            compact_stat("Passkeys", "2 registered", badge("On").success()),
300            compact_stat("Sessions", "3 active", button("Review").secondary()),
301        ]),
302    ])
303    .width(Size::Fill(1.0))
304}
305
306fn scale_card() -> El {
307    card([
308        card_header([
309            card_title("Interface scale"),
310            card_description("Reference captures keep browser zoom fixed and vary root UI scale."),
311        ]),
312        card_content([
313            row([text("Dense").caption(), spacer(), text("Default").caption()]),
314            slider(0.66, tokens::PRIMARY)
315                .key("interface-scale")
316                .width(Size::Fill(1.0)),
317        ]),
318    ])
319    .width(Size::Fill(1.0))
320}
321
322fn compact_stat(title: &'static str, detail: &'static str, control: El) -> El {
323    row([
324        column([
325            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
326            text(detail).caption().ellipsis().width(Size::Fill(1.0)),
327        ])
328        .gap(2.0)
329        .width(Size::Fill(1.0))
330        .height(Size::Hug),
331        control,
332    ])
333    .gap(tokens::SPACE_2)
334    .height(Size::Fixed(44.0))
335    .align(Align::Center)
336}
337
338fn section_label(label: &'static str) -> El {
339    text(label)
340        .caption()
341        .height(Size::Fixed(22.0))
342        .padding(Sides::xy(tokens::SPACE_2, 0.0))
343}
344
345fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
346    let mut item = row([
347        icon(icon_name)
348            .color(tokens::MUTED_FOREGROUND)
349            .icon_size(tokens::ICON_SM)
350            .width(Size::Fixed(tokens::ICON_SM)),
351        text(label)
352            .font_weight(FontWeight::Medium)
353            .ellipsis()
354            .width(Size::Fill(1.0)),
355    ])
356    .key(if selected {
357        "metric:sidebar.nav.row".to_string()
358    } else {
359        format!("side-item-{label}")
360    })
361    .metrics_role(MetricsRole::ListItem)
362    .gap(tokens::SPACE_2)
363    .padding(Sides::xy(tokens::SPACE_2, 0.0))
364    .height(Size::Fixed(32.0))
365    .align(Align::Center)
366    .focusable();
367
368    if selected {
369        item = item.current();
370    } else {
371        item = item.color(tokens::MUTED_FOREGROUND);
372    }
373
374    item
375}
376
377fn icon_slot(icon_name: &'static str) -> El {
378    El::new(Kind::Custom("icon_cell"))
379        .style_profile(StyleProfile::Surface)
380        .child(
381            icon(icon_name)
382                .color(tokens::FOREGROUND)
383                .icon_size(tokens::ICON_XS),
384        )
385        .align(Align::Center)
386        .justify(Justify::Center)
387        .fill(tokens::MUTED)
388        .stroke(tokens::BORDER)
389        .radius(tokens::RADIUS_SM)
390        .width(Size::Fixed(30.0))
391        .height(Size::Fixed(30.0))
392}
examples/dashboard_01_calibration.rs (line 37)
32fn dashboard_01_calibration() -> El {
33    row([dashboard_sidebar(), dashboard_main()])
34        .key("metric:root")
35        .gap(0.0)
36        .fill_size()
37        .align(Align::Stretch)
38        .fill(tokens::BACKGROUND)
39}
40
41fn dashboard_sidebar() -> El {
42    column([
43        row([
44            icon_cell("A"),
45            column([
46                text("Acme Inc.")
47                    .semibold()
48                    .ellipsis()
49                    .width(Size::Fill(1.0)),
50                text("Enterprise")
51                    .caption()
52                    .ellipsis()
53                    .width(Size::Fill(1.0)),
54            ])
55            .gap(2.0)
56            .width(Size::Fill(1.0))
57            .height(Size::Hug),
58        ])
59        .gap(tokens::SPACE_2)
60        .height(Size::Fixed(44.0))
61        .align(Align::Center),
62        section_label("Platform"),
63        side_item("layout-dashboard", "Dashboard", true),
64        side_item("activity", "Lifecycle", false),
65        side_item("bar-chart", "Analytics", false),
66        side_item("folder", "Projects", false),
67        spacer().height(Size::Fixed(tokens::SPACE_4)),
68        section_label("Documents"),
69        side_item("file-text", "Data library", false),
70        side_item("download", "Reports", false),
71        side_item("users", "Team", false),
72        spacer(),
73        row([
74            icon_cell("AK"),
75            column([
76                text("Alicia Koch")
77                    .semibold()
78                    .ellipsis()
79                    .width(Size::Fill(1.0)),
80                text("alicia@example.com")
81                    .caption()
82                    .ellipsis()
83                    .width(Size::Fill(1.0)),
84            ])
85            .gap(2.0)
86            .width(Size::Fill(1.0))
87            .height(Size::Hug),
88        ])
89        .gap(tokens::SPACE_2)
90        .height(Size::Fixed(50.0))
91        .align(Align::Center),
92    ])
93    .gap(tokens::SPACE_2)
94    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_2))
95    .key("metric:sidebar")
96    .width(Size::Fixed(244.0))
97    .height(Size::Fill(1.0))
98    .fill(tokens::CARD)
99    .stroke(tokens::BORDER)
100}
101
102fn section_label(label: &'static str) -> El {
103    text(label)
104        .caption()
105        .height(Size::Fixed(22.0))
106        .padding(Sides::xy(tokens::SPACE_2, 0.0))
107}
108
109fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
110    let mut item = row([
111        icon(icon_name)
112            .color(tokens::MUTED_FOREGROUND)
113            .icon_size(tokens::ICON_SM)
114            .width(Size::Fixed(tokens::ICON_SM)),
115        text(label)
116            .font_weight(FontWeight::Medium)
117            .ellipsis()
118            .width(Size::Fill(1.0)),
119    ])
120    .key(if selected {
121        "metric:sidebar.nav.row".to_string()
122    } else {
123        format!("side-item-{label}")
124    })
125    .metrics_role(MetricsRole::ListItem)
126    .gap(tokens::SPACE_2)
127    .padding(Sides::xy(tokens::SPACE_2, 0.0))
128    .height(Size::Fixed(32.0))
129    .align(Align::Center)
130    .focusable();
131
132    if selected {
133        item = item.current();
134    } else {
135        item = item.color(tokens::MUTED_FOREGROUND);
136    }
137
138    item
139}
140
141fn dashboard_main() -> El {
142    column([
143        dashboard_header(),
144        column([
145            row([
146                metric_card(
147                    "bar-chart",
148                    "Total Revenue",
149                    "$1,250.00",
150                    "+12.5%",
151                    "Trending up this month",
152                    true,
153                ),
154                metric_card(
155                    "users",
156                    "New Customers",
157                    "1,234",
158                    "-20%",
159                    "Acquisition needs attention",
160                    false,
161                ),
162                metric_card(
163                    "folder",
164                    "Active Accounts",
165                    "45,678",
166                    "+12.5%",
167                    "Strong user retention",
168                    true,
169                ),
170                metric_card(
171                    "activity",
172                    "Growth Rate",
173                    "4.5%",
174                    "+4.5%",
175                    "Meets growth projections",
176                    true,
177                ),
178            ])
179            .gap(tokens::SPACE_4),
180            row([chart_card(), sales_card()])
181                .gap(tokens::SPACE_4)
182                .height(Size::Fixed(306.0))
183                .align(Align::Stretch),
184            documents_card(),
185        ])
186        .gap(tokens::SPACE_4)
187        .padding(tokens::SPACE_7)
188        .height(Size::Fill(1.0)),
189    ])
190    .width(Size::Fill(1.0))
191    .height(Size::Fill(1.0))
192}
193
194fn dashboard_header() -> El {
195    row([
196        icon_button("menu").ghost(),
197        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
198        h3("Documents").key("metric:page.title"),
199        spacer(),
200        text_input("Search...", &Selection::default(), "dashboard-search")
201            .key("metric:command.input")
202            .width(Size::Fixed(260.0)),
203        icon_button("plus").ghost(),
204        icon_button("bell").ghost(),
205    ])
206    .key("metric:header")
207    .gap(tokens::SPACE_3)
208    .height(Size::Fixed(56.0))
209    .padding(Sides::xy(tokens::SPACE_4, 0.0))
210    .align(Align::Center)
211    .stroke(tokens::BORDER)
212}
213
214fn metric_card(
215    icon_name: &'static str,
216    title: &'static str,
217    value: &'static str,
218    delta: &'static str,
219    note: &'static str,
220    positive: bool,
221) -> El {
222    let badge = if positive {
223        badge(delta).success()
224    } else {
225        badge(delta).warning()
226    };
227    let badge = if title == "Total Revenue" {
228        badge.key("metric:kpi.badge")
229    } else {
230        badge
231    };
232    let value = if title == "Total Revenue" {
233        h2(value).ellipsis().key("metric:kpi.value")
234    } else {
235        h2(value).ellipsis()
236    };
237    card([card_content([
238        row([
239            row([
240                icon(icon_name)
241                    .color(tokens::MUTED_FOREGROUND)
242                    .icon_size(tokens::ICON_XS),
243                text(title).muted().ellipsis().width(Size::Fill(1.0)),
244            ])
245            .gap(tokens::SPACE_1)
246            .width(Size::Fill(1.0))
247            .align(Align::Center),
248            badge,
249        ])
250        .gap(tokens::SPACE_2)
251        .align(Align::Center),
252        value,
253        text(note).caption().ellipsis().width(Size::Fill(1.0)),
254    ])
255    .padding(tokens::SPACE_4)
256    .gap(tokens::SPACE_2)])
257    .key(if title == "Total Revenue" {
258        "metric:kpi.card"
259    } else {
260        title
261    })
262    .width(Size::Fill(1.0))
263}
264
265fn chart_card() -> El {
266    card([
267        card_header([
268            card_title("Visitors for the last 6 months"),
269            card_description("Total visitors by channel."),
270        ])
271        .padding(tokens::SPACE_4),
272        card_content([row(chart_bars())
273            .gap(2.0)
274            .height(Size::Fill(1.0))
275            .align(Align::End)])
276        .padding(Sides {
277            left: tokens::SPACE_4,
278            right: tokens::SPACE_4,
279            top: 0.0,
280            bottom: tokens::SPACE_4,
281        })
282        .height(Size::Fill(1.0)),
283    ])
284    .key("metric:chart.card")
285    .width(Size::Fill(1.0))
286    .height(Size::Fill(1.0))
287}
288
289fn chart_bars() -> Vec<El> {
290    [
291        48.0, 72.0, 56.0, 90.0, 64.0, 80.0, 108.0, 84.0, 122.0, 96.0, 136.0, 118.0,
292    ]
293    .into_iter()
294    .flat_map(|height| {
295        [
296            bar(height, tokens::MUTED_FOREGROUND),
297            bar((height - 28.0_f32).max(24.0), tokens::INPUT),
298        ]
299    })
300    .collect()
301}
302
303fn bar(height: f32, color: Color) -> El {
304    El::new(Kind::Custom("chart_bar"))
305        .fill(color)
306        .radius(tokens::RADIUS_SM)
307        .width(Size::Fill(1.0))
308        .height(Size::Fixed(height))
309}
310
311fn sales_card() -> El {
312    card([
313        card_header([
314            card_title("Recent Sales"),
315            card_description("You made 265 sales this month."),
316        ])
317        .padding(tokens::SPACE_4),
318        card_content([
319            sale_row("OM", "Olivia Martin", "olivia@example.com", "+$1,999.00"),
320            sale_row("JL", "Jackson Lee", "jackson@example.com", "+$39.00"),
321            sale_row("IN", "Isabella Nguyen", "isabella@example.com", "+$299.00"),
322            sale_row("WK", "William Kim", "will@example.com", "+$99.00"),
323        ])
324        .gap(tokens::SPACE_2)
325        .padding(Sides {
326            left: tokens::SPACE_4,
327            right: tokens::SPACE_4,
328            top: 0.0,
329            bottom: tokens::SPACE_4,
330        }),
331    ])
332    .key("metric:sales.card")
333    .width(Size::Fixed(330.0))
334    .height(Size::Fill(1.0))
335}
336
337fn sale_row(
338    initials: &'static str,
339    name: &'static str,
340    email: &'static str,
341    amount: &'static str,
342) -> El {
343    row([
344        icon_cell(initials),
345        column([
346            text(name).semibold().ellipsis().width(Size::Fill(1.0)),
347            text(email).caption().ellipsis().width(Size::Fill(1.0)),
348        ])
349        .gap(2.0)
350        .height(Size::Hug)
351        .width(Size::Fill(1.0)),
352        text(amount).label().small(),
353    ])
354    .gap(tokens::SPACE_2)
355    .height(Size::Fixed(42.0))
356    .align(Align::Center)
357}
358
359fn documents_card() -> El {
360    card([
361        card_header([card_title("Documents")]).padding(tokens::SPACE_4),
362        card_content([scroll([table([
363            table_header([table_row([
364                table_head("").width(Size::Fixed(35.0)),
365                table_head("Header").width(Size::Fill(1.8)),
366                table_head("Section Type").width(Size::Fill(1.0)),
367                table_head("Status").width(Size::Fixed(104.0)),
368                table_head("Target").width(Size::Fixed(64.0)),
369                table_head("Limit").width(Size::Fixed(64.0)),
370                table_head("Reviewer").width(Size::Fixed(128.0)),
371                table_head("").width(Size::Fixed(32.0)),
372            ])
373            .padding(Sides::xy(tokens::SPACE_4, 0.0))
374            .key("metric:table.header")]),
375            divider(),
376            table_body([
377                document_row(
378                    "Cover page",
379                    "Cover page",
380                    "In Process",
381                    "18",
382                    "5",
383                    "Eddie Lake",
384                    "info",
385                ),
386                document_row(
387                    "Table of contents",
388                    "Table of contents",
389                    "Done",
390                    "29",
391                    "24",
392                    "Eddie Lake",
393                    "success",
394                ),
395            ]),
396        ])])
397        .height(Size::Fill(1.0))])
398        .gap(0.0)
399        .padding(0.0)
400        .height(Size::Fill(1.0)),
401    ])
402    .key("metric:table.card")
403    .height(Size::Fill(1.0))
404}
405
406fn document_row(
407    header: &'static str,
408    section: &'static str,
409    status: &'static str,
410    target: &'static str,
411    limit: &'static str,
412    reviewer: &'static str,
413    tone: &'static str,
414) -> El {
415    let status_badge = match tone {
416        "success" => badge(status).success(),
417        _ => badge(status).info(),
418    };
419    table_row([
420        table_utility_cell("::"),
421        table_cell(text(header).label().small()).width(Size::Fill(1.8)),
422        table_cell(text(section).muted()).width(Size::Fill(1.0)),
423        table_cell(status_badge).width(Size::Fixed(104.0)),
424        table_cell(text(target).label().small()).width(Size::Fixed(64.0)),
425        table_cell(text(limit).label().small()).width(Size::Fixed(64.0)),
426        table_cell(text(reviewer).muted()).width(Size::Fixed(128.0)),
427        table_action_cell(),
428    ])
429    .padding(Sides::xy(tokens::SPACE_4, 0.0))
430    .key(if header == "Cover page" {
431        "metric:table.row"
432    } else {
433        header
434    })
435}
436
437fn table_utility_cell(label: &'static str) -> El {
438    table_cell(text(label).muted().center_text()).width(Size::Fixed(35.0))
439}
440
441fn table_action_cell() -> El {
442    stack([icon("more-horizontal")
443        .icon_size(tokens::ICON_SM)
444        .color(tokens::MUTED_FOREGROUND)])
445    .align(Align::Center)
446    .justify(Justify::Center)
447    .width(Size::Fixed(32.0))
448    .height(Size::Hug)
449}
examples/scrollbar.rs (line 30)
20fn list_rows() -> Vec<El> {
21    (0..40)
22        .map(|i| {
23            row([
24                text(format!("{i:02}.")).mono().muted(),
25                text(format!("scrollable list item {i}")),
26            ])
27            .gap(tokens::SPACE_2)
28            .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_1))
29            .height(Size::Fixed(28.0))
30            .align(Align::Center)
31        })
32        .collect()
33}
34
35fn fixture() -> El {
36    column([
37        h2("Scrollbar"),
38        text("scroll() and virtual_list() show a draggable thumb by default.").muted(),
39        row([
40            // 1) scroll() — default-on scrollbar.
41            column([
42                text("scroll() — default").bold(),
43                scroll(list_rows())
44                    .height(Size::Fixed(240.0))
45                    .padding(tokens::SPACE_2)
46                    .stroke(tokens::BORDER)
47                    .stroke_width(1.0)
48                    .radius(tokens::RADIUS_MD),
49            ])
50            .gap(tokens::SPACE_2)
51            .width(Size::Fill(1.0))
52            .height(Size::Hug),
53            // 2) virtual_list — thumb scales to content size.
54            column([
55                text("virtual_list(200, 28)").bold(),
56                virtual_list(200, 28.0, |i| {
57                    row([
58                        text(format!("{i:03}")).mono().muted(),
59                        text(format!("row {i}")),
60                    ])
61                    .gap(tokens::SPACE_2)
62                    .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_1))
63                    .height(Size::Fixed(28.0))
64                    .align(Align::Center)
65                })
66                .height(Size::Fixed(240.0))
67                .padding(tokens::SPACE_2)
68                .stroke(tokens::BORDER)
69                .stroke_width(1.0)
70                .radius(tokens::RADIUS_MD),
71            ])
72            .gap(tokens::SPACE_2)
73            .width(Size::Fill(1.0))
74            .height(Size::Hug),
75            // 3) Opt-out: same content, no thumb.
76            column([
77                text("scroll().no_scrollbar()").bold(),
78                scroll(list_rows())
79                    .no_scrollbar()
80                    .height(Size::Fixed(240.0))
81                    .padding(tokens::SPACE_2)
82                    .stroke(tokens::BORDER)
83                    .stroke_width(1.0)
84                    .radius(tokens::RADIUS_MD),
85            ])
86            .gap(tokens::SPACE_2)
87            .width(Size::Fill(1.0))
88            .height(Size::Hug),
89        ])
90        .gap(tokens::SPACE_4)
91        .width(Size::Fill(1.0)),
92    ])
93    .gap(tokens::SPACE_4)
94    .padding(tokens::SPACE_7)
95}
examples/custom_paint.rs (line 80)
68fn build_row(c: &FakeCommit, idx: usize, selected: bool) -> El {
69    row([
70        graph_cell(c.lane, selected),
71        text(c.sha).mono().muted(),
72        text(c.subject),
73        spacer(),
74        text(format!("{} · {}", c.author, c.when)).muted(),
75    ])
76    .key(format!("commit-{idx}"))
77    .gap(tokens::SPACE_3)
78    .padding(Sides::xy(tokens::SPACE_2, 0.0))
79    .height(Size::Fixed(ROW_HEIGHT))
80    .align(Align::Center)
81}
examples/palette_demo.rs (line 89)
77fn palette_demo(label: &'static str, palette: &Palette) -> El {
78    column([
79        row([
80            column([
81                h1("Palette demo"),
82                text("Aetna and Radix palettes rendered through Aetna tokens.").muted(),
83            ])
84            .gap(tokens::SPACE_2)
85            .height(Size::Hug),
86            spacer(),
87            badge(label).muted(),
88        ])
89        .align(Align::Start)
90        .height(Size::Hug),
91        row([
92            token_section(
93                "Core tokens",
94                "The shadcn-shaped semantic vocabulary.",
95                &CORE_TOKENS,
96                palette,
97            )
98            .width(Size::Fill(1.25)),
99            column([
100                token_section(
101                    "Aetna extensions",
102                    "Component and status tokens layered on top.",
103                    &EXTENSION_TOKENS,
104                    palette,
105                ),
106                component_section(),
107            ])
108            .gap(tokens::SPACE_4)
109            .width(Size::Fill(1.0))
110            .height(Size::Fill(1.0)),
111        ])
112        .gap(tokens::SPACE_4)
113        .align(Align::Stretch)
114        .height(Size::Fill(1.0)),
115    ])
116    .padding(tokens::SPACE_8)
117    .gap(tokens::SPACE_6)
118    .fill_size()
119    .fill(tokens::BACKGROUND)
120}
121
122const CORE_TOKENS: [TokenDef; 19] = [
123    TokenDef {
124        name: "background",
125        color: tokens::BACKGROUND,
126    },
127    TokenDef {
128        name: "foreground",
129        color: tokens::FOREGROUND,
130    },
131    TokenDef {
132        name: "card",
133        color: tokens::CARD,
134    },
135    TokenDef {
136        name: "card-foreground",
137        color: tokens::CARD_FOREGROUND,
138    },
139    TokenDef {
140        name: "popover",
141        color: tokens::POPOVER,
142    },
143    TokenDef {
144        name: "popover-foreground",
145        color: tokens::POPOVER_FOREGROUND,
146    },
147    TokenDef {
148        name: "primary",
149        color: tokens::PRIMARY,
150    },
151    TokenDef {
152        name: "primary-foreground",
153        color: tokens::PRIMARY_FOREGROUND,
154    },
155    TokenDef {
156        name: "secondary",
157        color: tokens::SECONDARY,
158    },
159    TokenDef {
160        name: "secondary-foreground",
161        color: tokens::SECONDARY_FOREGROUND,
162    },
163    TokenDef {
164        name: "muted",
165        color: tokens::MUTED,
166    },
167    TokenDef {
168        name: "muted-foreground",
169        color: tokens::MUTED_FOREGROUND,
170    },
171    TokenDef {
172        name: "accent",
173        color: tokens::ACCENT,
174    },
175    TokenDef {
176        name: "accent-foreground",
177        color: tokens::ACCENT_FOREGROUND,
178    },
179    TokenDef {
180        name: "destructive",
181        color: tokens::DESTRUCTIVE,
182    },
183    TokenDef {
184        name: "destructive-foreground",
185        color: tokens::DESTRUCTIVE_FOREGROUND,
186    },
187    TokenDef {
188        name: "border",
189        color: tokens::BORDER,
190    },
191    TokenDef {
192        name: "input",
193        color: tokens::INPUT,
194    },
195    TokenDef {
196        name: "ring",
197        color: tokens::RING,
198    },
199];
200
201const EXTENSION_TOKENS: [TokenDef; 12] = [
202    TokenDef {
203        name: "success",
204        color: tokens::SUCCESS,
205    },
206    TokenDef {
207        name: "success-foreground",
208        color: tokens::SUCCESS_FOREGROUND,
209    },
210    TokenDef {
211        name: "warning",
212        color: tokens::WARNING,
213    },
214    TokenDef {
215        name: "warning-foreground",
216        color: tokens::WARNING_FOREGROUND,
217    },
218    TokenDef {
219        name: "info",
220        color: tokens::INFO,
221    },
222    TokenDef {
223        name: "info-foreground",
224        color: tokens::INFO_FOREGROUND,
225    },
226    TokenDef {
227        name: "link-foreground",
228        color: tokens::LINK_FOREGROUND,
229    },
230    TokenDef {
231        name: "overlay-scrim",
232        color: tokens::OVERLAY_SCRIM,
233    },
234    TokenDef {
235        name: "scrollbar-thumb",
236        color: tokens::SCROLLBAR_THUMB_FILL,
237    },
238    TokenDef {
239        name: "scrollbar-thumb-active",
240        color: tokens::SCROLLBAR_THUMB_FILL_ACTIVE,
241    },
242    TokenDef {
243        name: "selection-bg",
244        color: tokens::SELECTION_BG,
245    },
246    TokenDef {
247        name: "selection-bg-unfocused",
248        color: tokens::SELECTION_BG_UNFOCUSED,
249    },
250];
251
252fn token_section(
253    title: &'static str,
254    description: &'static str,
255    tokens: &'static [TokenDef],
256    palette: &Palette,
257) -> El {
258    card([
259        card_header([card_title(title), card_description(description)]),
260        card_content([swatch_grid(tokens, palette)]),
261    ])
262    .height(Size::Fill(1.0))
263}
264
265fn swatch_grid(defs: &'static [TokenDef], palette: &Palette) -> El {
266    let rows = defs
267        .chunks(2)
268        .map(|chunk| {
269            row(chunk.iter().map(|token| token_chip(*token, palette)))
270                .gap(tokens::SPACE_3)
271                .align(Align::Center)
272                .width(Size::Fill(1.0))
273        })
274        .collect::<Vec<_>>();
275
276    column(rows)
277        .gap(tokens::SPACE_3)
278        .width(Size::Fill(1.0))
279        .height(Size::Hug)
280}
281
282fn token_chip(token: TokenDef, palette: &Palette) -> El {
283    let resolved = palette.resolve(token.color);
284    row([
285        El::new(Kind::Custom("palette-swatch"))
286            .at(file!(), line!())
287            .fill(token.color)
288            .stroke(tokens::BORDER)
289            .radius(tokens::RADIUS_SM)
290            .width(Size::Fixed(42.0))
291            .height(Size::Fixed(34.0)),
292        column([
293            text(token.name)
294                .label()
295                .ellipsis()
296                .nowrap_text()
297                .width(Size::Fill(1.0)),
298            mono(rgba_label(resolved)).caption().muted(),
299        ])
300        .gap(0.0)
301        .width(Size::Fill(1.0))
302        .height(Size::Hug),
303    ])
304    .gap(tokens::SPACE_2)
305    .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_2))
306    .align(Align::Center)
307    .fill(tokens::CARD)
308    .stroke(tokens::BORDER)
309    .radius(tokens::RADIUS_MD)
310    .width(Size::Fill(1.0))
311    .height(Size::Fixed(54.0))
312}
313
314fn component_section() -> El {
315    card([
316        card_header([
317            card_title("Stock widgets"),
318            card_description("The same palette applied to regular component constructors."),
319        ]),
320        card_content([
321            row([
322                button("Primary").primary(),
323                button("Secondary").secondary(),
324                button("Outline").outline(),
325                button("Ghost").ghost(),
326            ])
327            .gap(tokens::SPACE_2)
328            .align(Align::Center),
329            row([
330                badge("success").success(),
331                badge("warning").warning(),
332                badge("destructive").destructive(),
333                badge("info").info(),
334                badge("muted").muted(),
335            ])
336            .gap(tokens::SPACE_2)
337            .align(Align::Center),
338            row([
339                text_input("palette search", &Selection::default(), "palette:search")
340                    .width(Size::Fill(1.0)),
341                button_with_icon("settings", "Tune").secondary(),
342            ])
343            .gap(tokens::SPACE_2)
344            .align(Align::Center),
345            row([
346                surface_sample("Card", tokens::CARD),
347                surface_sample("Muted", tokens::MUTED),
348                surface_sample("Popover", tokens::POPOVER),
349            ])
350            .gap(tokens::SPACE_3)
351            .align(Align::Stretch),
352        ])
353        .gap(tokens::SPACE_4),
354    ])
355    .height(Size::Hug)
356}
Source

pub fn justify(self, j: Justify) -> Self

Examples found in repository?
examples/dashboard_01_calibration.rs (line 446)
441fn table_action_cell() -> El {
442    stack([icon("more-horizontal")
443        .icon_size(tokens::ICON_SM)
444        .color(tokens::MUTED_FOREGROUND)])
445    .align(Align::Center)
446    .justify(Justify::Center)
447    .width(Size::Fixed(32.0))
448    .height(Size::Hug)
449}
More examples
Hide additional examples
examples/settings_calibration.rs (line 386)
377fn icon_slot(icon_name: &'static str) -> El {
378    El::new(Kind::Custom("icon_cell"))
379        .style_profile(StyleProfile::Surface)
380        .child(
381            icon(icon_name)
382                .color(tokens::FOREGROUND)
383                .icon_size(tokens::ICON_XS),
384        )
385        .align(Align::Center)
386        .justify(Justify::Center)
387        .fill(tokens::MUTED)
388        .stroke(tokens::BORDER)
389        .radius(tokens::RADIUS_SM)
390        .width(Size::Fixed(30.0))
391        .height(Size::Fixed(30.0))
392}
Source

pub fn clip(self) -> Self

Source

pub fn scrollable(self) -> Self

Source

pub fn scrollbar(self) -> Self

Show a draggable vertical scrollbar thumb when this scrollable node’s content overflows.

Source

pub fn no_scrollbar(self) -> Self

Suppress the default scrollbar thumb on this scrollable node.

Examples found in repository?
examples/scrollbar.rs (line 79)
35fn fixture() -> El {
36    column([
37        h2("Scrollbar"),
38        text("scroll() and virtual_list() show a draggable thumb by default.").muted(),
39        row([
40            // 1) scroll() — default-on scrollbar.
41            column([
42                text("scroll() — default").bold(),
43                scroll(list_rows())
44                    .height(Size::Fixed(240.0))
45                    .padding(tokens::SPACE_2)
46                    .stroke(tokens::BORDER)
47                    .stroke_width(1.0)
48                    .radius(tokens::RADIUS_MD),
49            ])
50            .gap(tokens::SPACE_2)
51            .width(Size::Fill(1.0))
52            .height(Size::Hug),
53            // 2) virtual_list — thumb scales to content size.
54            column([
55                text("virtual_list(200, 28)").bold(),
56                virtual_list(200, 28.0, |i| {
57                    row([
58                        text(format!("{i:03}")).mono().muted(),
59                        text(format!("row {i}")),
60                    ])
61                    .gap(tokens::SPACE_2)
62                    .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_1))
63                    .height(Size::Fixed(28.0))
64                    .align(Align::Center)
65                })
66                .height(Size::Fixed(240.0))
67                .padding(tokens::SPACE_2)
68                .stroke(tokens::BORDER)
69                .stroke_width(1.0)
70                .radius(tokens::RADIUS_MD),
71            ])
72            .gap(tokens::SPACE_2)
73            .width(Size::Fill(1.0))
74            .height(Size::Hug),
75            // 3) Opt-out: same content, no thumb.
76            column([
77                text("scroll().no_scrollbar()").bold(),
78                scroll(list_rows())
79                    .no_scrollbar()
80                    .height(Size::Fixed(240.0))
81                    .padding(tokens::SPACE_2)
82                    .stroke(tokens::BORDER)
83                    .stroke_width(1.0)
84                    .radius(tokens::RADIUS_MD),
85            ])
86            .gap(tokens::SPACE_2)
87            .width(Size::Fill(1.0))
88            .height(Size::Hug),
89        ])
90        .gap(tokens::SPACE_4)
91        .width(Size::Fill(1.0)),
92    ])
93    .gap(tokens::SPACE_4)
94    .padding(tokens::SPACE_7)
95}
Source

pub fn pin_end(self) -> Self

Stick this scroll viewport’s offset to the tail of its content the way chat logs and activity feeds do — when new children land below the current bottom, the offset follows them; when the user scrolls up, the pin releases; when the user scrolls back to the bottom, it re-engages. Mirrors egui::ScrollArea::stick_to_bottom.

On first layout the offset starts at max_offset, so a freshly mounted scroll([...]).pin_end() paints with its tail visible rather than its head. Programmatic crate::scroll::ScrollRequest::EnsureVisible requests that resolve away from the tail also release the pin, so a “jump-to-message N” action behaves as the user expects.

Source

pub fn virtual_anchor_policy(self, policy: VirtualAnchorPolicy) -> Self

Override how a dynamic virtual list chooses the in-viewport row point that anchors the next frame.

Source

pub fn arrow_nav_siblings(self) -> Self

Treat this element’s focusable children as a single arrow-navigable group.

Source

pub fn layout<F>(self, f: F) -> Self
where F: Fn(LayoutCtx<'_>) -> Vec<Rect> + Send + Sync + 'static,

Replace the column/row/overlay distribution for this node with a custom child layout function.

Examples found in repository?
examples/circular_layout.rs (line 76)
48fn fixture() -> El {
49    let centre = h2("Compass").center_text();
50    let dirs = [
51        ("North", "n"),
52        ("NE", "ne"),
53        ("East", "e"),
54        ("SE", "se"),
55        ("South", "s"),
56        ("SW", "sw"),
57        ("West", "w"),
58        ("NW", "nw"),
59    ];
60
61    let mut children: Vec<El> = vec![centre];
62    for (label, k) in dirs {
63        children.push(button(label).key(k).primary());
64    }
65
66    column([
67        h1("Custom layout — circular"),
68        paragraph(
69            "Eight buttons positioned on a circle by an author-supplied \
70             LayoutFn. Stock paint, automatic hover/press, and hit-test \
71             all keep working — only the rect distribution changed.",
72        )
73        .muted(),
74        stack(children)
75            .key("compass")
76            .layout(circular)
77            .width(Size::Fill(1.0))
78            .height(Size::Fixed(360.0)),
79    ])
80    .gap(tokens::SPACE_4)
81    .padding(tokens::SPACE_7)
82}
Source

pub fn child(self, c: impl Into<El>) -> Self

Examples found in repository?
examples/settings_calibration.rs (lines 380-384)
377fn icon_slot(icon_name: &'static str) -> El {
378    El::new(Kind::Custom("icon_cell"))
379        .style_profile(StyleProfile::Surface)
380        .child(
381            icon(icon_name)
382                .color(tokens::FOREGROUND)
383                .icon_size(tokens::ICON_XS),
384        )
385        .align(Align::Center)
386        .justify(Justify::Center)
387        .fill(tokens::MUTED)
388        .stroke(tokens::BORDER)
389        .radius(tokens::RADIUS_SM)
390        .width(Size::Fixed(30.0))
391        .height(Size::Fixed(30.0))
392}
Source

pub fn children<I, E>(self, cs: I) -> Self
where I: IntoIterator<Item = E>, E: Into<El>,

Source

pub fn axis(self, a: Axis) -> Self

Set the layout axis directly.

Source§

impl El

Source

pub fn fill(self, c: Color) -> Self

Examples found in repository?
examples/polish_calibration.rs (line 41)
35fn polish_calibration() -> El {
36    row([sidebar(), main_panel()])
37        .key("metric:root")
38        .gap(0.0)
39        .fill_size()
40        .align(Align::Stretch)
41        .fill(tokens::BACKGROUND)
42}
43
44fn sidebar() -> El {
45    column([
46        column([h2("Aetna"), text("calibration").muted()])
47            .key("metric:sidebar.brand")
48            .gap(tokens::SPACE_1)
49            .height(Size::Hug),
50        spacer().height(Size::Fixed(tokens::SPACE_4)),
51        nav_item("01", "Overview", true),
52        nav_item("02", "Commands", false),
53        nav_item("03", "Tables", false),
54        nav_item("04", "Forms", false),
55        spacer(),
56        badge("dark theme").muted(),
57    ])
58    .gap(tokens::SPACE_2)
59    .padding(tokens::SPACE_5)
60    .key("metric:sidebar")
61    .width(Size::Fixed(220.0))
62    .height(Size::Fill(1.0))
63    .fill(tokens::CARD)
64    .stroke(tokens::BORDER)
65}
66
67fn nav_item(icon: &'static str, label: &'static str, selected: bool) -> El {
68    let mut item = row([
69        icon_cell(icon),
70        text(label)
71            .font_weight(FontWeight::Medium)
72            .ellipsis()
73            .width(Size::Fill(1.0)),
74    ])
75    .key(if selected {
76        "metric:sidebar.nav.row".to_string()
77    } else {
78        format!("nav-{label}")
79    })
80    .metrics_role(MetricsRole::ListItem)
81    .gap(tokens::SPACE_3)
82    .padding(Sides::xy(tokens::SPACE_2, 0.0))
83    .height(Size::Fixed(40.0))
84    .align(Align::Center)
85    .focusable();
86
87    if selected {
88        item = item.current();
89    }
90
91    item
92}
93
94fn main_panel() -> El {
95    column([
96        toolbar(),
97        column([
98            row([
99                kpi_card("Latency", "42 ms", "-18%", true),
100                kpi_card("Runs", "1,284", "+12%", true),
101                kpi_card("Errors", "7", "+2", false),
102            ])
103            .gap(tokens::SPACE_4),
104            row([table_card(), command_card()])
105                .gap(tokens::SPACE_4)
106                .height(Size::Fill(1.0))
107                .align(Align::Stretch),
108        ])
109        .gap(tokens::SPACE_4)
110        .height(Size::Fill(1.0))
111        .align(Align::Stretch),
112    ])
113    .padding(tokens::SPACE_7)
114    .gap(tokens::SPACE_2)
115    .width(Size::Fill(1.0))
116    .height(Size::Fill(1.0))
117}
118
119fn toolbar() -> El {
120    row([
121        column([
122            h1("Polish calibration").key("metric:page.title"),
123            text("A representative app surface for default tuning.")
124                .muted()
125                .key("metric:page.subtitle"),
126        ])
127        .gap(tokens::SPACE_2)
128        .height(Size::Hug),
129        spacer(),
130        button_with_icon("search", "Preview")
131            .secondary()
132            .key("metric:action.secondary"),
133        button_with_icon("upload", "Publish")
134            .primary()
135            .key("metric:action.primary"),
136    ])
137    .key("metric:header")
138    .gap(tokens::SPACE_4)
139    .height(Size::Hug)
140    .align(Align::Start)
141}
142
143fn kpi_card(label: &'static str, value: &'static str, delta: &'static str, positive: bool) -> El {
144    let delta_badge = if positive {
145        badge(delta).success()
146    } else {
147        badge(delta).destructive()
148    };
149    let delta_badge = if label == "Latency" {
150        delta_badge.key("metric:kpi.badge")
151    } else {
152        delta_badge
153    };
154    let value_text = h2(value).display();
155    let value_text = if label == "Latency" {
156        value_text.key("metric:kpi.value")
157    } else {
158        value_text
159    };
160    card([
161        card_header([card_title(label)]),
162        card_content([
163            row([value_text, spacer(), delta_badge]).align(Align::Center),
164            text(if positive {
165                "Moving in the expected direction"
166            } else {
167                "Needs visual attention"
168            })
169            .muted(),
170        ])
171        .gap(tokens::SPACE_6),
172    ])
173    .key(if label == "Latency" {
174        "metric:kpi.card"
175    } else {
176        label
177    })
178    .width(Size::Fill(1.0))
179}
180
181fn table_card() -> El {
182    card([
183        card_header([card_title("Reference rows")]),
184        card_content([table([
185            table_header([table_row([
186                table_head("Status").width(Size::Fixed(86.0)),
187                table_head("Surface").width(Size::Fill(1.0)),
188                table_head("Owner").width(Size::Fixed(110.0)),
189                table_head("State").width(Size::Fixed(86.0)),
190            ])
191            .key("metric:table.header")]),
192            divider(),
193            table_body([
194                data_row("OK", "Settings card", "core", "selected", true, "success"),
195                data_row(
196                    "WARN",
197                    "Command palette density",
198                    "widgets",
199                    "needs work",
200                    false,
201                    "warning",
202                ),
203                data_row(
204                    "ERR",
205                    "Disabled and invalid states",
206                    "style",
207                    "missing",
208                    false,
209                    "destructive",
210                ),
211                data_row(
212                    "INFO",
213                    "Token resolution",
214                    "theme",
215                    "planned",
216                    false,
217                    "info",
218                ),
219                data_row(
220                    "OK",
221                    "Popover elevation",
222                    "shader",
223                    "queued",
224                    false,
225                    "success",
226                ),
227            ])
228            .gap(tokens::SPACE_1)
229            .width(Size::Fill(1.0)),
230        ])]),
231    ])
232    .key("metric:table.card")
233    .width(Size::Fill(1.2))
234    .height(Size::Fill(1.0))
235}
236
237fn data_row(
238    status: &'static str,
239    title: &'static str,
240    owner: &'static str,
241    state: &'static str,
242    selected: bool,
243    tone: &'static str,
244) -> El {
245    let status_badge = match tone {
246        "success" => badge(status).success(),
247        "warning" => badge(status).warning(),
248        "destructive" => badge(status).destructive(),
249        _ => badge(status).info(),
250    };
251    let status_badge = if selected {
252        status_badge.key("metric:table.badge")
253    } else {
254        status_badge
255    };
256
257    let mut row = table_row([
258        table_cell(status_badge).width(Size::Fixed(70.0)),
259        column([
260            text(title)
261                .font_weight(FontWeight::Medium)
262                .ellipsis()
263                .width(Size::Fill(1.0)),
264            text("Default styling probe.")
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0)),
271        table_cell(text(owner).muted()).width(Size::Fixed(110.0)),
272        table_cell(text(state).label().small()).width(Size::Fixed(86.0)),
273    ])
274    .key(if selected {
275        "metric:table.row".to_string()
276    } else {
277        format!("row-{title}")
278    })
279    .focusable();
280
281    if selected {
282        row = row.selected();
283    }
284
285    row
286}
287
288fn command_card() -> El {
289    card([
290        card_header([card_title("Command surface")]),
291        card_content([
292            text_input(
293                "Search commands...",
294                &Selection::default(),
295                "command-search",
296            )
297            .key("metric:command.input")
298            .width(Size::Fill(1.0)),
299            popover_panel([
300                command_row("git-branch", "New branch", "Ctrl+B").key("metric:command.row"),
301                command_row("git-commit", "Commit staged files", "Ctrl+Enter")
302                    .key("command-row-commit"),
303                command_row("refresh-cw", "Refresh repository", "Ctrl+R")
304                    .key("command-row-refresh"),
305                command_row("alert-circle", "Force push", "Danger").key("command-row-force"),
306            ])
307            .width(Size::Fill(1.0)),
308            scroll([form_probe()]).key("form-probe-scroll"),
309        ])
310        .height(Size::Fill(1.0)),
311    ])
312    .key("metric:command.card")
313    .width(Size::Fill(0.8))
314    .height(Size::Fill(1.0))
315}
316
317fn form_probe() -> El {
318    form([
319        form_item([
320            form_label("Valid input"),
321            form_control(
322                text_input(
323                    "Valid input",
324                    &Selection::caret("valid-input", 11),
325                    "valid-input",
326                )
327                .key("metric:form.input"),
328            ),
329            form_description("Default field spacing and helper text."),
330        ]),
331        form_item([
332            form_label("Invalid input"),
333            form_control(
334                text_input(
335                    "Invalid input",
336                    &Selection::caret("invalid-input", 13),
337                    "invalid-input",
338                )
339                .invalid(),
340            ),
341            form_message("This field needs attention."),
342        ]),
343        row([
344            button("Disabled").secondary().disabled(),
345            button("Loading").primary().loading(),
346            spacer(),
347        ]),
348    ])
349    .padding(tokens::SPACE_3)
350    .fill(tokens::MUTED)
351    .stroke(tokens::BORDER)
352    .radius(tokens::RADIUS_MD)
353}
354
355fn icon_cell(label: &'static str) -> El {
356    El::new(Kind::Custom("icon_cell"))
357        .style_profile(StyleProfile::Surface)
358        .text(label)
359        .text_align(TextAlign::Center)
360        .caption()
361        .font_weight(FontWeight::Semibold)
362        .fill(tokens::MUTED)
363        .stroke(tokens::BORDER)
364        .radius(tokens::RADIUS_SM)
365        .width(Size::Fixed(26.0))
366        .height(Size::Fixed(26.0))
367}
More examples
Hide additional examples
examples/settings_calibration.rs (line 38)
32fn settings_calibration() -> El {
33    row([settings_sidebar(), settings_main()])
34        .key("metric:root")
35        .gap(0.0)
36        .fill_size()
37        .align(Align::Stretch)
38        .fill(tokens::BACKGROUND)
39}
40
41fn settings_sidebar() -> El {
42    column([
43        row([
44            icon_slot("settings"),
45            column([
46                text("Workspace")
47                    .semibold()
48                    .ellipsis()
49                    .width(Size::Fill(1.0)),
50                text("Settings").caption().ellipsis().width(Size::Fill(1.0)),
51            ])
52            .gap(2.0)
53            .width(Size::Fill(1.0))
54            .height(Size::Hug),
55        ])
56        .gap(tokens::SPACE_2)
57        .height(Size::Fixed(44.0))
58        .align(Align::Center),
59        section_label("Personal"),
60        side_item("users", "Profile", false),
61        side_item("settings", "Account", true),
62        side_item("alert-circle", "Security", false),
63        side_item("bell", "Notifications", false),
64        spacer().height(Size::Fixed(tokens::SPACE_4)),
65        section_label("Workspace"),
66        side_item("file-text", "Billing", false),
67        side_item("bar-chart", "Appearance", false),
68        side_item("activity", "Integrations", false),
69        spacer(),
70        column([text("Changes sync after save.").caption().wrap_text()])
71            .padding(tokens::SPACE_2)
72            .fill(tokens::MUTED)
73            .radius(tokens::RADIUS_MD),
74    ])
75    .gap(tokens::SPACE_2)
76    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_2))
77    .key("metric:sidebar")
78    .width(Size::Fixed(244.0))
79    .height(Size::Fill(1.0))
80    .fill(tokens::CARD)
81    .stroke(tokens::BORDER)
82}
83
84fn settings_main() -> El {
85    column([
86        settings_header(),
87        row([settings_nav_card(), settings_body(), settings_aside()])
88            .gap(tokens::SPACE_4)
89            .padding(tokens::SPACE_4)
90            .height(Size::Fill(1.0))
91            .align(Align::Stretch),
92    ])
93    .width(Size::Fill(1.0))
94    .height(Size::Fill(1.0))
95}
96
97fn settings_header() -> El {
98    row([
99        icon_button("menu").ghost(),
100        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
101        h3("Settings").key("metric:page.title"),
102        spacer(),
103        button("Reset").secondary(),
104        button("Save changes").primary(),
105    ])
106    .key("metric:header")
107    .gap(tokens::SPACE_3)
108    .height(Size::Fixed(56.0))
109    .padding(Sides::xy(tokens::SPACE_4, 0.0))
110    .align(Align::Center)
111    .stroke(tokens::BORDER)
112}
113
114fn settings_nav_card() -> El {
115    column([
116        settings_nav_item("Account", true),
117        settings_nav_item("Security", false),
118        settings_nav_item("Notifications", false),
119        settings_nav_item("Appearance", false),
120        settings_nav_item("Billing", false),
121    ])
122    .gap(tokens::SPACE_1)
123    .padding(tokens::SPACE_1)
124    .width(Size::Fixed(220.0))
125    .height(Size::Fill(1.0))
126    .style_profile(StyleProfile::Surface)
127    .surface_role(SurfaceRole::Panel)
128    .fill(tokens::CARD)
129    .stroke(tokens::BORDER)
130    .radius(tokens::RADIUS_MD)
131    .shadow(tokens::SHADOW_MD)
132}
133
134fn settings_nav_item(label: &'static str, selected: bool) -> El {
135    let mut item = row([
136        El::new(Kind::Custom("nav-dot"))
137            .fill(tokens::MUTED_FOREGROUND)
138            .radius(tokens::RADIUS_PILL)
139            .width(Size::Fixed(6.0))
140            .height(Size::Fixed(6.0)),
141        text(label)
142            .font_weight(FontWeight::Medium)
143            .ellipsis()
144            .width(Size::Fill(1.0)),
145    ])
146    .key(if selected {
147        "metric:settings.nav.row".to_string()
148    } else {
149        format!("settings-nav-{label}")
150    })
151    .metrics_role(MetricsRole::ListItem)
152    .align(Align::Center)
153    .focusable();
154
155    if selected {
156        item = item.current();
157    } else {
158        item = item.color(tokens::MUTED_FOREGROUND);
159    }
160
161    item
162}
163
164fn settings_body() -> El {
165    column([
166        column([
167            h1("Account").heading().key("metric:section.title"),
168            text("Manage identity, workspace defaults, and security preferences.")
169                .muted()
170                .wrap_text()
171                .key("metric:page.subtitle"),
172        ])
173        .gap(tokens::SPACE_1)
174        .height(Size::Hug),
175        scroll([profile_card(), preferences_card()])
176            .key("settings-body-scroll")
177            .gap(tokens::SPACE_4)
178            .width(Size::Fill(1.0))
179            .height(Size::Fill(1.0)),
180    ])
181    .gap(tokens::SPACE_4)
182    .width(Size::Fill(1.0))
183    .height(Size::Fill(1.0))
184}
185
186fn profile_card() -> El {
187    card([
188        card_header([
189            card_title("Profile"),
190            card_description("This information appears in audit logs and shared documents."),
191        ]),
192        card_content([form([
193            row([
194                setting_field("Display name", "Alicia Koch", "display-name"),
195                setting_field("Email", "alicia@acme.co", "email"),
196            ])
197            .gap(tokens::SPACE_3),
198            row([
199                setting_select("Role", "Workspace admin", "role"),
200                setting_select("Region", "US East", "region"),
201            ])
202            .gap(tokens::SPACE_3),
203        ])]),
204    ])
205    .key("metric:profile.card")
206}
207
208fn setting_field(label: &'static str, value: &'static str, key: &'static str) -> El {
209    form_item([
210        form_label(label),
211        form_control(
212            text_input(value, &Selection::caret(key, value.len()), key).key(
213                if key == "display-name" {
214                    "metric:form.input"
215                } else {
216                    key
217                },
218            ),
219        ),
220    ])
221    .width(Size::Fill(1.0))
222}
223
224fn setting_select(label: &'static str, value: &'static str, key: &'static str) -> El {
225    form_item([form_label(label), form_control(select_trigger(key, value))]).width(Size::Fill(1.0))
226}
227
228fn preferences_card() -> El {
229    card([
230        card_header([
231            card_title("Preferences"),
232            card_description("Defaults used when creating new dashboards and exports."),
233        ]),
234        card_content([column([
235            preference_row(
236                "Compact navigation",
237                "Use tighter rows in the sidebar and command menus.",
238                switch(true).key("compact-navigation"),
239            ),
240            divider(),
241            preference_row(
242                "Email summaries",
243                "Send a daily digest when documents change.",
244                switch(false).key("email-summaries"),
245            ),
246            divider(),
247            preference_row(
248                "Require approval",
249                "Route external sharing through an owner review.",
250                checkbox(true).key("approval-required"),
251            ),
252        ])
253        .gap(0.0)
254        .width(Size::Fill(1.0))])
255        .padding(0.0),
256    ])
257    .key("metric:preferences.card")
258}
259
260fn preference_row(title: &'static str, description: &'static str, control: El) -> El {
261    row([
262        column([
263            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
264            text(description)
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0))
271        .height(Size::Hug),
272        control,
273    ])
274    .key(if title == "Compact navigation" {
275        "metric:preference.row".to_string()
276    } else {
277        format!("preference-{title}")
278    })
279    .metrics_role(MetricsRole::PreferenceRow)
280    .gap(tokens::SPACE_4)
281    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_3))
282    .align(Align::Center)
283}
284
285fn settings_aside() -> El {
286    column([security_card(), scale_card()])
287        .gap(tokens::SPACE_4)
288        .width(Size::Fixed(300.0))
289        .height(Size::Fill(1.0))
290}
291
292fn security_card() -> El {
293    card([
294        card_header([
295            card_title("Security"),
296            card_description("Two-factor authentication is enabled for all privileged users."),
297        ]),
298        card_content([
299            compact_stat("Passkeys", "2 registered", badge("On").success()),
300            compact_stat("Sessions", "3 active", button("Review").secondary()),
301        ]),
302    ])
303    .width(Size::Fill(1.0))
304}
305
306fn scale_card() -> El {
307    card([
308        card_header([
309            card_title("Interface scale"),
310            card_description("Reference captures keep browser zoom fixed and vary root UI scale."),
311        ]),
312        card_content([
313            row([text("Dense").caption(), spacer(), text("Default").caption()]),
314            slider(0.66, tokens::PRIMARY)
315                .key("interface-scale")
316                .width(Size::Fill(1.0)),
317        ]),
318    ])
319    .width(Size::Fill(1.0))
320}
321
322fn compact_stat(title: &'static str, detail: &'static str, control: El) -> El {
323    row([
324        column([
325            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
326            text(detail).caption().ellipsis().width(Size::Fill(1.0)),
327        ])
328        .gap(2.0)
329        .width(Size::Fill(1.0))
330        .height(Size::Hug),
331        control,
332    ])
333    .gap(tokens::SPACE_2)
334    .height(Size::Fixed(44.0))
335    .align(Align::Center)
336}
337
338fn section_label(label: &'static str) -> El {
339    text(label)
340        .caption()
341        .height(Size::Fixed(22.0))
342        .padding(Sides::xy(tokens::SPACE_2, 0.0))
343}
344
345fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
346    let mut item = row([
347        icon(icon_name)
348            .color(tokens::MUTED_FOREGROUND)
349            .icon_size(tokens::ICON_SM)
350            .width(Size::Fixed(tokens::ICON_SM)),
351        text(label)
352            .font_weight(FontWeight::Medium)
353            .ellipsis()
354            .width(Size::Fill(1.0)),
355    ])
356    .key(if selected {
357        "metric:sidebar.nav.row".to_string()
358    } else {
359        format!("side-item-{label}")
360    })
361    .metrics_role(MetricsRole::ListItem)
362    .gap(tokens::SPACE_2)
363    .padding(Sides::xy(tokens::SPACE_2, 0.0))
364    .height(Size::Fixed(32.0))
365    .align(Align::Center)
366    .focusable();
367
368    if selected {
369        item = item.current();
370    } else {
371        item = item.color(tokens::MUTED_FOREGROUND);
372    }
373
374    item
375}
376
377fn icon_slot(icon_name: &'static str) -> El {
378    El::new(Kind::Custom("icon_cell"))
379        .style_profile(StyleProfile::Surface)
380        .child(
381            icon(icon_name)
382                .color(tokens::FOREGROUND)
383                .icon_size(tokens::ICON_XS),
384        )
385        .align(Align::Center)
386        .justify(Justify::Center)
387        .fill(tokens::MUTED)
388        .stroke(tokens::BORDER)
389        .radius(tokens::RADIUS_SM)
390        .width(Size::Fixed(30.0))
391        .height(Size::Fixed(30.0))
392}
examples/dashboard_01_calibration.rs (line 38)
32fn dashboard_01_calibration() -> El {
33    row([dashboard_sidebar(), dashboard_main()])
34        .key("metric:root")
35        .gap(0.0)
36        .fill_size()
37        .align(Align::Stretch)
38        .fill(tokens::BACKGROUND)
39}
40
41fn dashboard_sidebar() -> El {
42    column([
43        row([
44            icon_cell("A"),
45            column([
46                text("Acme Inc.")
47                    .semibold()
48                    .ellipsis()
49                    .width(Size::Fill(1.0)),
50                text("Enterprise")
51                    .caption()
52                    .ellipsis()
53                    .width(Size::Fill(1.0)),
54            ])
55            .gap(2.0)
56            .width(Size::Fill(1.0))
57            .height(Size::Hug),
58        ])
59        .gap(tokens::SPACE_2)
60        .height(Size::Fixed(44.0))
61        .align(Align::Center),
62        section_label("Platform"),
63        side_item("layout-dashboard", "Dashboard", true),
64        side_item("activity", "Lifecycle", false),
65        side_item("bar-chart", "Analytics", false),
66        side_item("folder", "Projects", false),
67        spacer().height(Size::Fixed(tokens::SPACE_4)),
68        section_label("Documents"),
69        side_item("file-text", "Data library", false),
70        side_item("download", "Reports", false),
71        side_item("users", "Team", false),
72        spacer(),
73        row([
74            icon_cell("AK"),
75            column([
76                text("Alicia Koch")
77                    .semibold()
78                    .ellipsis()
79                    .width(Size::Fill(1.0)),
80                text("alicia@example.com")
81                    .caption()
82                    .ellipsis()
83                    .width(Size::Fill(1.0)),
84            ])
85            .gap(2.0)
86            .width(Size::Fill(1.0))
87            .height(Size::Hug),
88        ])
89        .gap(tokens::SPACE_2)
90        .height(Size::Fixed(50.0))
91        .align(Align::Center),
92    ])
93    .gap(tokens::SPACE_2)
94    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_2))
95    .key("metric:sidebar")
96    .width(Size::Fixed(244.0))
97    .height(Size::Fill(1.0))
98    .fill(tokens::CARD)
99    .stroke(tokens::BORDER)
100}
101
102fn section_label(label: &'static str) -> El {
103    text(label)
104        .caption()
105        .height(Size::Fixed(22.0))
106        .padding(Sides::xy(tokens::SPACE_2, 0.0))
107}
108
109fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
110    let mut item = row([
111        icon(icon_name)
112            .color(tokens::MUTED_FOREGROUND)
113            .icon_size(tokens::ICON_SM)
114            .width(Size::Fixed(tokens::ICON_SM)),
115        text(label)
116            .font_weight(FontWeight::Medium)
117            .ellipsis()
118            .width(Size::Fill(1.0)),
119    ])
120    .key(if selected {
121        "metric:sidebar.nav.row".to_string()
122    } else {
123        format!("side-item-{label}")
124    })
125    .metrics_role(MetricsRole::ListItem)
126    .gap(tokens::SPACE_2)
127    .padding(Sides::xy(tokens::SPACE_2, 0.0))
128    .height(Size::Fixed(32.0))
129    .align(Align::Center)
130    .focusable();
131
132    if selected {
133        item = item.current();
134    } else {
135        item = item.color(tokens::MUTED_FOREGROUND);
136    }
137
138    item
139}
140
141fn dashboard_main() -> El {
142    column([
143        dashboard_header(),
144        column([
145            row([
146                metric_card(
147                    "bar-chart",
148                    "Total Revenue",
149                    "$1,250.00",
150                    "+12.5%",
151                    "Trending up this month",
152                    true,
153                ),
154                metric_card(
155                    "users",
156                    "New Customers",
157                    "1,234",
158                    "-20%",
159                    "Acquisition needs attention",
160                    false,
161                ),
162                metric_card(
163                    "folder",
164                    "Active Accounts",
165                    "45,678",
166                    "+12.5%",
167                    "Strong user retention",
168                    true,
169                ),
170                metric_card(
171                    "activity",
172                    "Growth Rate",
173                    "4.5%",
174                    "+4.5%",
175                    "Meets growth projections",
176                    true,
177                ),
178            ])
179            .gap(tokens::SPACE_4),
180            row([chart_card(), sales_card()])
181                .gap(tokens::SPACE_4)
182                .height(Size::Fixed(306.0))
183                .align(Align::Stretch),
184            documents_card(),
185        ])
186        .gap(tokens::SPACE_4)
187        .padding(tokens::SPACE_7)
188        .height(Size::Fill(1.0)),
189    ])
190    .width(Size::Fill(1.0))
191    .height(Size::Fill(1.0))
192}
193
194fn dashboard_header() -> El {
195    row([
196        icon_button("menu").ghost(),
197        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
198        h3("Documents").key("metric:page.title"),
199        spacer(),
200        text_input("Search...", &Selection::default(), "dashboard-search")
201            .key("metric:command.input")
202            .width(Size::Fixed(260.0)),
203        icon_button("plus").ghost(),
204        icon_button("bell").ghost(),
205    ])
206    .key("metric:header")
207    .gap(tokens::SPACE_3)
208    .height(Size::Fixed(56.0))
209    .padding(Sides::xy(tokens::SPACE_4, 0.0))
210    .align(Align::Center)
211    .stroke(tokens::BORDER)
212}
213
214fn metric_card(
215    icon_name: &'static str,
216    title: &'static str,
217    value: &'static str,
218    delta: &'static str,
219    note: &'static str,
220    positive: bool,
221) -> El {
222    let badge = if positive {
223        badge(delta).success()
224    } else {
225        badge(delta).warning()
226    };
227    let badge = if title == "Total Revenue" {
228        badge.key("metric:kpi.badge")
229    } else {
230        badge
231    };
232    let value = if title == "Total Revenue" {
233        h2(value).ellipsis().key("metric:kpi.value")
234    } else {
235        h2(value).ellipsis()
236    };
237    card([card_content([
238        row([
239            row([
240                icon(icon_name)
241                    .color(tokens::MUTED_FOREGROUND)
242                    .icon_size(tokens::ICON_XS),
243                text(title).muted().ellipsis().width(Size::Fill(1.0)),
244            ])
245            .gap(tokens::SPACE_1)
246            .width(Size::Fill(1.0))
247            .align(Align::Center),
248            badge,
249        ])
250        .gap(tokens::SPACE_2)
251        .align(Align::Center),
252        value,
253        text(note).caption().ellipsis().width(Size::Fill(1.0)),
254    ])
255    .padding(tokens::SPACE_4)
256    .gap(tokens::SPACE_2)])
257    .key(if title == "Total Revenue" {
258        "metric:kpi.card"
259    } else {
260        title
261    })
262    .width(Size::Fill(1.0))
263}
264
265fn chart_card() -> El {
266    card([
267        card_header([
268            card_title("Visitors for the last 6 months"),
269            card_description("Total visitors by channel."),
270        ])
271        .padding(tokens::SPACE_4),
272        card_content([row(chart_bars())
273            .gap(2.0)
274            .height(Size::Fill(1.0))
275            .align(Align::End)])
276        .padding(Sides {
277            left: tokens::SPACE_4,
278            right: tokens::SPACE_4,
279            top: 0.0,
280            bottom: tokens::SPACE_4,
281        })
282        .height(Size::Fill(1.0)),
283    ])
284    .key("metric:chart.card")
285    .width(Size::Fill(1.0))
286    .height(Size::Fill(1.0))
287}
288
289fn chart_bars() -> Vec<El> {
290    [
291        48.0, 72.0, 56.0, 90.0, 64.0, 80.0, 108.0, 84.0, 122.0, 96.0, 136.0, 118.0,
292    ]
293    .into_iter()
294    .flat_map(|height| {
295        [
296            bar(height, tokens::MUTED_FOREGROUND),
297            bar((height - 28.0_f32).max(24.0), tokens::INPUT),
298        ]
299    })
300    .collect()
301}
302
303fn bar(height: f32, color: Color) -> El {
304    El::new(Kind::Custom("chart_bar"))
305        .fill(color)
306        .radius(tokens::RADIUS_SM)
307        .width(Size::Fill(1.0))
308        .height(Size::Fixed(height))
309}
310
311fn sales_card() -> El {
312    card([
313        card_header([
314            card_title("Recent Sales"),
315            card_description("You made 265 sales this month."),
316        ])
317        .padding(tokens::SPACE_4),
318        card_content([
319            sale_row("OM", "Olivia Martin", "olivia@example.com", "+$1,999.00"),
320            sale_row("JL", "Jackson Lee", "jackson@example.com", "+$39.00"),
321            sale_row("IN", "Isabella Nguyen", "isabella@example.com", "+$299.00"),
322            sale_row("WK", "William Kim", "will@example.com", "+$99.00"),
323        ])
324        .gap(tokens::SPACE_2)
325        .padding(Sides {
326            left: tokens::SPACE_4,
327            right: tokens::SPACE_4,
328            top: 0.0,
329            bottom: tokens::SPACE_4,
330        }),
331    ])
332    .key("metric:sales.card")
333    .width(Size::Fixed(330.0))
334    .height(Size::Fill(1.0))
335}
336
337fn sale_row(
338    initials: &'static str,
339    name: &'static str,
340    email: &'static str,
341    amount: &'static str,
342) -> El {
343    row([
344        icon_cell(initials),
345        column([
346            text(name).semibold().ellipsis().width(Size::Fill(1.0)),
347            text(email).caption().ellipsis().width(Size::Fill(1.0)),
348        ])
349        .gap(2.0)
350        .height(Size::Hug)
351        .width(Size::Fill(1.0)),
352        text(amount).label().small(),
353    ])
354    .gap(tokens::SPACE_2)
355    .height(Size::Fixed(42.0))
356    .align(Align::Center)
357}
358
359fn documents_card() -> El {
360    card([
361        card_header([card_title("Documents")]).padding(tokens::SPACE_4),
362        card_content([scroll([table([
363            table_header([table_row([
364                table_head("").width(Size::Fixed(35.0)),
365                table_head("Header").width(Size::Fill(1.8)),
366                table_head("Section Type").width(Size::Fill(1.0)),
367                table_head("Status").width(Size::Fixed(104.0)),
368                table_head("Target").width(Size::Fixed(64.0)),
369                table_head("Limit").width(Size::Fixed(64.0)),
370                table_head("Reviewer").width(Size::Fixed(128.0)),
371                table_head("").width(Size::Fixed(32.0)),
372            ])
373            .padding(Sides::xy(tokens::SPACE_4, 0.0))
374            .key("metric:table.header")]),
375            divider(),
376            table_body([
377                document_row(
378                    "Cover page",
379                    "Cover page",
380                    "In Process",
381                    "18",
382                    "5",
383                    "Eddie Lake",
384                    "info",
385                ),
386                document_row(
387                    "Table of contents",
388                    "Table of contents",
389                    "Done",
390                    "29",
391                    "24",
392                    "Eddie Lake",
393                    "success",
394                ),
395            ]),
396        ])])
397        .height(Size::Fill(1.0))])
398        .gap(0.0)
399        .padding(0.0)
400        .height(Size::Fill(1.0)),
401    ])
402    .key("metric:table.card")
403    .height(Size::Fill(1.0))
404}
405
406fn document_row(
407    header: &'static str,
408    section: &'static str,
409    status: &'static str,
410    target: &'static str,
411    limit: &'static str,
412    reviewer: &'static str,
413    tone: &'static str,
414) -> El {
415    let status_badge = match tone {
416        "success" => badge(status).success(),
417        _ => badge(status).info(),
418    };
419    table_row([
420        table_utility_cell("::"),
421        table_cell(text(header).label().small()).width(Size::Fill(1.8)),
422        table_cell(text(section).muted()).width(Size::Fill(1.0)),
423        table_cell(status_badge).width(Size::Fixed(104.0)),
424        table_cell(text(target).label().small()).width(Size::Fixed(64.0)),
425        table_cell(text(limit).label().small()).width(Size::Fixed(64.0)),
426        table_cell(text(reviewer).muted()).width(Size::Fixed(128.0)),
427        table_action_cell(),
428    ])
429    .padding(Sides::xy(tokens::SPACE_4, 0.0))
430    .key(if header == "Cover page" {
431        "metric:table.row"
432    } else {
433        header
434    })
435}
436
437fn table_utility_cell(label: &'static str) -> El {
438    table_cell(text(label).muted().center_text()).width(Size::Fixed(35.0))
439}
440
441fn table_action_cell() -> El {
442    stack([icon("more-horizontal")
443        .icon_size(tokens::ICON_SM)
444        .color(tokens::MUTED_FOREGROUND)])
445    .align(Align::Center)
446    .justify(Justify::Center)
447    .width(Size::Fixed(32.0))
448    .height(Size::Hug)
449}
450
451fn icon_cell(label: &'static str) -> El {
452    El::new(Kind::Custom("icon_cell"))
453        .style_profile(StyleProfile::Surface)
454        .text(label)
455        .text_align(TextAlign::Center)
456        .caption()
457        .font_weight(FontWeight::Semibold)
458        .fill(tokens::MUTED)
459        .radius(tokens::RADIUS_SM)
460        .width(Size::Fixed(30.0))
461        .height(Size::Fixed(30.0))
462}
examples/custom_paint.rs (line 65)
44fn graph_cell(lane: u8, selected: bool) -> El {
45    let lane_color = lane_palette(lane);
46    let ring_color = if selected {
47        Color::rgb(245, 245, 250)
48    } else {
49        lane_color
50    };
51    let ring_w = if selected { 2.5 } else { 1.5 };
52    let radius = 5.0;
53    let line_w = 2.0;
54    let lane_frac = (lane as f32 + 0.5) / LANE_COUNT as f32;
55
56    El::new(Kind::Custom("graph_cell"))
57        .width(Size::Fixed(GRAPH_WIDTH))
58        .height(Size::Fixed(ROW_HEIGHT))
59        .shader(
60            ShaderBinding::custom("commit_node")
61                .color("vec_a", tokens::BACKGROUND)
62                .color("vec_b", ring_color)
63                .vec4("vec_c", [radius, ring_w, line_w, lane_frac]),
64        )
65        .fill(lane_color)
66}
examples/palette_demo.rs (line 119)
77fn palette_demo(label: &'static str, palette: &Palette) -> El {
78    column([
79        row([
80            column([
81                h1("Palette demo"),
82                text("Aetna and Radix palettes rendered through Aetna tokens.").muted(),
83            ])
84            .gap(tokens::SPACE_2)
85            .height(Size::Hug),
86            spacer(),
87            badge(label).muted(),
88        ])
89        .align(Align::Start)
90        .height(Size::Hug),
91        row([
92            token_section(
93                "Core tokens",
94                "The shadcn-shaped semantic vocabulary.",
95                &CORE_TOKENS,
96                palette,
97            )
98            .width(Size::Fill(1.25)),
99            column([
100                token_section(
101                    "Aetna extensions",
102                    "Component and status tokens layered on top.",
103                    &EXTENSION_TOKENS,
104                    palette,
105                ),
106                component_section(),
107            ])
108            .gap(tokens::SPACE_4)
109            .width(Size::Fill(1.0))
110            .height(Size::Fill(1.0)),
111        ])
112        .gap(tokens::SPACE_4)
113        .align(Align::Stretch)
114        .height(Size::Fill(1.0)),
115    ])
116    .padding(tokens::SPACE_8)
117    .gap(tokens::SPACE_6)
118    .fill_size()
119    .fill(tokens::BACKGROUND)
120}
121
122const CORE_TOKENS: [TokenDef; 19] = [
123    TokenDef {
124        name: "background",
125        color: tokens::BACKGROUND,
126    },
127    TokenDef {
128        name: "foreground",
129        color: tokens::FOREGROUND,
130    },
131    TokenDef {
132        name: "card",
133        color: tokens::CARD,
134    },
135    TokenDef {
136        name: "card-foreground",
137        color: tokens::CARD_FOREGROUND,
138    },
139    TokenDef {
140        name: "popover",
141        color: tokens::POPOVER,
142    },
143    TokenDef {
144        name: "popover-foreground",
145        color: tokens::POPOVER_FOREGROUND,
146    },
147    TokenDef {
148        name: "primary",
149        color: tokens::PRIMARY,
150    },
151    TokenDef {
152        name: "primary-foreground",
153        color: tokens::PRIMARY_FOREGROUND,
154    },
155    TokenDef {
156        name: "secondary",
157        color: tokens::SECONDARY,
158    },
159    TokenDef {
160        name: "secondary-foreground",
161        color: tokens::SECONDARY_FOREGROUND,
162    },
163    TokenDef {
164        name: "muted",
165        color: tokens::MUTED,
166    },
167    TokenDef {
168        name: "muted-foreground",
169        color: tokens::MUTED_FOREGROUND,
170    },
171    TokenDef {
172        name: "accent",
173        color: tokens::ACCENT,
174    },
175    TokenDef {
176        name: "accent-foreground",
177        color: tokens::ACCENT_FOREGROUND,
178    },
179    TokenDef {
180        name: "destructive",
181        color: tokens::DESTRUCTIVE,
182    },
183    TokenDef {
184        name: "destructive-foreground",
185        color: tokens::DESTRUCTIVE_FOREGROUND,
186    },
187    TokenDef {
188        name: "border",
189        color: tokens::BORDER,
190    },
191    TokenDef {
192        name: "input",
193        color: tokens::INPUT,
194    },
195    TokenDef {
196        name: "ring",
197        color: tokens::RING,
198    },
199];
200
201const EXTENSION_TOKENS: [TokenDef; 12] = [
202    TokenDef {
203        name: "success",
204        color: tokens::SUCCESS,
205    },
206    TokenDef {
207        name: "success-foreground",
208        color: tokens::SUCCESS_FOREGROUND,
209    },
210    TokenDef {
211        name: "warning",
212        color: tokens::WARNING,
213    },
214    TokenDef {
215        name: "warning-foreground",
216        color: tokens::WARNING_FOREGROUND,
217    },
218    TokenDef {
219        name: "info",
220        color: tokens::INFO,
221    },
222    TokenDef {
223        name: "info-foreground",
224        color: tokens::INFO_FOREGROUND,
225    },
226    TokenDef {
227        name: "link-foreground",
228        color: tokens::LINK_FOREGROUND,
229    },
230    TokenDef {
231        name: "overlay-scrim",
232        color: tokens::OVERLAY_SCRIM,
233    },
234    TokenDef {
235        name: "scrollbar-thumb",
236        color: tokens::SCROLLBAR_THUMB_FILL,
237    },
238    TokenDef {
239        name: "scrollbar-thumb-active",
240        color: tokens::SCROLLBAR_THUMB_FILL_ACTIVE,
241    },
242    TokenDef {
243        name: "selection-bg",
244        color: tokens::SELECTION_BG,
245    },
246    TokenDef {
247        name: "selection-bg-unfocused",
248        color: tokens::SELECTION_BG_UNFOCUSED,
249    },
250];
251
252fn token_section(
253    title: &'static str,
254    description: &'static str,
255    tokens: &'static [TokenDef],
256    palette: &Palette,
257) -> El {
258    card([
259        card_header([card_title(title), card_description(description)]),
260        card_content([swatch_grid(tokens, palette)]),
261    ])
262    .height(Size::Fill(1.0))
263}
264
265fn swatch_grid(defs: &'static [TokenDef], palette: &Palette) -> El {
266    let rows = defs
267        .chunks(2)
268        .map(|chunk| {
269            row(chunk.iter().map(|token| token_chip(*token, palette)))
270                .gap(tokens::SPACE_3)
271                .align(Align::Center)
272                .width(Size::Fill(1.0))
273        })
274        .collect::<Vec<_>>();
275
276    column(rows)
277        .gap(tokens::SPACE_3)
278        .width(Size::Fill(1.0))
279        .height(Size::Hug)
280}
281
282fn token_chip(token: TokenDef, palette: &Palette) -> El {
283    let resolved = palette.resolve(token.color);
284    row([
285        El::new(Kind::Custom("palette-swatch"))
286            .at(file!(), line!())
287            .fill(token.color)
288            .stroke(tokens::BORDER)
289            .radius(tokens::RADIUS_SM)
290            .width(Size::Fixed(42.0))
291            .height(Size::Fixed(34.0)),
292        column([
293            text(token.name)
294                .label()
295                .ellipsis()
296                .nowrap_text()
297                .width(Size::Fill(1.0)),
298            mono(rgba_label(resolved)).caption().muted(),
299        ])
300        .gap(0.0)
301        .width(Size::Fill(1.0))
302        .height(Size::Hug),
303    ])
304    .gap(tokens::SPACE_2)
305    .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_2))
306    .align(Align::Center)
307    .fill(tokens::CARD)
308    .stroke(tokens::BORDER)
309    .radius(tokens::RADIUS_MD)
310    .width(Size::Fill(1.0))
311    .height(Size::Fixed(54.0))
312}
313
314fn component_section() -> El {
315    card([
316        card_header([
317            card_title("Stock widgets"),
318            card_description("The same palette applied to regular component constructors."),
319        ]),
320        card_content([
321            row([
322                button("Primary").primary(),
323                button("Secondary").secondary(),
324                button("Outline").outline(),
325                button("Ghost").ghost(),
326            ])
327            .gap(tokens::SPACE_2)
328            .align(Align::Center),
329            row([
330                badge("success").success(),
331                badge("warning").warning(),
332                badge("destructive").destructive(),
333                badge("info").info(),
334                badge("muted").muted(),
335            ])
336            .gap(tokens::SPACE_2)
337            .align(Align::Center),
338            row([
339                text_input("palette search", &Selection::default(), "palette:search")
340                    .width(Size::Fill(1.0)),
341                button_with_icon("settings", "Tune").secondary(),
342            ])
343            .gap(tokens::SPACE_2)
344            .align(Align::Center),
345            row([
346                surface_sample("Card", tokens::CARD),
347                surface_sample("Muted", tokens::MUTED),
348                surface_sample("Popover", tokens::POPOVER),
349            ])
350            .gap(tokens::SPACE_3)
351            .align(Align::Stretch),
352        ])
353        .gap(tokens::SPACE_4),
354    ])
355    .height(Size::Hug)
356}
357
358fn surface_sample(title: &'static str, fill: Color) -> El {
359    column([
360        text(title).label(),
361        text("surface sample").caption().muted(),
362    ])
363    .gap(tokens::SPACE_1)
364    .padding(tokens::SPACE_3)
365    .fill(fill)
366    .stroke(tokens::BORDER)
367    .radius(tokens::RADIUS_MD)
368    .width(Size::Fill(1.0))
369    .height(Size::Fixed(76.0))
370}
Source

pub fn dim_fill(self, c: Color) -> Self

Fill applied when the nearest focusable ancestor isn’t focused; the painter lerps from dim_fill toward fill as the focus envelope rises from 0 to 1. See Self::dim_fill field doc.

Source

pub fn stroke(self, c: Color) -> Self

Examples found in repository?
examples/polish_calibration.rs (line 64)
44fn sidebar() -> El {
45    column([
46        column([h2("Aetna"), text("calibration").muted()])
47            .key("metric:sidebar.brand")
48            .gap(tokens::SPACE_1)
49            .height(Size::Hug),
50        spacer().height(Size::Fixed(tokens::SPACE_4)),
51        nav_item("01", "Overview", true),
52        nav_item("02", "Commands", false),
53        nav_item("03", "Tables", false),
54        nav_item("04", "Forms", false),
55        spacer(),
56        badge("dark theme").muted(),
57    ])
58    .gap(tokens::SPACE_2)
59    .padding(tokens::SPACE_5)
60    .key("metric:sidebar")
61    .width(Size::Fixed(220.0))
62    .height(Size::Fill(1.0))
63    .fill(tokens::CARD)
64    .stroke(tokens::BORDER)
65}
66
67fn nav_item(icon: &'static str, label: &'static str, selected: bool) -> El {
68    let mut item = row([
69        icon_cell(icon),
70        text(label)
71            .font_weight(FontWeight::Medium)
72            .ellipsis()
73            .width(Size::Fill(1.0)),
74    ])
75    .key(if selected {
76        "metric:sidebar.nav.row".to_string()
77    } else {
78        format!("nav-{label}")
79    })
80    .metrics_role(MetricsRole::ListItem)
81    .gap(tokens::SPACE_3)
82    .padding(Sides::xy(tokens::SPACE_2, 0.0))
83    .height(Size::Fixed(40.0))
84    .align(Align::Center)
85    .focusable();
86
87    if selected {
88        item = item.current();
89    }
90
91    item
92}
93
94fn main_panel() -> El {
95    column([
96        toolbar(),
97        column([
98            row([
99                kpi_card("Latency", "42 ms", "-18%", true),
100                kpi_card("Runs", "1,284", "+12%", true),
101                kpi_card("Errors", "7", "+2", false),
102            ])
103            .gap(tokens::SPACE_4),
104            row([table_card(), command_card()])
105                .gap(tokens::SPACE_4)
106                .height(Size::Fill(1.0))
107                .align(Align::Stretch),
108        ])
109        .gap(tokens::SPACE_4)
110        .height(Size::Fill(1.0))
111        .align(Align::Stretch),
112    ])
113    .padding(tokens::SPACE_7)
114    .gap(tokens::SPACE_2)
115    .width(Size::Fill(1.0))
116    .height(Size::Fill(1.0))
117}
118
119fn toolbar() -> El {
120    row([
121        column([
122            h1("Polish calibration").key("metric:page.title"),
123            text("A representative app surface for default tuning.")
124                .muted()
125                .key("metric:page.subtitle"),
126        ])
127        .gap(tokens::SPACE_2)
128        .height(Size::Hug),
129        spacer(),
130        button_with_icon("search", "Preview")
131            .secondary()
132            .key("metric:action.secondary"),
133        button_with_icon("upload", "Publish")
134            .primary()
135            .key("metric:action.primary"),
136    ])
137    .key("metric:header")
138    .gap(tokens::SPACE_4)
139    .height(Size::Hug)
140    .align(Align::Start)
141}
142
143fn kpi_card(label: &'static str, value: &'static str, delta: &'static str, positive: bool) -> El {
144    let delta_badge = if positive {
145        badge(delta).success()
146    } else {
147        badge(delta).destructive()
148    };
149    let delta_badge = if label == "Latency" {
150        delta_badge.key("metric:kpi.badge")
151    } else {
152        delta_badge
153    };
154    let value_text = h2(value).display();
155    let value_text = if label == "Latency" {
156        value_text.key("metric:kpi.value")
157    } else {
158        value_text
159    };
160    card([
161        card_header([card_title(label)]),
162        card_content([
163            row([value_text, spacer(), delta_badge]).align(Align::Center),
164            text(if positive {
165                "Moving in the expected direction"
166            } else {
167                "Needs visual attention"
168            })
169            .muted(),
170        ])
171        .gap(tokens::SPACE_6),
172    ])
173    .key(if label == "Latency" {
174        "metric:kpi.card"
175    } else {
176        label
177    })
178    .width(Size::Fill(1.0))
179}
180
181fn table_card() -> El {
182    card([
183        card_header([card_title("Reference rows")]),
184        card_content([table([
185            table_header([table_row([
186                table_head("Status").width(Size::Fixed(86.0)),
187                table_head("Surface").width(Size::Fill(1.0)),
188                table_head("Owner").width(Size::Fixed(110.0)),
189                table_head("State").width(Size::Fixed(86.0)),
190            ])
191            .key("metric:table.header")]),
192            divider(),
193            table_body([
194                data_row("OK", "Settings card", "core", "selected", true, "success"),
195                data_row(
196                    "WARN",
197                    "Command palette density",
198                    "widgets",
199                    "needs work",
200                    false,
201                    "warning",
202                ),
203                data_row(
204                    "ERR",
205                    "Disabled and invalid states",
206                    "style",
207                    "missing",
208                    false,
209                    "destructive",
210                ),
211                data_row(
212                    "INFO",
213                    "Token resolution",
214                    "theme",
215                    "planned",
216                    false,
217                    "info",
218                ),
219                data_row(
220                    "OK",
221                    "Popover elevation",
222                    "shader",
223                    "queued",
224                    false,
225                    "success",
226                ),
227            ])
228            .gap(tokens::SPACE_1)
229            .width(Size::Fill(1.0)),
230        ])]),
231    ])
232    .key("metric:table.card")
233    .width(Size::Fill(1.2))
234    .height(Size::Fill(1.0))
235}
236
237fn data_row(
238    status: &'static str,
239    title: &'static str,
240    owner: &'static str,
241    state: &'static str,
242    selected: bool,
243    tone: &'static str,
244) -> El {
245    let status_badge = match tone {
246        "success" => badge(status).success(),
247        "warning" => badge(status).warning(),
248        "destructive" => badge(status).destructive(),
249        _ => badge(status).info(),
250    };
251    let status_badge = if selected {
252        status_badge.key("metric:table.badge")
253    } else {
254        status_badge
255    };
256
257    let mut row = table_row([
258        table_cell(status_badge).width(Size::Fixed(70.0)),
259        column([
260            text(title)
261                .font_weight(FontWeight::Medium)
262                .ellipsis()
263                .width(Size::Fill(1.0)),
264            text("Default styling probe.")
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0)),
271        table_cell(text(owner).muted()).width(Size::Fixed(110.0)),
272        table_cell(text(state).label().small()).width(Size::Fixed(86.0)),
273    ])
274    .key(if selected {
275        "metric:table.row".to_string()
276    } else {
277        format!("row-{title}")
278    })
279    .focusable();
280
281    if selected {
282        row = row.selected();
283    }
284
285    row
286}
287
288fn command_card() -> El {
289    card([
290        card_header([card_title("Command surface")]),
291        card_content([
292            text_input(
293                "Search commands...",
294                &Selection::default(),
295                "command-search",
296            )
297            .key("metric:command.input")
298            .width(Size::Fill(1.0)),
299            popover_panel([
300                command_row("git-branch", "New branch", "Ctrl+B").key("metric:command.row"),
301                command_row("git-commit", "Commit staged files", "Ctrl+Enter")
302                    .key("command-row-commit"),
303                command_row("refresh-cw", "Refresh repository", "Ctrl+R")
304                    .key("command-row-refresh"),
305                command_row("alert-circle", "Force push", "Danger").key("command-row-force"),
306            ])
307            .width(Size::Fill(1.0)),
308            scroll([form_probe()]).key("form-probe-scroll"),
309        ])
310        .height(Size::Fill(1.0)),
311    ])
312    .key("metric:command.card")
313    .width(Size::Fill(0.8))
314    .height(Size::Fill(1.0))
315}
316
317fn form_probe() -> El {
318    form([
319        form_item([
320            form_label("Valid input"),
321            form_control(
322                text_input(
323                    "Valid input",
324                    &Selection::caret("valid-input", 11),
325                    "valid-input",
326                )
327                .key("metric:form.input"),
328            ),
329            form_description("Default field spacing and helper text."),
330        ]),
331        form_item([
332            form_label("Invalid input"),
333            form_control(
334                text_input(
335                    "Invalid input",
336                    &Selection::caret("invalid-input", 13),
337                    "invalid-input",
338                )
339                .invalid(),
340            ),
341            form_message("This field needs attention."),
342        ]),
343        row([
344            button("Disabled").secondary().disabled(),
345            button("Loading").primary().loading(),
346            spacer(),
347        ]),
348    ])
349    .padding(tokens::SPACE_3)
350    .fill(tokens::MUTED)
351    .stroke(tokens::BORDER)
352    .radius(tokens::RADIUS_MD)
353}
354
355fn icon_cell(label: &'static str) -> El {
356    El::new(Kind::Custom("icon_cell"))
357        .style_profile(StyleProfile::Surface)
358        .text(label)
359        .text_align(TextAlign::Center)
360        .caption()
361        .font_weight(FontWeight::Semibold)
362        .fill(tokens::MUTED)
363        .stroke(tokens::BORDER)
364        .radius(tokens::RADIUS_SM)
365        .width(Size::Fixed(26.0))
366        .height(Size::Fixed(26.0))
367}
More examples
Hide additional examples
examples/palette_demo.rs (line 288)
282fn token_chip(token: TokenDef, palette: &Palette) -> El {
283    let resolved = palette.resolve(token.color);
284    row([
285        El::new(Kind::Custom("palette-swatch"))
286            .at(file!(), line!())
287            .fill(token.color)
288            .stroke(tokens::BORDER)
289            .radius(tokens::RADIUS_SM)
290            .width(Size::Fixed(42.0))
291            .height(Size::Fixed(34.0)),
292        column([
293            text(token.name)
294                .label()
295                .ellipsis()
296                .nowrap_text()
297                .width(Size::Fill(1.0)),
298            mono(rgba_label(resolved)).caption().muted(),
299        ])
300        .gap(0.0)
301        .width(Size::Fill(1.0))
302        .height(Size::Hug),
303    ])
304    .gap(tokens::SPACE_2)
305    .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_2))
306    .align(Align::Center)
307    .fill(tokens::CARD)
308    .stroke(tokens::BORDER)
309    .radius(tokens::RADIUS_MD)
310    .width(Size::Fill(1.0))
311    .height(Size::Fixed(54.0))
312}
313
314fn component_section() -> El {
315    card([
316        card_header([
317            card_title("Stock widgets"),
318            card_description("The same palette applied to regular component constructors."),
319        ]),
320        card_content([
321            row([
322                button("Primary").primary(),
323                button("Secondary").secondary(),
324                button("Outline").outline(),
325                button("Ghost").ghost(),
326            ])
327            .gap(tokens::SPACE_2)
328            .align(Align::Center),
329            row([
330                badge("success").success(),
331                badge("warning").warning(),
332                badge("destructive").destructive(),
333                badge("info").info(),
334                badge("muted").muted(),
335            ])
336            .gap(tokens::SPACE_2)
337            .align(Align::Center),
338            row([
339                text_input("palette search", &Selection::default(), "palette:search")
340                    .width(Size::Fill(1.0)),
341                button_with_icon("settings", "Tune").secondary(),
342            ])
343            .gap(tokens::SPACE_2)
344            .align(Align::Center),
345            row([
346                surface_sample("Card", tokens::CARD),
347                surface_sample("Muted", tokens::MUTED),
348                surface_sample("Popover", tokens::POPOVER),
349            ])
350            .gap(tokens::SPACE_3)
351            .align(Align::Stretch),
352        ])
353        .gap(tokens::SPACE_4),
354    ])
355    .height(Size::Hug)
356}
357
358fn surface_sample(title: &'static str, fill: Color) -> El {
359    column([
360        text(title).label(),
361        text("surface sample").caption().muted(),
362    ])
363    .gap(tokens::SPACE_1)
364    .padding(tokens::SPACE_3)
365    .fill(fill)
366    .stroke(tokens::BORDER)
367    .radius(tokens::RADIUS_MD)
368    .width(Size::Fill(1.0))
369    .height(Size::Fixed(76.0))
370}
examples/settings_calibration.rs (line 81)
41fn settings_sidebar() -> El {
42    column([
43        row([
44            icon_slot("settings"),
45            column([
46                text("Workspace")
47                    .semibold()
48                    .ellipsis()
49                    .width(Size::Fill(1.0)),
50                text("Settings").caption().ellipsis().width(Size::Fill(1.0)),
51            ])
52            .gap(2.0)
53            .width(Size::Fill(1.0))
54            .height(Size::Hug),
55        ])
56        .gap(tokens::SPACE_2)
57        .height(Size::Fixed(44.0))
58        .align(Align::Center),
59        section_label("Personal"),
60        side_item("users", "Profile", false),
61        side_item("settings", "Account", true),
62        side_item("alert-circle", "Security", false),
63        side_item("bell", "Notifications", false),
64        spacer().height(Size::Fixed(tokens::SPACE_4)),
65        section_label("Workspace"),
66        side_item("file-text", "Billing", false),
67        side_item("bar-chart", "Appearance", false),
68        side_item("activity", "Integrations", false),
69        spacer(),
70        column([text("Changes sync after save.").caption().wrap_text()])
71            .padding(tokens::SPACE_2)
72            .fill(tokens::MUTED)
73            .radius(tokens::RADIUS_MD),
74    ])
75    .gap(tokens::SPACE_2)
76    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_2))
77    .key("metric:sidebar")
78    .width(Size::Fixed(244.0))
79    .height(Size::Fill(1.0))
80    .fill(tokens::CARD)
81    .stroke(tokens::BORDER)
82}
83
84fn settings_main() -> El {
85    column([
86        settings_header(),
87        row([settings_nav_card(), settings_body(), settings_aside()])
88            .gap(tokens::SPACE_4)
89            .padding(tokens::SPACE_4)
90            .height(Size::Fill(1.0))
91            .align(Align::Stretch),
92    ])
93    .width(Size::Fill(1.0))
94    .height(Size::Fill(1.0))
95}
96
97fn settings_header() -> El {
98    row([
99        icon_button("menu").ghost(),
100        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
101        h3("Settings").key("metric:page.title"),
102        spacer(),
103        button("Reset").secondary(),
104        button("Save changes").primary(),
105    ])
106    .key("metric:header")
107    .gap(tokens::SPACE_3)
108    .height(Size::Fixed(56.0))
109    .padding(Sides::xy(tokens::SPACE_4, 0.0))
110    .align(Align::Center)
111    .stroke(tokens::BORDER)
112}
113
114fn settings_nav_card() -> El {
115    column([
116        settings_nav_item("Account", true),
117        settings_nav_item("Security", false),
118        settings_nav_item("Notifications", false),
119        settings_nav_item("Appearance", false),
120        settings_nav_item("Billing", false),
121    ])
122    .gap(tokens::SPACE_1)
123    .padding(tokens::SPACE_1)
124    .width(Size::Fixed(220.0))
125    .height(Size::Fill(1.0))
126    .style_profile(StyleProfile::Surface)
127    .surface_role(SurfaceRole::Panel)
128    .fill(tokens::CARD)
129    .stroke(tokens::BORDER)
130    .radius(tokens::RADIUS_MD)
131    .shadow(tokens::SHADOW_MD)
132}
133
134fn settings_nav_item(label: &'static str, selected: bool) -> El {
135    let mut item = row([
136        El::new(Kind::Custom("nav-dot"))
137            .fill(tokens::MUTED_FOREGROUND)
138            .radius(tokens::RADIUS_PILL)
139            .width(Size::Fixed(6.0))
140            .height(Size::Fixed(6.0)),
141        text(label)
142            .font_weight(FontWeight::Medium)
143            .ellipsis()
144            .width(Size::Fill(1.0)),
145    ])
146    .key(if selected {
147        "metric:settings.nav.row".to_string()
148    } else {
149        format!("settings-nav-{label}")
150    })
151    .metrics_role(MetricsRole::ListItem)
152    .align(Align::Center)
153    .focusable();
154
155    if selected {
156        item = item.current();
157    } else {
158        item = item.color(tokens::MUTED_FOREGROUND);
159    }
160
161    item
162}
163
164fn settings_body() -> El {
165    column([
166        column([
167            h1("Account").heading().key("metric:section.title"),
168            text("Manage identity, workspace defaults, and security preferences.")
169                .muted()
170                .wrap_text()
171                .key("metric:page.subtitle"),
172        ])
173        .gap(tokens::SPACE_1)
174        .height(Size::Hug),
175        scroll([profile_card(), preferences_card()])
176            .key("settings-body-scroll")
177            .gap(tokens::SPACE_4)
178            .width(Size::Fill(1.0))
179            .height(Size::Fill(1.0)),
180    ])
181    .gap(tokens::SPACE_4)
182    .width(Size::Fill(1.0))
183    .height(Size::Fill(1.0))
184}
185
186fn profile_card() -> El {
187    card([
188        card_header([
189            card_title("Profile"),
190            card_description("This information appears in audit logs and shared documents."),
191        ]),
192        card_content([form([
193            row([
194                setting_field("Display name", "Alicia Koch", "display-name"),
195                setting_field("Email", "alicia@acme.co", "email"),
196            ])
197            .gap(tokens::SPACE_3),
198            row([
199                setting_select("Role", "Workspace admin", "role"),
200                setting_select("Region", "US East", "region"),
201            ])
202            .gap(tokens::SPACE_3),
203        ])]),
204    ])
205    .key("metric:profile.card")
206}
207
208fn setting_field(label: &'static str, value: &'static str, key: &'static str) -> El {
209    form_item([
210        form_label(label),
211        form_control(
212            text_input(value, &Selection::caret(key, value.len()), key).key(
213                if key == "display-name" {
214                    "metric:form.input"
215                } else {
216                    key
217                },
218            ),
219        ),
220    ])
221    .width(Size::Fill(1.0))
222}
223
224fn setting_select(label: &'static str, value: &'static str, key: &'static str) -> El {
225    form_item([form_label(label), form_control(select_trigger(key, value))]).width(Size::Fill(1.0))
226}
227
228fn preferences_card() -> El {
229    card([
230        card_header([
231            card_title("Preferences"),
232            card_description("Defaults used when creating new dashboards and exports."),
233        ]),
234        card_content([column([
235            preference_row(
236                "Compact navigation",
237                "Use tighter rows in the sidebar and command menus.",
238                switch(true).key("compact-navigation"),
239            ),
240            divider(),
241            preference_row(
242                "Email summaries",
243                "Send a daily digest when documents change.",
244                switch(false).key("email-summaries"),
245            ),
246            divider(),
247            preference_row(
248                "Require approval",
249                "Route external sharing through an owner review.",
250                checkbox(true).key("approval-required"),
251            ),
252        ])
253        .gap(0.0)
254        .width(Size::Fill(1.0))])
255        .padding(0.0),
256    ])
257    .key("metric:preferences.card")
258}
259
260fn preference_row(title: &'static str, description: &'static str, control: El) -> El {
261    row([
262        column([
263            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
264            text(description)
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0))
271        .height(Size::Hug),
272        control,
273    ])
274    .key(if title == "Compact navigation" {
275        "metric:preference.row".to_string()
276    } else {
277        format!("preference-{title}")
278    })
279    .metrics_role(MetricsRole::PreferenceRow)
280    .gap(tokens::SPACE_4)
281    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_3))
282    .align(Align::Center)
283}
284
285fn settings_aside() -> El {
286    column([security_card(), scale_card()])
287        .gap(tokens::SPACE_4)
288        .width(Size::Fixed(300.0))
289        .height(Size::Fill(1.0))
290}
291
292fn security_card() -> El {
293    card([
294        card_header([
295            card_title("Security"),
296            card_description("Two-factor authentication is enabled for all privileged users."),
297        ]),
298        card_content([
299            compact_stat("Passkeys", "2 registered", badge("On").success()),
300            compact_stat("Sessions", "3 active", button("Review").secondary()),
301        ]),
302    ])
303    .width(Size::Fill(1.0))
304}
305
306fn scale_card() -> El {
307    card([
308        card_header([
309            card_title("Interface scale"),
310            card_description("Reference captures keep browser zoom fixed and vary root UI scale."),
311        ]),
312        card_content([
313            row([text("Dense").caption(), spacer(), text("Default").caption()]),
314            slider(0.66, tokens::PRIMARY)
315                .key("interface-scale")
316                .width(Size::Fill(1.0)),
317        ]),
318    ])
319    .width(Size::Fill(1.0))
320}
321
322fn compact_stat(title: &'static str, detail: &'static str, control: El) -> El {
323    row([
324        column([
325            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
326            text(detail).caption().ellipsis().width(Size::Fill(1.0)),
327        ])
328        .gap(2.0)
329        .width(Size::Fill(1.0))
330        .height(Size::Hug),
331        control,
332    ])
333    .gap(tokens::SPACE_2)
334    .height(Size::Fixed(44.0))
335    .align(Align::Center)
336}
337
338fn section_label(label: &'static str) -> El {
339    text(label)
340        .caption()
341        .height(Size::Fixed(22.0))
342        .padding(Sides::xy(tokens::SPACE_2, 0.0))
343}
344
345fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
346    let mut item = row([
347        icon(icon_name)
348            .color(tokens::MUTED_FOREGROUND)
349            .icon_size(tokens::ICON_SM)
350            .width(Size::Fixed(tokens::ICON_SM)),
351        text(label)
352            .font_weight(FontWeight::Medium)
353            .ellipsis()
354            .width(Size::Fill(1.0)),
355    ])
356    .key(if selected {
357        "metric:sidebar.nav.row".to_string()
358    } else {
359        format!("side-item-{label}")
360    })
361    .metrics_role(MetricsRole::ListItem)
362    .gap(tokens::SPACE_2)
363    .padding(Sides::xy(tokens::SPACE_2, 0.0))
364    .height(Size::Fixed(32.0))
365    .align(Align::Center)
366    .focusable();
367
368    if selected {
369        item = item.current();
370    } else {
371        item = item.color(tokens::MUTED_FOREGROUND);
372    }
373
374    item
375}
376
377fn icon_slot(icon_name: &'static str) -> El {
378    El::new(Kind::Custom("icon_cell"))
379        .style_profile(StyleProfile::Surface)
380        .child(
381            icon(icon_name)
382                .color(tokens::FOREGROUND)
383                .icon_size(tokens::ICON_XS),
384        )
385        .align(Align::Center)
386        .justify(Justify::Center)
387        .fill(tokens::MUTED)
388        .stroke(tokens::BORDER)
389        .radius(tokens::RADIUS_SM)
390        .width(Size::Fixed(30.0))
391        .height(Size::Fixed(30.0))
392}
examples/dashboard_01_calibration.rs (line 99)
41fn dashboard_sidebar() -> El {
42    column([
43        row([
44            icon_cell("A"),
45            column([
46                text("Acme Inc.")
47                    .semibold()
48                    .ellipsis()
49                    .width(Size::Fill(1.0)),
50                text("Enterprise")
51                    .caption()
52                    .ellipsis()
53                    .width(Size::Fill(1.0)),
54            ])
55            .gap(2.0)
56            .width(Size::Fill(1.0))
57            .height(Size::Hug),
58        ])
59        .gap(tokens::SPACE_2)
60        .height(Size::Fixed(44.0))
61        .align(Align::Center),
62        section_label("Platform"),
63        side_item("layout-dashboard", "Dashboard", true),
64        side_item("activity", "Lifecycle", false),
65        side_item("bar-chart", "Analytics", false),
66        side_item("folder", "Projects", false),
67        spacer().height(Size::Fixed(tokens::SPACE_4)),
68        section_label("Documents"),
69        side_item("file-text", "Data library", false),
70        side_item("download", "Reports", false),
71        side_item("users", "Team", false),
72        spacer(),
73        row([
74            icon_cell("AK"),
75            column([
76                text("Alicia Koch")
77                    .semibold()
78                    .ellipsis()
79                    .width(Size::Fill(1.0)),
80                text("alicia@example.com")
81                    .caption()
82                    .ellipsis()
83                    .width(Size::Fill(1.0)),
84            ])
85            .gap(2.0)
86            .width(Size::Fill(1.0))
87            .height(Size::Hug),
88        ])
89        .gap(tokens::SPACE_2)
90        .height(Size::Fixed(50.0))
91        .align(Align::Center),
92    ])
93    .gap(tokens::SPACE_2)
94    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_2))
95    .key("metric:sidebar")
96    .width(Size::Fixed(244.0))
97    .height(Size::Fill(1.0))
98    .fill(tokens::CARD)
99    .stroke(tokens::BORDER)
100}
101
102fn section_label(label: &'static str) -> El {
103    text(label)
104        .caption()
105        .height(Size::Fixed(22.0))
106        .padding(Sides::xy(tokens::SPACE_2, 0.0))
107}
108
109fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
110    let mut item = row([
111        icon(icon_name)
112            .color(tokens::MUTED_FOREGROUND)
113            .icon_size(tokens::ICON_SM)
114            .width(Size::Fixed(tokens::ICON_SM)),
115        text(label)
116            .font_weight(FontWeight::Medium)
117            .ellipsis()
118            .width(Size::Fill(1.0)),
119    ])
120    .key(if selected {
121        "metric:sidebar.nav.row".to_string()
122    } else {
123        format!("side-item-{label}")
124    })
125    .metrics_role(MetricsRole::ListItem)
126    .gap(tokens::SPACE_2)
127    .padding(Sides::xy(tokens::SPACE_2, 0.0))
128    .height(Size::Fixed(32.0))
129    .align(Align::Center)
130    .focusable();
131
132    if selected {
133        item = item.current();
134    } else {
135        item = item.color(tokens::MUTED_FOREGROUND);
136    }
137
138    item
139}
140
141fn dashboard_main() -> El {
142    column([
143        dashboard_header(),
144        column([
145            row([
146                metric_card(
147                    "bar-chart",
148                    "Total Revenue",
149                    "$1,250.00",
150                    "+12.5%",
151                    "Trending up this month",
152                    true,
153                ),
154                metric_card(
155                    "users",
156                    "New Customers",
157                    "1,234",
158                    "-20%",
159                    "Acquisition needs attention",
160                    false,
161                ),
162                metric_card(
163                    "folder",
164                    "Active Accounts",
165                    "45,678",
166                    "+12.5%",
167                    "Strong user retention",
168                    true,
169                ),
170                metric_card(
171                    "activity",
172                    "Growth Rate",
173                    "4.5%",
174                    "+4.5%",
175                    "Meets growth projections",
176                    true,
177                ),
178            ])
179            .gap(tokens::SPACE_4),
180            row([chart_card(), sales_card()])
181                .gap(tokens::SPACE_4)
182                .height(Size::Fixed(306.0))
183                .align(Align::Stretch),
184            documents_card(),
185        ])
186        .gap(tokens::SPACE_4)
187        .padding(tokens::SPACE_7)
188        .height(Size::Fill(1.0)),
189    ])
190    .width(Size::Fill(1.0))
191    .height(Size::Fill(1.0))
192}
193
194fn dashboard_header() -> El {
195    row([
196        icon_button("menu").ghost(),
197        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
198        h3("Documents").key("metric:page.title"),
199        spacer(),
200        text_input("Search...", &Selection::default(), "dashboard-search")
201            .key("metric:command.input")
202            .width(Size::Fixed(260.0)),
203        icon_button("plus").ghost(),
204        icon_button("bell").ghost(),
205    ])
206    .key("metric:header")
207    .gap(tokens::SPACE_3)
208    .height(Size::Fixed(56.0))
209    .padding(Sides::xy(tokens::SPACE_4, 0.0))
210    .align(Align::Center)
211    .stroke(tokens::BORDER)
212}
examples/scrollbar.rs (line 46)
35fn fixture() -> El {
36    column([
37        h2("Scrollbar"),
38        text("scroll() and virtual_list() show a draggable thumb by default.").muted(),
39        row([
40            // 1) scroll() — default-on scrollbar.
41            column([
42                text("scroll() — default").bold(),
43                scroll(list_rows())
44                    .height(Size::Fixed(240.0))
45                    .padding(tokens::SPACE_2)
46                    .stroke(tokens::BORDER)
47                    .stroke_width(1.0)
48                    .radius(tokens::RADIUS_MD),
49            ])
50            .gap(tokens::SPACE_2)
51            .width(Size::Fill(1.0))
52            .height(Size::Hug),
53            // 2) virtual_list — thumb scales to content size.
54            column([
55                text("virtual_list(200, 28)").bold(),
56                virtual_list(200, 28.0, |i| {
57                    row([
58                        text(format!("{i:03}")).mono().muted(),
59                        text(format!("row {i}")),
60                    ])
61                    .gap(tokens::SPACE_2)
62                    .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_1))
63                    .height(Size::Fixed(28.0))
64                    .align(Align::Center)
65                })
66                .height(Size::Fixed(240.0))
67                .padding(tokens::SPACE_2)
68                .stroke(tokens::BORDER)
69                .stroke_width(1.0)
70                .radius(tokens::RADIUS_MD),
71            ])
72            .gap(tokens::SPACE_2)
73            .width(Size::Fill(1.0))
74            .height(Size::Hug),
75            // 3) Opt-out: same content, no thumb.
76            column([
77                text("scroll().no_scrollbar()").bold(),
78                scroll(list_rows())
79                    .no_scrollbar()
80                    .height(Size::Fixed(240.0))
81                    .padding(tokens::SPACE_2)
82                    .stroke(tokens::BORDER)
83                    .stroke_width(1.0)
84                    .radius(tokens::RADIUS_MD),
85            ])
86            .gap(tokens::SPACE_2)
87            .width(Size::Fill(1.0))
88            .height(Size::Hug),
89        ])
90        .gap(tokens::SPACE_4)
91        .width(Size::Fill(1.0)),
92    ])
93    .gap(tokens::SPACE_4)
94    .padding(tokens::SPACE_7)
95}
Source

pub fn stroke_width(self, w: f32) -> Self

Examples found in repository?
examples/scrollbar.rs (line 47)
35fn fixture() -> El {
36    column([
37        h2("Scrollbar"),
38        text("scroll() and virtual_list() show a draggable thumb by default.").muted(),
39        row([
40            // 1) scroll() — default-on scrollbar.
41            column([
42                text("scroll() — default").bold(),
43                scroll(list_rows())
44                    .height(Size::Fixed(240.0))
45                    .padding(tokens::SPACE_2)
46                    .stroke(tokens::BORDER)
47                    .stroke_width(1.0)
48                    .radius(tokens::RADIUS_MD),
49            ])
50            .gap(tokens::SPACE_2)
51            .width(Size::Fill(1.0))
52            .height(Size::Hug),
53            // 2) virtual_list — thumb scales to content size.
54            column([
55                text("virtual_list(200, 28)").bold(),
56                virtual_list(200, 28.0, |i| {
57                    row([
58                        text(format!("{i:03}")).mono().muted(),
59                        text(format!("row {i}")),
60                    ])
61                    .gap(tokens::SPACE_2)
62                    .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_1))
63                    .height(Size::Fixed(28.0))
64                    .align(Align::Center)
65                })
66                .height(Size::Fixed(240.0))
67                .padding(tokens::SPACE_2)
68                .stroke(tokens::BORDER)
69                .stroke_width(1.0)
70                .radius(tokens::RADIUS_MD),
71            ])
72            .gap(tokens::SPACE_2)
73            .width(Size::Fill(1.0))
74            .height(Size::Hug),
75            // 3) Opt-out: same content, no thumb.
76            column([
77                text("scroll().no_scrollbar()").bold(),
78                scroll(list_rows())
79                    .no_scrollbar()
80                    .height(Size::Fixed(240.0))
81                    .padding(tokens::SPACE_2)
82                    .stroke(tokens::BORDER)
83                    .stroke_width(1.0)
84                    .radius(tokens::RADIUS_MD),
85            ])
86            .gap(tokens::SPACE_2)
87            .width(Size::Fill(1.0))
88            .height(Size::Hug),
89        ])
90        .gap(tokens::SPACE_4)
91        .width(Size::Fill(1.0)),
92    ])
93    .gap(tokens::SPACE_4)
94    .padding(tokens::SPACE_7)
95}
Source

pub fn radius(self, r: impl Into<Corners>) -> Self

Set the element’s corner radii. A scalar (e.g. .radius(tokens::RADIUS_MD)) sets all four corners uniformly via Corners::from; pass Corners::top / Corners::bottom / Corners::left / Corners::right, or a directly-built Corners, to round only a subset of corners.

Examples found in repository?
examples/dashboard_01_calibration.rs (line 306)
303fn bar(height: f32, color: Color) -> El {
304    El::new(Kind::Custom("chart_bar"))
305        .fill(color)
306        .radius(tokens::RADIUS_SM)
307        .width(Size::Fill(1.0))
308        .height(Size::Fixed(height))
309}
310
311fn sales_card() -> El {
312    card([
313        card_header([
314            card_title("Recent Sales"),
315            card_description("You made 265 sales this month."),
316        ])
317        .padding(tokens::SPACE_4),
318        card_content([
319            sale_row("OM", "Olivia Martin", "olivia@example.com", "+$1,999.00"),
320            sale_row("JL", "Jackson Lee", "jackson@example.com", "+$39.00"),
321            sale_row("IN", "Isabella Nguyen", "isabella@example.com", "+$299.00"),
322            sale_row("WK", "William Kim", "will@example.com", "+$99.00"),
323        ])
324        .gap(tokens::SPACE_2)
325        .padding(Sides {
326            left: tokens::SPACE_4,
327            right: tokens::SPACE_4,
328            top: 0.0,
329            bottom: tokens::SPACE_4,
330        }),
331    ])
332    .key("metric:sales.card")
333    .width(Size::Fixed(330.0))
334    .height(Size::Fill(1.0))
335}
336
337fn sale_row(
338    initials: &'static str,
339    name: &'static str,
340    email: &'static str,
341    amount: &'static str,
342) -> El {
343    row([
344        icon_cell(initials),
345        column([
346            text(name).semibold().ellipsis().width(Size::Fill(1.0)),
347            text(email).caption().ellipsis().width(Size::Fill(1.0)),
348        ])
349        .gap(2.0)
350        .height(Size::Hug)
351        .width(Size::Fill(1.0)),
352        text(amount).label().small(),
353    ])
354    .gap(tokens::SPACE_2)
355    .height(Size::Fixed(42.0))
356    .align(Align::Center)
357}
358
359fn documents_card() -> El {
360    card([
361        card_header([card_title("Documents")]).padding(tokens::SPACE_4),
362        card_content([scroll([table([
363            table_header([table_row([
364                table_head("").width(Size::Fixed(35.0)),
365                table_head("Header").width(Size::Fill(1.8)),
366                table_head("Section Type").width(Size::Fill(1.0)),
367                table_head("Status").width(Size::Fixed(104.0)),
368                table_head("Target").width(Size::Fixed(64.0)),
369                table_head("Limit").width(Size::Fixed(64.0)),
370                table_head("Reviewer").width(Size::Fixed(128.0)),
371                table_head("").width(Size::Fixed(32.0)),
372            ])
373            .padding(Sides::xy(tokens::SPACE_4, 0.0))
374            .key("metric:table.header")]),
375            divider(),
376            table_body([
377                document_row(
378                    "Cover page",
379                    "Cover page",
380                    "In Process",
381                    "18",
382                    "5",
383                    "Eddie Lake",
384                    "info",
385                ),
386                document_row(
387                    "Table of contents",
388                    "Table of contents",
389                    "Done",
390                    "29",
391                    "24",
392                    "Eddie Lake",
393                    "success",
394                ),
395            ]),
396        ])])
397        .height(Size::Fill(1.0))])
398        .gap(0.0)
399        .padding(0.0)
400        .height(Size::Fill(1.0)),
401    ])
402    .key("metric:table.card")
403    .height(Size::Fill(1.0))
404}
405
406fn document_row(
407    header: &'static str,
408    section: &'static str,
409    status: &'static str,
410    target: &'static str,
411    limit: &'static str,
412    reviewer: &'static str,
413    tone: &'static str,
414) -> El {
415    let status_badge = match tone {
416        "success" => badge(status).success(),
417        _ => badge(status).info(),
418    };
419    table_row([
420        table_utility_cell("::"),
421        table_cell(text(header).label().small()).width(Size::Fill(1.8)),
422        table_cell(text(section).muted()).width(Size::Fill(1.0)),
423        table_cell(status_badge).width(Size::Fixed(104.0)),
424        table_cell(text(target).label().small()).width(Size::Fixed(64.0)),
425        table_cell(text(limit).label().small()).width(Size::Fixed(64.0)),
426        table_cell(text(reviewer).muted()).width(Size::Fixed(128.0)),
427        table_action_cell(),
428    ])
429    .padding(Sides::xy(tokens::SPACE_4, 0.0))
430    .key(if header == "Cover page" {
431        "metric:table.row"
432    } else {
433        header
434    })
435}
436
437fn table_utility_cell(label: &'static str) -> El {
438    table_cell(text(label).muted().center_text()).width(Size::Fixed(35.0))
439}
440
441fn table_action_cell() -> El {
442    stack([icon("more-horizontal")
443        .icon_size(tokens::ICON_SM)
444        .color(tokens::MUTED_FOREGROUND)])
445    .align(Align::Center)
446    .justify(Justify::Center)
447    .width(Size::Fixed(32.0))
448    .height(Size::Hug)
449}
450
451fn icon_cell(label: &'static str) -> El {
452    El::new(Kind::Custom("icon_cell"))
453        .style_profile(StyleProfile::Surface)
454        .text(label)
455        .text_align(TextAlign::Center)
456        .caption()
457        .font_weight(FontWeight::Semibold)
458        .fill(tokens::MUTED)
459        .radius(tokens::RADIUS_SM)
460        .width(Size::Fixed(30.0))
461        .height(Size::Fixed(30.0))
462}
More examples
Hide additional examples
examples/palette_demo.rs (line 289)
282fn token_chip(token: TokenDef, palette: &Palette) -> El {
283    let resolved = palette.resolve(token.color);
284    row([
285        El::new(Kind::Custom("palette-swatch"))
286            .at(file!(), line!())
287            .fill(token.color)
288            .stroke(tokens::BORDER)
289            .radius(tokens::RADIUS_SM)
290            .width(Size::Fixed(42.0))
291            .height(Size::Fixed(34.0)),
292        column([
293            text(token.name)
294                .label()
295                .ellipsis()
296                .nowrap_text()
297                .width(Size::Fill(1.0)),
298            mono(rgba_label(resolved)).caption().muted(),
299        ])
300        .gap(0.0)
301        .width(Size::Fill(1.0))
302        .height(Size::Hug),
303    ])
304    .gap(tokens::SPACE_2)
305    .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_2))
306    .align(Align::Center)
307    .fill(tokens::CARD)
308    .stroke(tokens::BORDER)
309    .radius(tokens::RADIUS_MD)
310    .width(Size::Fill(1.0))
311    .height(Size::Fixed(54.0))
312}
313
314fn component_section() -> El {
315    card([
316        card_header([
317            card_title("Stock widgets"),
318            card_description("The same palette applied to regular component constructors."),
319        ]),
320        card_content([
321            row([
322                button("Primary").primary(),
323                button("Secondary").secondary(),
324                button("Outline").outline(),
325                button("Ghost").ghost(),
326            ])
327            .gap(tokens::SPACE_2)
328            .align(Align::Center),
329            row([
330                badge("success").success(),
331                badge("warning").warning(),
332                badge("destructive").destructive(),
333                badge("info").info(),
334                badge("muted").muted(),
335            ])
336            .gap(tokens::SPACE_2)
337            .align(Align::Center),
338            row([
339                text_input("palette search", &Selection::default(), "palette:search")
340                    .width(Size::Fill(1.0)),
341                button_with_icon("settings", "Tune").secondary(),
342            ])
343            .gap(tokens::SPACE_2)
344            .align(Align::Center),
345            row([
346                surface_sample("Card", tokens::CARD),
347                surface_sample("Muted", tokens::MUTED),
348                surface_sample("Popover", tokens::POPOVER),
349            ])
350            .gap(tokens::SPACE_3)
351            .align(Align::Stretch),
352        ])
353        .gap(tokens::SPACE_4),
354    ])
355    .height(Size::Hug)
356}
357
358fn surface_sample(title: &'static str, fill: Color) -> El {
359    column([
360        text(title).label(),
361        text("surface sample").caption().muted(),
362    ])
363    .gap(tokens::SPACE_1)
364    .padding(tokens::SPACE_3)
365    .fill(fill)
366    .stroke(tokens::BORDER)
367    .radius(tokens::RADIUS_MD)
368    .width(Size::Fill(1.0))
369    .height(Size::Fixed(76.0))
370}
examples/polish_calibration.rs (line 352)
317fn form_probe() -> El {
318    form([
319        form_item([
320            form_label("Valid input"),
321            form_control(
322                text_input(
323                    "Valid input",
324                    &Selection::caret("valid-input", 11),
325                    "valid-input",
326                )
327                .key("metric:form.input"),
328            ),
329            form_description("Default field spacing and helper text."),
330        ]),
331        form_item([
332            form_label("Invalid input"),
333            form_control(
334                text_input(
335                    "Invalid input",
336                    &Selection::caret("invalid-input", 13),
337                    "invalid-input",
338                )
339                .invalid(),
340            ),
341            form_message("This field needs attention."),
342        ]),
343        row([
344            button("Disabled").secondary().disabled(),
345            button("Loading").primary().loading(),
346            spacer(),
347        ]),
348    ])
349    .padding(tokens::SPACE_3)
350    .fill(tokens::MUTED)
351    .stroke(tokens::BORDER)
352    .radius(tokens::RADIUS_MD)
353}
354
355fn icon_cell(label: &'static str) -> El {
356    El::new(Kind::Custom("icon_cell"))
357        .style_profile(StyleProfile::Surface)
358        .text(label)
359        .text_align(TextAlign::Center)
360        .caption()
361        .font_weight(FontWeight::Semibold)
362        .fill(tokens::MUTED)
363        .stroke(tokens::BORDER)
364        .radius(tokens::RADIUS_SM)
365        .width(Size::Fixed(26.0))
366        .height(Size::Fixed(26.0))
367}
examples/settings_calibration.rs (line 73)
41fn settings_sidebar() -> El {
42    column([
43        row([
44            icon_slot("settings"),
45            column([
46                text("Workspace")
47                    .semibold()
48                    .ellipsis()
49                    .width(Size::Fill(1.0)),
50                text("Settings").caption().ellipsis().width(Size::Fill(1.0)),
51            ])
52            .gap(2.0)
53            .width(Size::Fill(1.0))
54            .height(Size::Hug),
55        ])
56        .gap(tokens::SPACE_2)
57        .height(Size::Fixed(44.0))
58        .align(Align::Center),
59        section_label("Personal"),
60        side_item("users", "Profile", false),
61        side_item("settings", "Account", true),
62        side_item("alert-circle", "Security", false),
63        side_item("bell", "Notifications", false),
64        spacer().height(Size::Fixed(tokens::SPACE_4)),
65        section_label("Workspace"),
66        side_item("file-text", "Billing", false),
67        side_item("bar-chart", "Appearance", false),
68        side_item("activity", "Integrations", false),
69        spacer(),
70        column([text("Changes sync after save.").caption().wrap_text()])
71            .padding(tokens::SPACE_2)
72            .fill(tokens::MUTED)
73            .radius(tokens::RADIUS_MD),
74    ])
75    .gap(tokens::SPACE_2)
76    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_2))
77    .key("metric:sidebar")
78    .width(Size::Fixed(244.0))
79    .height(Size::Fill(1.0))
80    .fill(tokens::CARD)
81    .stroke(tokens::BORDER)
82}
83
84fn settings_main() -> El {
85    column([
86        settings_header(),
87        row([settings_nav_card(), settings_body(), settings_aside()])
88            .gap(tokens::SPACE_4)
89            .padding(tokens::SPACE_4)
90            .height(Size::Fill(1.0))
91            .align(Align::Stretch),
92    ])
93    .width(Size::Fill(1.0))
94    .height(Size::Fill(1.0))
95}
96
97fn settings_header() -> El {
98    row([
99        icon_button("menu").ghost(),
100        divider().width(Size::Fixed(1.0)).height(Size::Fixed(22.0)),
101        h3("Settings").key("metric:page.title"),
102        spacer(),
103        button("Reset").secondary(),
104        button("Save changes").primary(),
105    ])
106    .key("metric:header")
107    .gap(tokens::SPACE_3)
108    .height(Size::Fixed(56.0))
109    .padding(Sides::xy(tokens::SPACE_4, 0.0))
110    .align(Align::Center)
111    .stroke(tokens::BORDER)
112}
113
114fn settings_nav_card() -> El {
115    column([
116        settings_nav_item("Account", true),
117        settings_nav_item("Security", false),
118        settings_nav_item("Notifications", false),
119        settings_nav_item("Appearance", false),
120        settings_nav_item("Billing", false),
121    ])
122    .gap(tokens::SPACE_1)
123    .padding(tokens::SPACE_1)
124    .width(Size::Fixed(220.0))
125    .height(Size::Fill(1.0))
126    .style_profile(StyleProfile::Surface)
127    .surface_role(SurfaceRole::Panel)
128    .fill(tokens::CARD)
129    .stroke(tokens::BORDER)
130    .radius(tokens::RADIUS_MD)
131    .shadow(tokens::SHADOW_MD)
132}
133
134fn settings_nav_item(label: &'static str, selected: bool) -> El {
135    let mut item = row([
136        El::new(Kind::Custom("nav-dot"))
137            .fill(tokens::MUTED_FOREGROUND)
138            .radius(tokens::RADIUS_PILL)
139            .width(Size::Fixed(6.0))
140            .height(Size::Fixed(6.0)),
141        text(label)
142            .font_weight(FontWeight::Medium)
143            .ellipsis()
144            .width(Size::Fill(1.0)),
145    ])
146    .key(if selected {
147        "metric:settings.nav.row".to_string()
148    } else {
149        format!("settings-nav-{label}")
150    })
151    .metrics_role(MetricsRole::ListItem)
152    .align(Align::Center)
153    .focusable();
154
155    if selected {
156        item = item.current();
157    } else {
158        item = item.color(tokens::MUTED_FOREGROUND);
159    }
160
161    item
162}
163
164fn settings_body() -> El {
165    column([
166        column([
167            h1("Account").heading().key("metric:section.title"),
168            text("Manage identity, workspace defaults, and security preferences.")
169                .muted()
170                .wrap_text()
171                .key("metric:page.subtitle"),
172        ])
173        .gap(tokens::SPACE_1)
174        .height(Size::Hug),
175        scroll([profile_card(), preferences_card()])
176            .key("settings-body-scroll")
177            .gap(tokens::SPACE_4)
178            .width(Size::Fill(1.0))
179            .height(Size::Fill(1.0)),
180    ])
181    .gap(tokens::SPACE_4)
182    .width(Size::Fill(1.0))
183    .height(Size::Fill(1.0))
184}
185
186fn profile_card() -> El {
187    card([
188        card_header([
189            card_title("Profile"),
190            card_description("This information appears in audit logs and shared documents."),
191        ]),
192        card_content([form([
193            row([
194                setting_field("Display name", "Alicia Koch", "display-name"),
195                setting_field("Email", "alicia@acme.co", "email"),
196            ])
197            .gap(tokens::SPACE_3),
198            row([
199                setting_select("Role", "Workspace admin", "role"),
200                setting_select("Region", "US East", "region"),
201            ])
202            .gap(tokens::SPACE_3),
203        ])]),
204    ])
205    .key("metric:profile.card")
206}
207
208fn setting_field(label: &'static str, value: &'static str, key: &'static str) -> El {
209    form_item([
210        form_label(label),
211        form_control(
212            text_input(value, &Selection::caret(key, value.len()), key).key(
213                if key == "display-name" {
214                    "metric:form.input"
215                } else {
216                    key
217                },
218            ),
219        ),
220    ])
221    .width(Size::Fill(1.0))
222}
223
224fn setting_select(label: &'static str, value: &'static str, key: &'static str) -> El {
225    form_item([form_label(label), form_control(select_trigger(key, value))]).width(Size::Fill(1.0))
226}
227
228fn preferences_card() -> El {
229    card([
230        card_header([
231            card_title("Preferences"),
232            card_description("Defaults used when creating new dashboards and exports."),
233        ]),
234        card_content([column([
235            preference_row(
236                "Compact navigation",
237                "Use tighter rows in the sidebar and command menus.",
238                switch(true).key("compact-navigation"),
239            ),
240            divider(),
241            preference_row(
242                "Email summaries",
243                "Send a daily digest when documents change.",
244                switch(false).key("email-summaries"),
245            ),
246            divider(),
247            preference_row(
248                "Require approval",
249                "Route external sharing through an owner review.",
250                checkbox(true).key("approval-required"),
251            ),
252        ])
253        .gap(0.0)
254        .width(Size::Fill(1.0))])
255        .padding(0.0),
256    ])
257    .key("metric:preferences.card")
258}
259
260fn preference_row(title: &'static str, description: &'static str, control: El) -> El {
261    row([
262        column([
263            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
264            text(description)
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0))
271        .height(Size::Hug),
272        control,
273    ])
274    .key(if title == "Compact navigation" {
275        "metric:preference.row".to_string()
276    } else {
277        format!("preference-{title}")
278    })
279    .metrics_role(MetricsRole::PreferenceRow)
280    .gap(tokens::SPACE_4)
281    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_3))
282    .align(Align::Center)
283}
284
285fn settings_aside() -> El {
286    column([security_card(), scale_card()])
287        .gap(tokens::SPACE_4)
288        .width(Size::Fixed(300.0))
289        .height(Size::Fill(1.0))
290}
291
292fn security_card() -> El {
293    card([
294        card_header([
295            card_title("Security"),
296            card_description("Two-factor authentication is enabled for all privileged users."),
297        ]),
298        card_content([
299            compact_stat("Passkeys", "2 registered", badge("On").success()),
300            compact_stat("Sessions", "3 active", button("Review").secondary()),
301        ]),
302    ])
303    .width(Size::Fill(1.0))
304}
305
306fn scale_card() -> El {
307    card([
308        card_header([
309            card_title("Interface scale"),
310            card_description("Reference captures keep browser zoom fixed and vary root UI scale."),
311        ]),
312        card_content([
313            row([text("Dense").caption(), spacer(), text("Default").caption()]),
314            slider(0.66, tokens::PRIMARY)
315                .key("interface-scale")
316                .width(Size::Fill(1.0)),
317        ]),
318    ])
319    .width(Size::Fill(1.0))
320}
321
322fn compact_stat(title: &'static str, detail: &'static str, control: El) -> El {
323    row([
324        column([
325            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
326            text(detail).caption().ellipsis().width(Size::Fill(1.0)),
327        ])
328        .gap(2.0)
329        .width(Size::Fill(1.0))
330        .height(Size::Hug),
331        control,
332    ])
333    .gap(tokens::SPACE_2)
334    .height(Size::Fixed(44.0))
335    .align(Align::Center)
336}
337
338fn section_label(label: &'static str) -> El {
339    text(label)
340        .caption()
341        .height(Size::Fixed(22.0))
342        .padding(Sides::xy(tokens::SPACE_2, 0.0))
343}
344
345fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
346    let mut item = row([
347        icon(icon_name)
348            .color(tokens::MUTED_FOREGROUND)
349            .icon_size(tokens::ICON_SM)
350            .width(Size::Fixed(tokens::ICON_SM)),
351        text(label)
352            .font_weight(FontWeight::Medium)
353            .ellipsis()
354            .width(Size::Fill(1.0)),
355    ])
356    .key(if selected {
357        "metric:sidebar.nav.row".to_string()
358    } else {
359        format!("side-item-{label}")
360    })
361    .metrics_role(MetricsRole::ListItem)
362    .gap(tokens::SPACE_2)
363    .padding(Sides::xy(tokens::SPACE_2, 0.0))
364    .height(Size::Fixed(32.0))
365    .align(Align::Center)
366    .focusable();
367
368    if selected {
369        item = item.current();
370    } else {
371        item = item.color(tokens::MUTED_FOREGROUND);
372    }
373
374    item
375}
376
377fn icon_slot(icon_name: &'static str) -> El {
378    El::new(Kind::Custom("icon_cell"))
379        .style_profile(StyleProfile::Surface)
380        .child(
381            icon(icon_name)
382                .color(tokens::FOREGROUND)
383                .icon_size(tokens::ICON_XS),
384        )
385        .align(Align::Center)
386        .justify(Justify::Center)
387        .fill(tokens::MUTED)
388        .stroke(tokens::BORDER)
389        .radius(tokens::RADIUS_SM)
390        .width(Size::Fixed(30.0))
391        .height(Size::Fixed(30.0))
392}
examples/scrollbar.rs (line 48)
35fn fixture() -> El {
36    column([
37        h2("Scrollbar"),
38        text("scroll() and virtual_list() show a draggable thumb by default.").muted(),
39        row([
40            // 1) scroll() — default-on scrollbar.
41            column([
42                text("scroll() — default").bold(),
43                scroll(list_rows())
44                    .height(Size::Fixed(240.0))
45                    .padding(tokens::SPACE_2)
46                    .stroke(tokens::BORDER)
47                    .stroke_width(1.0)
48                    .radius(tokens::RADIUS_MD),
49            ])
50            .gap(tokens::SPACE_2)
51            .width(Size::Fill(1.0))
52            .height(Size::Hug),
53            // 2) virtual_list — thumb scales to content size.
54            column([
55                text("virtual_list(200, 28)").bold(),
56                virtual_list(200, 28.0, |i| {
57                    row([
58                        text(format!("{i:03}")).mono().muted(),
59                        text(format!("row {i}")),
60                    ])
61                    .gap(tokens::SPACE_2)
62                    .padding(Sides::xy(tokens::SPACE_2, tokens::SPACE_1))
63                    .height(Size::Fixed(28.0))
64                    .align(Align::Center)
65                })
66                .height(Size::Fixed(240.0))
67                .padding(tokens::SPACE_2)
68                .stroke(tokens::BORDER)
69                .stroke_width(1.0)
70                .radius(tokens::RADIUS_MD),
71            ])
72            .gap(tokens::SPACE_2)
73            .width(Size::Fill(1.0))
74            .height(Size::Hug),
75            // 3) Opt-out: same content, no thumb.
76            column([
77                text("scroll().no_scrollbar()").bold(),
78                scroll(list_rows())
79                    .no_scrollbar()
80                    .height(Size::Fixed(240.0))
81                    .padding(tokens::SPACE_2)
82                    .stroke(tokens::BORDER)
83                    .stroke_width(1.0)
84                    .radius(tokens::RADIUS_MD),
85            ])
86            .gap(tokens::SPACE_2)
87            .width(Size::Fill(1.0))
88            .height(Size::Hug),
89        ])
90        .gap(tokens::SPACE_4)
91        .width(Size::Fill(1.0)),
92    ])
93    .gap(tokens::SPACE_4)
94    .padding(tokens::SPACE_7)
95}
Source

pub fn shadow(self, s: f32) -> Self

Examples found in repository?
examples/settings_calibration.rs (line 131)
114fn settings_nav_card() -> El {
115    column([
116        settings_nav_item("Account", true),
117        settings_nav_item("Security", false),
118        settings_nav_item("Notifications", false),
119        settings_nav_item("Appearance", false),
120        settings_nav_item("Billing", false),
121    ])
122    .gap(tokens::SPACE_1)
123    .padding(tokens::SPACE_1)
124    .width(Size::Fixed(220.0))
125    .height(Size::Fill(1.0))
126    .style_profile(StyleProfile::Surface)
127    .surface_role(SurfaceRole::Panel)
128    .fill(tokens::CARD)
129    .stroke(tokens::BORDER)
130    .radius(tokens::RADIUS_MD)
131    .shadow(tokens::SHADOW_MD)
132}
Source

pub fn surface_role(self, role: SurfaceRole) -> Self

Tag this node with a semantic SurfaceRole so the theme can route it through the appropriate paint recipe. Most app code should not call this directly: the catalog widgets (card(), sidebar(), dialog(), popover(), tabs_list(), etc.) set the right role and the matching fill / stroke / radius / shadow together, while the .selected() and .current() chainables wrap the corresponding state recipes.

Reach for the raw chainable when authoring a new widget or when composing a custom container that the catalog doesn’t cover — and remember that decorative roles (Panel, Raised, Popover, Danger) require you to supply a fill yourself; see the SurfaceRole doc for the per-variant contract. The bundle lint pass flags Panel without a fill as crate::bundle::lint::FindingKind::MissingSurfaceFill.

Examples found in repository?
examples/settings_calibration.rs (line 127)
114fn settings_nav_card() -> El {
115    column([
116        settings_nav_item("Account", true),
117        settings_nav_item("Security", false),
118        settings_nav_item("Notifications", false),
119        settings_nav_item("Appearance", false),
120        settings_nav_item("Billing", false),
121    ])
122    .gap(tokens::SPACE_1)
123    .padding(tokens::SPACE_1)
124    .width(Size::Fixed(220.0))
125    .height(Size::Fill(1.0))
126    .style_profile(StyleProfile::Surface)
127    .surface_role(SurfaceRole::Panel)
128    .fill(tokens::CARD)
129    .stroke(tokens::BORDER)
130    .radius(tokens::RADIUS_MD)
131    .shadow(tokens::SHADOW_MD)
132}
Source

pub fn paint_overflow(self, outset: impl Into<Sides>) -> Self

Permit paint to extend beyond this element’s layout bounds by outset on each side. Layout-neutral; siblings don’t move and hit-testing still uses the layout rect.

Source

pub fn focus_ring_inside(self) -> Self

Draw the stock focus ring just inside this node’s layout rect.

The default focus ring is outside the rect so it does not reduce usable control area. Inside rings are for dense, flush stacks such as menu rows, where adding gaps would change the intended visual recipe.

Source

pub fn focus_ring_outside(self) -> Self

Draw the stock focus ring outside this node’s layout rect.

Source

pub fn tooltip(self, text: impl Into<String>) -> Self

Attach a hover tooltip to this element. The runtime synthesizes a floating tooltip layer when the pointer rests on the node for the configured delay.

The node must also have a key. Tooltips fire through the hit-test pipeline, and crate::hit_test only returns keyed nodes — an unkeyed leaf with .tooltip() is silently dead, because hover skips past it to the nearest keyed ancestor (which has a different computed_id and a different tooltip). The bundle lint flags this case as crate::bundle::lint::FindingKind::DeadTooltip.

For info-only chrome inside list rows (sha cells, timestamps, chips, identicon avatars) the usual key is a synthetic one like "row:{idx}.<part>" — its only purpose is to make the tooltip’s hover land. The tooltip text is snapshotted onto the hit target at hit-test time, so tooltips fire correctly even on virtual_list_dyn rows whose children are realized only during layout.

Source

pub fn cursor(self, cursor: Cursor) -> Self

Declare the pointer cursor when the pointer is over this element.

Source

pub fn cursor_pressed(self, cursor: Cursor) -> Self

Declare the cursor shown only while a press is captured at this exact node.

Source

pub fn opacity(self, v: f32) -> Self

Multiply this element’s paint alpha by v (clamped to [0, 1]).

Source

pub fn translate(self, x: f32, y: f32) -> Self

Offset this element’s paint and its descendants by (x, y) in logical pixels.

Source

pub fn scale(self, v: f32) -> Self

Uniformly scale this element’s paint around its rect centre.

Source

pub fn animate(self, timing: Timing) -> Self

Opt this element into app-driven prop interpolation.

Source

pub fn shader(self, binding: ShaderBinding) -> Self

Bind a shader for the surface paint, replacing the implicit stock::rounded_rect.

Examples found in repository?
examples/custom_shader.rs (lines 23-28)
22fn gradient_button(label: &str, top: Color, bottom: Color, radius: f32) -> El {
23    button(label).text_color(tokens::PRIMARY_FOREGROUND).shader(
24        ShaderBinding::custom("gradient")
25            .color("vec_a", top)
26            .color("vec_b", bottom)
27            .f32("vec_c", radius),
28    )
29}
More examples
Hide additional examples
examples/custom_paint.rs (lines 59-64)
44fn graph_cell(lane: u8, selected: bool) -> El {
45    let lane_color = lane_palette(lane);
46    let ring_color = if selected {
47        Color::rgb(245, 245, 250)
48    } else {
49        lane_color
50    };
51    let ring_w = if selected { 2.5 } else { 1.5 };
52    let radius = 5.0;
53    let line_w = 2.0;
54    let lane_frac = (lane as f32 + 0.5) / LANE_COUNT as f32;
55
56    El::new(Kind::Custom("graph_cell"))
57        .width(Size::Fixed(GRAPH_WIDTH))
58        .height(Size::Fixed(ROW_HEIGHT))
59        .shader(
60            ShaderBinding::custom("commit_node")
61                .color("vec_a", tokens::BACKGROUND)
62                .color("vec_b", ring_color)
63                .vec4("vec_c", [radius, ring_w, line_w, lane_frac]),
64        )
65        .fill(lane_color)
66}
Source

pub fn style_profile(self, p: StyleProfile) -> Self

Examples found in repository?
examples/dashboard_01_calibration.rs (line 453)
451fn icon_cell(label: &'static str) -> El {
452    El::new(Kind::Custom("icon_cell"))
453        .style_profile(StyleProfile::Surface)
454        .text(label)
455        .text_align(TextAlign::Center)
456        .caption()
457        .font_weight(FontWeight::Semibold)
458        .fill(tokens::MUTED)
459        .radius(tokens::RADIUS_SM)
460        .width(Size::Fixed(30.0))
461        .height(Size::Fixed(30.0))
462}
More examples
Hide additional examples
examples/polish_calibration.rs (line 357)
355fn icon_cell(label: &'static str) -> El {
356    El::new(Kind::Custom("icon_cell"))
357        .style_profile(StyleProfile::Surface)
358        .text(label)
359        .text_align(TextAlign::Center)
360        .caption()
361        .font_weight(FontWeight::Semibold)
362        .fill(tokens::MUTED)
363        .stroke(tokens::BORDER)
364        .radius(tokens::RADIUS_SM)
365        .width(Size::Fixed(26.0))
366        .height(Size::Fixed(26.0))
367}
examples/settings_calibration.rs (line 126)
114fn settings_nav_card() -> El {
115    column([
116        settings_nav_item("Account", true),
117        settings_nav_item("Security", false),
118        settings_nav_item("Notifications", false),
119        settings_nav_item("Appearance", false),
120        settings_nav_item("Billing", false),
121    ])
122    .gap(tokens::SPACE_1)
123    .padding(tokens::SPACE_1)
124    .width(Size::Fixed(220.0))
125    .height(Size::Fill(1.0))
126    .style_profile(StyleProfile::Surface)
127    .surface_role(SurfaceRole::Panel)
128    .fill(tokens::CARD)
129    .stroke(tokens::BORDER)
130    .radius(tokens::RADIUS_MD)
131    .shadow(tokens::SHADOW_MD)
132}
133
134fn settings_nav_item(label: &'static str, selected: bool) -> El {
135    let mut item = row([
136        El::new(Kind::Custom("nav-dot"))
137            .fill(tokens::MUTED_FOREGROUND)
138            .radius(tokens::RADIUS_PILL)
139            .width(Size::Fixed(6.0))
140            .height(Size::Fixed(6.0)),
141        text(label)
142            .font_weight(FontWeight::Medium)
143            .ellipsis()
144            .width(Size::Fill(1.0)),
145    ])
146    .key(if selected {
147        "metric:settings.nav.row".to_string()
148    } else {
149        format!("settings-nav-{label}")
150    })
151    .metrics_role(MetricsRole::ListItem)
152    .align(Align::Center)
153    .focusable();
154
155    if selected {
156        item = item.current();
157    } else {
158        item = item.color(tokens::MUTED_FOREGROUND);
159    }
160
161    item
162}
163
164fn settings_body() -> El {
165    column([
166        column([
167            h1("Account").heading().key("metric:section.title"),
168            text("Manage identity, workspace defaults, and security preferences.")
169                .muted()
170                .wrap_text()
171                .key("metric:page.subtitle"),
172        ])
173        .gap(tokens::SPACE_1)
174        .height(Size::Hug),
175        scroll([profile_card(), preferences_card()])
176            .key("settings-body-scroll")
177            .gap(tokens::SPACE_4)
178            .width(Size::Fill(1.0))
179            .height(Size::Fill(1.0)),
180    ])
181    .gap(tokens::SPACE_4)
182    .width(Size::Fill(1.0))
183    .height(Size::Fill(1.0))
184}
185
186fn profile_card() -> El {
187    card([
188        card_header([
189            card_title("Profile"),
190            card_description("This information appears in audit logs and shared documents."),
191        ]),
192        card_content([form([
193            row([
194                setting_field("Display name", "Alicia Koch", "display-name"),
195                setting_field("Email", "alicia@acme.co", "email"),
196            ])
197            .gap(tokens::SPACE_3),
198            row([
199                setting_select("Role", "Workspace admin", "role"),
200                setting_select("Region", "US East", "region"),
201            ])
202            .gap(tokens::SPACE_3),
203        ])]),
204    ])
205    .key("metric:profile.card")
206}
207
208fn setting_field(label: &'static str, value: &'static str, key: &'static str) -> El {
209    form_item([
210        form_label(label),
211        form_control(
212            text_input(value, &Selection::caret(key, value.len()), key).key(
213                if key == "display-name" {
214                    "metric:form.input"
215                } else {
216                    key
217                },
218            ),
219        ),
220    ])
221    .width(Size::Fill(1.0))
222}
223
224fn setting_select(label: &'static str, value: &'static str, key: &'static str) -> El {
225    form_item([form_label(label), form_control(select_trigger(key, value))]).width(Size::Fill(1.0))
226}
227
228fn preferences_card() -> El {
229    card([
230        card_header([
231            card_title("Preferences"),
232            card_description("Defaults used when creating new dashboards and exports."),
233        ]),
234        card_content([column([
235            preference_row(
236                "Compact navigation",
237                "Use tighter rows in the sidebar and command menus.",
238                switch(true).key("compact-navigation"),
239            ),
240            divider(),
241            preference_row(
242                "Email summaries",
243                "Send a daily digest when documents change.",
244                switch(false).key("email-summaries"),
245            ),
246            divider(),
247            preference_row(
248                "Require approval",
249                "Route external sharing through an owner review.",
250                checkbox(true).key("approval-required"),
251            ),
252        ])
253        .gap(0.0)
254        .width(Size::Fill(1.0))])
255        .padding(0.0),
256    ])
257    .key("metric:preferences.card")
258}
259
260fn preference_row(title: &'static str, description: &'static str, control: El) -> El {
261    row([
262        column([
263            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
264            text(description)
265                .caption()
266                .ellipsis()
267                .width(Size::Fill(1.0)),
268        ])
269        .gap(2.0)
270        .width(Size::Fill(1.0))
271        .height(Size::Hug),
272        control,
273    ])
274    .key(if title == "Compact navigation" {
275        "metric:preference.row".to_string()
276    } else {
277        format!("preference-{title}")
278    })
279    .metrics_role(MetricsRole::PreferenceRow)
280    .gap(tokens::SPACE_4)
281    .padding(Sides::xy(tokens::SPACE_4, tokens::SPACE_3))
282    .align(Align::Center)
283}
284
285fn settings_aside() -> El {
286    column([security_card(), scale_card()])
287        .gap(tokens::SPACE_4)
288        .width(Size::Fixed(300.0))
289        .height(Size::Fill(1.0))
290}
291
292fn security_card() -> El {
293    card([
294        card_header([
295            card_title("Security"),
296            card_description("Two-factor authentication is enabled for all privileged users."),
297        ]),
298        card_content([
299            compact_stat("Passkeys", "2 registered", badge("On").success()),
300            compact_stat("Sessions", "3 active", button("Review").secondary()),
301        ]),
302    ])
303    .width(Size::Fill(1.0))
304}
305
306fn scale_card() -> El {
307    card([
308        card_header([
309            card_title("Interface scale"),
310            card_description("Reference captures keep browser zoom fixed and vary root UI scale."),
311        ]),
312        card_content([
313            row([text("Dense").caption(), spacer(), text("Default").caption()]),
314            slider(0.66, tokens::PRIMARY)
315                .key("interface-scale")
316                .width(Size::Fill(1.0)),
317        ]),
318    ])
319    .width(Size::Fill(1.0))
320}
321
322fn compact_stat(title: &'static str, detail: &'static str, control: El) -> El {
323    row([
324        column([
325            text(title).semibold().ellipsis().width(Size::Fill(1.0)),
326            text(detail).caption().ellipsis().width(Size::Fill(1.0)),
327        ])
328        .gap(2.0)
329        .width(Size::Fill(1.0))
330        .height(Size::Hug),
331        control,
332    ])
333    .gap(tokens::SPACE_2)
334    .height(Size::Fixed(44.0))
335    .align(Align::Center)
336}
337
338fn section_label(label: &'static str) -> El {
339    text(label)
340        .caption()
341        .height(Size::Fixed(22.0))
342        .padding(Sides::xy(tokens::SPACE_2, 0.0))
343}
344
345fn side_item(icon_name: &'static str, label: &'static str, selected: bool) -> El {
346    let mut item = row([
347        icon(icon_name)
348            .color(tokens::MUTED_FOREGROUND)
349            .icon_size(tokens::ICON_SM)
350            .width(Size::Fixed(tokens::ICON_SM)),
351        text(label)
352            .font_weight(FontWeight::Medium)
353            .ellipsis()
354            .width(Size::Fill(1.0)),
355    ])
356    .key(if selected {
357        "metric:sidebar.nav.row".to_string()
358    } else {
359        format!("side-item-{label}")
360    })
361    .metrics_role(MetricsRole::ListItem)
362    .gap(tokens::SPACE_2)
363    .padding(Sides::xy(tokens::SPACE_2, 0.0))
364    .height(Size::Fixed(32.0))
365    .align(Align::Center)
366    .focusable();
367
368    if selected {
369        item = item.current();
370    } else {
371        item = item.color(tokens::MUTED_FOREGROUND);
372    }
373
374    item
375}
376
377fn icon_slot(icon_name: &'static str) -> El {
378    El::new(Kind::Custom("icon_cell"))
379        .style_profile(StyleProfile::Surface)
380        .child(
381            icon(icon_name)
382                .color(tokens::FOREGROUND)
383                .icon_size(tokens::ICON_XS),
384        )
385        .align(Align::Center)
386        .justify(Justify::Center)
387        .fill(tokens::MUTED)
388        .stroke(tokens::BORDER)
389        .radius(tokens::RADIUS_SM)
390        .width(Size::Fixed(30.0))
391        .height(Size::Fixed(30.0))
392}

Trait Implementations§

Source§

impl Clone for El

Source§

fn clone(&self) -> El

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for El

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for El

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl From<&String> for El

Source§

fn from(s: &String) -> Self

Converts to this type from the input type.
Source§

impl From<&str> for El

Source§

fn from(s: &str) -> Self

Converts to this type from the input type.
Source§

impl From<String> for El

Source§

fn from(s: String) -> Self

Converts to this type from the input type.

Auto Trait Implementations§

§

impl Freeze for El

§

impl !RefUnwindSafe for El

§

impl Send for El

§

impl Sync for El

§

impl Unpin for El

§

impl UnsafeUnpin for El

§

impl !UnwindSafe for El

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<SS, SP> SupersetOf<SS> for SP
where SS: SubsetOf<SP>,

Source§

fn to_subset(&self) -> Option<SS>

The inverse inclusion map: attempts to construct self from the equivalent element of its superset. Read more
Source§

fn is_in_subset(&self) -> bool

Checks if self is actually part of its subset T (and can be converted to it).
Source§

fn to_subset_unchecked(&self) -> SS

Use with care! Same as self.to_subset but without any property checks. Always succeeds.
Source§

fn from_subset(element: &SS) -> SP

The inclusion map: converts self to the equivalent element of its superset.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.