GrButtonGroup
Groups related buttons into a compact action cluster.
Overview
When to take it
- actions of the same class stand side by side — a toolbar, a “Save / Cancel” row, view switches;
- the row has to read as one whole — the inner borders are not doubled, the rounding stays at the edges;
- the styling is set once —
size,toneandvariantare handed to every button at once; - the group needs a name —
ariaLabelexplains what unites these buttons.
When to take something else
| Need | Take |
|---|---|
| A value is selected rather than an action performed | GrSegmented |
| There are many actions and they are hidden | GrDropdownMenu |
| There is a single action | GrButton |
| Sections with different content | GrTabs |
A button group and a segmented control look alike and mean different things: here every button does something and nothing stays selected after the press, there one of the options is always active.
Variants every tone and view, rendered by the real component
tone \ variant | primary | secondary | outline | ghost | ghost-border |
|---|---|---|---|---|---|
primary | |||||
neutral | |||||
success | |||||
warning | |||||
danger | |||||
info | |||||
slate | |||||
azure |
Sizes
xssmmdlg
The grid is live: hover and press it. Hover, focus and active states are not drawn here — they belong to the browser, not to a prop.
Playground controls come from the API, not from hand
Loading…
0 changed· state is in the URL
<GrButtonGroup>
<GrButton variant="outline">Board</GrButton>
<GrButton>List</GrButton>
<GrButton variant="outline">Calendar</GrButton>
</GrButtonGroup>Examples 3 each with a live demo and its code
Segmented switcher of one context
The group joins adjacent actions into one control: the edges are glued, the pressed segment is the state of the screen, and
aria-labeltells what exactly is being switched.A column and a row with a gap
orientation="vertical"stacks the buttons and moves the rounded corners to the top and bottom edges;:attached="false"gives a plain row where every button keeps its own radii and borders.
API generated from the package · cannot drift
Props
| Prop | Type | default | Description |
|---|---|---|---|
tone |
| undefined | The tone of all the buttons in the group. The prop of a button itself is stronger. |
variant | GrButtonVariant | undefined | undefined | The weight of all the buttons in the group: `primary`, `outline`, `ghost` and so on. The prop of a button itself is stronger. |
size | "xs" | "sm" | "md" | "lg" | undefined | undefined | Styling shared by the buttons of the group. The prop of a button itself is stronger. |
ariaLabel | string | undefined | undefined | The accessible name of the group: without it the buttons read as unrelated. |
orientation | GrButtonGroupOrientation | undefined | "horizontal" | The direction of the row: `vertical` stacks the buttons in a column. `horizontal` by default. |
attached | boolean | undefined | true | Glue the buttons into one block. `false` — an ordinary row with a gap: every button keeps its own radii and borders. |
Slots
| Slot | Type | Description |
|---|---|---|
default | any | The buttons of the group. |
Types
Every exported type of the subpath, grouped by the file it lives in. Names, generics and bodies come from the package’s declaration files and cannot drift.
import type { GrButtonGroupContext, GrButtonGroupOrientation, GrButtonGroupProps } from '@feugene/granularity/components/GrButtonGroup'Filedist/types/components/GrButtonGroup/config.d.ts
const grButtonGroupConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrButtonGroup">;Filedist/types/components/shared/buttonGroupContext.d.ts
type GrButtonGroupContext = {
size: ComputedRef<GrButtonSize | undefined>;
variant: ComputedRef<GrButtonVariant | undefined>;
tone: ComputedRef<GrButtonTone | undefined>;
};function useGrButtonGroup(): GrButtonGroupContext | undefined;Filedist/types/components/GrButtonGroup/GrButtonGroup.vue.d.ts
type GrButtonGroupOrientation = 'horizontal' | 'vertical';interface GrButtonGroupProps {
ariaLabel?: string;
orientation?: GrButtonGroupOrientation;
attached?: boolean;
size?: GrButtonSize;
variant?: GrButtonVariant;
tone?: GrButtonTone;
}Usage from the library prose
Styling in one line
<GrButtonGroup aria-label="Period" size="sm" variant="outline" tone="neutral">
<GrButton>Day</GrButton>
<GrButton>Week</GrButton>
<GrButton tone="primary">Month</GrButton>
</GrButtonGroup>The size, variant and tone of the group reach the buttons through context.
The resolution order: the button’s prop → the group → GrConfigProvider → the
default — the group is closer to the button than the global provider, so it beats
the provider, but not the button’s own prop.
The context is available from the outside as well — useGrButtonGroup(), if a
control of your own is built on top of the group.
Wrappers do not break the row
The gluing counts the links of the group — direct children that either are a
button or contain one. A button can therefore be wrapped into a tooltip, a v-if
wrapper or a router link, and the row stays whole:
<GrButtonGroup aria-label="Document">
<GrButton>Open</GrButton>
<GrTooltip content="The copy will appear next to it">
<GrButton>Duplicate</GrButton>
</GrTooltip>
</GrButtonGroup>A non-button child (a separator, a label) does not enter the gluing and does not take any rounding for itself.
Orientation and the mode without gluing
| Prop | What it does |
|---|---|
orientation="vertical" | a column: the rounding moves to the top and bottom edges of the row |
:attached="false" | an ordinary row with a gap, every button with its own radii |
The radius
The group takes its radius from the same point of customisation as the button
itself — --gr-button-radius (0.375rem by default). Set it once and the edges of
the group move together with the buttons:
.app { --gr-button-radius: 12px; }Accessibility
The root is declared role="group"; the name is set with the ariaLabel prop —
without it a set of buttons reads as unrelated. The button under the cursor or with
focus rises above its neighbours, so the focus ring is not clipped by the
overlapping borders.
Props in this example