Forms (form / section / labeled)
The UI snippets use generated res::str accessors from an app’s Fluent catalog; see the
example messages and catalog setup.
Authoring
form((
section((
labeled(res::str::volume(), slider(volume).range(0.0..=100.0)),
labeled(res::str::enabled(), toggle(enabled)),
))
.title(res::str::sound()),
section((
labeled(res::str::name(), text_field(name)),
label(res::str::hint()).font(Font::Footnote), // sections take arbitrary pieces, not just rows
)),
))
form(sections)— a vertical run (spacing 16) that provides the shared label column to everylabeledrow underneath it, via the scoped environment (docs/environment.md).section(children).title(t)— one grouped card: an optional footnote-style header above a rounded card (radius 10, content padded 14, spacing 10). Children are arbitrary pieces;labeledrows are just the ones that participate in alignment. Asectionalso works standalone, outside anyform.labeled(text, control)— one form row: the label sits right-aligned in the form-wide column, on the same text baseline as the control (docs/baseline.md; centered instead when either side has no baseline, e.g. a toggle, or on a toolkit that reports none); the control starts at the column edge + 12. A control marked.grow()(text fields, sliders, or arow(( … ))wrapper) stretches to the row’s remaining width; others hug their natural size. Outside aform, the column is just that row’s own label width. A control whose natural width does not fit beside the column — a two-button stepper in a 280-point inspector — goes UNDER the label instead, leading-aligned with the control across the whole row (the settings-list idiom), rather than clipping at the row’s edge. A stretching control shrinks rather than overflows, so give one that needs room a.min_width(pt): a slider beside a color well then stacks instead of vanishing. The label also becomes the control’s accessible name (a live text follows its source), unless the control names itself with.a11y(|a| a.label(..)), so a form needs no repeated label.
The section-card surface
The card’s background is not a hard-coded color: the container is realized with
SurfaceRole::SectionCard (day-spec ContainerProps.role), and each toolkit maps it to its
own theme-adaptive grouped-content material, so cards follow light/dark mode and platform
theming with no app code:
| Toolkit | Material |
|---|---|
| AppKit | NSColor.quaternarySystemFillColor, resolved per-draw in drawRect: (stays live across appearance changes) |
| UIKit | tertiarySystemFillColor + layer corner radius |
| GTK | libadwaita’s .card style class |
| Qt | translucent neutral fill via a scoped stylesheet + WA_StyledBackground (no grouped-card palette role exists; a 12% gray adapts to any palette) |
| Android | ?attr/colorSurfaceContainer (Material 3; falls back to colorSurfaceVariant) in a rounded GradientDrawable |
| XAML | CardBackgroundFillColorDefaultBrush theme resource |
| ArkUI | translucent neutral fill + corner radius |
Layout: how alignment works
form plants a shared Rc<Cell<f64>> column width in the environment; each LabeledLayout
registers its label’s unconstrained width during measure and reads back the running max in
place. A row that stacks registers nothing: its label sits above its control, not in the
column, so a long label on a narrow screen (a French settings row on a phone) leaves the other
rows’ controls where they were instead of pushing them off the row. Within one layout pass the enclosing stacks measure every child before placing any,
so by place time the max is final; alignment is consistent with no invalidation pass needed. Rows
report the proposed width as their size (labels align form-wide, controls may stretch), so a
growing column width never changes a row’s measured size and can’t oscillate the pass.
Vertically the row asks both children for their first text baseline (docs/baseline.md) and shifts each down to the deeper of the two, so the label meets the control’s inset text instead of the middle of its box. The row is then as tall as that shared baseline plus the deepest descent below it. When either side reports no baseline the row centers both, as before.
Verification
day-piecesmock e2e (form_aligns_labels_and_sections_carry_the_card_surface): two sections carry the card role + radius; label right edges align across sections; control left edges align across sections.- The showcase uses forms on the Controls, Canvas & shapes, Device & sensors, and the four Platform pages; the walkthrough drives every control inside them on all targets, and the three screenshot variants (light / dark / fr) verify the card material adapts per theme on every toolkit in CI.
Follow-ups
labeledcaption/footer affordances (secondary line under the control, iOS-settings style).- Optional per-
sectionlabel column (opt out of form-wide alignment for asymmetric sections).