Responsive layout
Use Flexbox, Grid, constraints, wrapping, and container queries without device-specific branches.
Let constraints do the work
Rows, columns, wrapping, intrinsic text measurement, min/max size, and percentage values adapt automatically. Prefer these rules before reaching for a container query.
let actions = Element::row([
Button::new("save", "Save", theme.button()).build(),
Button::new("cancel", "Cancel", theme.ghost_button()).build(),
])
.flex_wrap(FlexWrap::Wrap)
.gap(8.0);
Element::column([
Element::text("Account settings"),
form,
actions,
])
.width(percent(1.0))
.max_width(length(720.0))
.padding(Sides::length(24.0))
.gap(20.0)Change presentation with a container query
A named container query is appropriate when narrow space requires a deliberate structural presentation change, such as turning a toolbar row into a column. Queries resolve against the nearest scope with the same identity.
let scope = ContainerScopeId::new("toolbar");
let toolbar = Element::row(actions)
.when(ContainerQuery::max_width(scope, 360.0), compact_patch)
.container_scope(scope);Design for touch as well as width
Keep controls large enough to tap, allow action rows to wrap, and avoid hard-coded desktop widths. Hit slop can enlarge interaction geometry without changing pixels or layout.
Reference files
These are the implementation and guide files used for this chapter.
crates/argui/examples/layout.rsdocs/ui/styling.mdcrates/argui-layout/src/lib.rsResize this compiled responsive layout
The Rust file below is imported verbatim by this page and compiled into the WebAssembly application running underneath it.
use argui::{
paint::CornerRadii,
runtime::{Context, LayoutSnapshot, Render},
text::TextStyle,
ui::{Element, FlexWrap, Sides, length, percent},
widgets::default_theme,
};
#[derive(Default)]
pub struct Example {
compact: bool,
}
impl Render for Example {
fn render(&mut self, cx: &mut Context<Self>) -> Element {
let themes = default_theme(cx.environment());
let theme = themes.resolve(cx.environment().color_scheme);
let width = if self.compact {
percent(1.0)
} else {
length(180.0)
};
let card = |label| {
Element::text(label)
.text_style(TextStyle {
color: theme.primary_foreground,
weight: 650,
..TextStyle::default()
})
.width(width)
.grow(1.0)
.padding(Sides::length(22.0))
.background(theme.primary)
.radius(CornerRadii::all(12.0))
};
Element::row([card("Flexible"), card("Responsive"), card("Retained")])
.width(percent(1.0))
.padding(Sides::length(24.0))
.gap(12.0)
.flex_wrap(FlexWrap::Wrap)
.background(theme.background)
}
fn layout_changed(&mut self, layout: &LayoutSnapshot, cx: &mut Context<Self>) {
let compact = layout.viewport_size().width < 540.0;
if self.compact != compact {
self.compact = compact;
cx.notify();
}
}
}
Starting this lesson’s WebAssembly module…