ButtonGroup
A cluster of buttons that assistive technology announces as one thing before it reads them.
import { ButtonGroup, ButtonGroupColumn } from "@glint/components/button-group.slint";import { Button, ButtonVariant, ButtonJoin } from "@glint/components/button.slint";import { Tokens } from "@glint/theme/tokens.slint";
export component Demo inherits Window { width: 620px; height: 260px; background: Tokens.color-background;
HorizontalLayout { padding: 24px; spacing: 48px; alignment: center;
VerticalLayout { alignment: center;
// The row: a cluster with no gap, each button keeping its corners. ButtonGroup { accessible-label: "Text style"; Button { variant: ButtonVariant.outline; text: "Bold"; } Button { variant: ButtonVariant.outline; text: "Italic"; } Button { variant: ButtonVariant.outline; text: "Underline"; } } }
VerticalLayout { alignment: center;
// The column: welded, so the corners that meet are squared. ButtonGroupColumn { accessible-label: "Zoom"; Button { variant: ButtonVariant.outline; text: "Zoom in"; join: ButtonJoin.first; } Button { variant: ButtonVariant.outline; text: "Reset"; join: ButtonJoin.middle; } Button { variant: ButtonVariant.outline; text: "Zoom out"; join: ButtonJoin.last; } } } }}Usage
import { ButtonGroup } from "@glint/components/button-group.slint";import { Button, ButtonVariant } from "@glint/components/button.slint";import { Tokens } from "@glint/theme/tokens.slint";
export component AppWindow inherits Window { width: 420px; height: 140px; background: Tokens.color-background;
VerticalLayout { padding: 24px; alignment: center;
HorizontalLayout { alignment: center;
ButtonGroup { accessible-label: "Alignment"; Button { variant: ButtonVariant.outline; text: "Left"; } Button { variant: ButtonVariant.outline; text: "Center"; } Button { variant: ButtonVariant.outline; text: "Right"; } } } }}Drop two or more Buttons into the group’s slot. The group is layout and a
name: it lays them out in a single row with no gap between them, and publishes
one node for the cluster so a screen reader says what the buttons belong to
before it reads them. Naming it is the call site’s — accessible-label, the way
every Glint control is named.
Each button keeps its own variant and its own rounded corners — the group is
not a style override. Pair the cluster with variant: ButtonVariant.outline for
the classic segmented look.
ButtonGroup is not a selection. It is a row of independent actions, each with
its own clicked. Reach for ToggleGroup when
one of the buttons has to stay pressed, and for Tabs
when picking one switches what is on screen.
Examples
The stacked cluster, and the weld
ButtonGroupColumn is the same cluster standing on end. It is a component of
its own rather than an orientation on ButtonGroup, because a @children
slot is written once and never inside an if (ADR-0027) — a group cannot pick
between two layouts at runtime, so the axis is structural. Sheet and Drawer
make the same move for the same reason.
The column is what welds. Stacked buttons overlap by the hairline they
share, so two bordered neighbours draw one line instead of two, and the corners
where they meet are squared. Squaring them is each button’s own join — Slint
gives a parent no way to reach into its @children, so the column cannot do it
for you (the wall ADR-0015 and ADR-0021 hit). ButtonJoin.first squares the
bottom corners, last the top ones, middle all four, and none — the
default — squares nothing. The plain ButtonGroup row stays unwelded.
import { ButtonGroupColumn } from "@glint/components/button-group.slint";import { Button, ButtonVariant, ButtonJoin } from "@glint/components/button.slint";import { Tokens } from "@glint/theme/tokens.slint";
export component Demo inherits Window { width: 620px; height: 280px; background: Tokens.color-background;
HorizontalLayout { padding: 24px; spacing: 64px; alignment: center;
VerticalLayout { spacing: 8px; alignment: center;
Text { text: "Welded"; color: Tokens.color-muted-foreground; font-size: Tokens.typography-body-sm-size; horizontal-alignment: center; } ButtonGroupColumn { accessible-label: "Welded stack"; Button { variant: ButtonVariant.outline; text: "One"; join: ButtonJoin.first; } Button { variant: ButtonVariant.outline; text: "Two"; join: ButtonJoin.middle; } Button { variant: ButtonVariant.outline; text: "Three"; join: ButtonJoin.last; } } }
VerticalLayout { spacing: 8px; alignment: center;
Text { text: "Every join left at none"; color: Tokens.color-muted-foreground; font-size: Tokens.typography-body-sm-size; horizontal-alignment: center; } ButtonGroupColumn { accessible-label: "Unwelded stack"; Button { variant: ButtonVariant.outline; text: "One"; join: ButtonJoin.none; } Button { variant: ButtonVariant.outline; text: "Two"; join: ButtonJoin.none; } Button { variant: ButtonVariant.outline; text: "Three"; join: ButtonJoin.none; } } } }}A chunk of text in the cluster
ButtonGroupText is the piece of the cluster that is not a button — the
https:// ahead of a host field, the page count between two steppers. It wears
the buttons’ chrome so the cluster reads as one, and nothing else: no press, no
focus, no tab stop. It is announced as text, because a node that says “button”
offers an action that does not exist.
import { ButtonGroup, ButtonGroupText } from "@glint/components/button-group.slint";import { Button, ButtonVariant, ButtonSize } from "@glint/components/button.slint";import { Tokens } from "@glint/theme/tokens.slint";import { IconSet } from "@lucide";
export component Demo inherits Window { width: 620px; height: 200px; background: Tokens.color-background;
in-out property <int> page: 3;
VerticalLayout { padding: 24px; alignment: center;
HorizontalLayout { alignment: center;
ButtonGroup { accessible-label: "Page"; Button { variant: ButtonVariant.outline; size: ButtonSize.icon; leading-icon: IconSet.ChevronLeft; accessible-label: "Previous page"; clicked => { root.page = max(1, root.page - 1); } } ButtonGroupText { text: root.page + " of 12"; } Button { variant: ButtonVariant.outline; size: ButtonSize.icon; leading-icon: IconSet.ChevronRight; accessible-label: "Next page"; clicked => { root.page = min(12, root.page + 1); } } } } }}It takes the same join its neighbours do, derived the same way, so a chunk
welds into a stack exactly as a button does.
import { ButtonGroupColumn, ButtonGroupText } from "@glint/components/button-group.slint";import { Button, ButtonVariant, ButtonJoin } from "@glint/components/button.slint";import { Tokens } from "@glint/theme/tokens.slint";
export component Demo inherits Window { width: 620px; height: 240px; background: Tokens.color-background;
VerticalLayout { padding: 24px; alignment: center;
HorizontalLayout { alignment: center;
ButtonGroupColumn { accessible-label: "Quantity"; Button { variant: ButtonVariant.outline; text: "More"; join: ButtonJoin.first; } ButtonGroupText { text: "12 in stock"; join: ButtonJoin.middle; } Button { variant: ButtonVariant.outline; text: "Fewer"; join: ButtonJoin.last; } } } }}A chunk stands a default-size button tall. Beside another ButtonSize, set
height: on it at the call site — it is a plain Slint property, and a second
size vocabulary here would be Button’s own cascade
written twice.
API Reference
ButtonGroup publishes no properties, callbacks or functions of its own. What a call site can set on it is the Slint Rectangle it inherits.
ButtonGroupColumn
The stacked cluster, and the one that welds.
ButtonGroupColumn publishes no properties, callbacks or functions of its own. What a call site can set on it is the Slint Rectangle it inherits.
ButtonGroupText
The non-interactive chunk inside a cluster.
Properties
| Property | Type | Default | Description |
|---|---|---|---|
text | in string | no default | Text rendered inside the chunk. |
join | in ButtonJoin | ButtonJoin.none | Where this chunk sits in a welded group — the same knob its neighbours take, so a stack of them welds too. |
Enums
| Enum | Values |
|---|---|
ButtonJoin | none, first, middle, last |
Accessibility
- One node for the cluster. Both groups carry
accessible-role: groupbox, so assistive technology announces what the buttons belong to before reading them. Slint 1.17 has no plaingrouprole andgroupboxis the nearest; GroupBox publishes the same role for a titled cluster. - The name is yours. The groups declare the role and no label, so a call
site’s own
accessible-labellands on that node. A group with no label is a container a screen reader announces without saying what it holds. - The buttons speak for themselves. Each keeps its own name, its own
enabled state and its own tab stop — the cluster adds a node above them and
takes nothing away. An icon-only button in a cluster still needs its
accessible-label. - A chunk of text is text.
ButtonGroupTextannouncesaccessible-role: textwith its own content as the name, the way Kbd’s cap does. It takes no focus and answers no activation, so nothing in the cluster invites a press that does nothing. joinis paint. Squaring a corner changes no announcement: a welded button is announced exactly as a standalone one.