Marigold
v18.0.0-beta.3
Marigold
v18.0.0-beta.3

Application

MarigoldProvider
RouterProvider

Layout

AppShellbeta
Aside
Aspect
Center
Columns
Container
Grid
Inline
Inset
Pagebeta
Panelbeta
Scrollable
Split
Stack
Tiles

Actions

Buttonupdated
ButtonGroupbeta
Link
LinkButton
ToggleButtonbeta

Form

Autocomplete
Calendar
Checkbox
ComboBox
DateField
DatePicker
DateRangePickerbeta
FileField
Form
NumberField
Radio
RangeCalendaralpha
SearchField
SegmentedControlbeta
Select
SelectListupdated
Slider
Switchupdated
TagFieldbeta
TextArea
TextField
TimeField

Collection

Cardupdated
Table
Tag
ActionBaralpha

Navigation

Accordion
Breadcrumbs
Pagination
Sidebarbeta
Tabs
TopNavigationbeta

Overlay

ActionMenualpha
ContextualHelp
Dialog
Drawer
Menuupdated
Toastbeta
Tooltip

Content

Badge
Descriptionalpha
Divider
EmptyStatebeta
Headline
Keyboardbeta
List
Loader
SectionMessage
SVG
Text
TextValuealpha
Titlealpha

Formatters

DateFormat
NumericFormat

Hooks and Utils

cn
cva
extendTheme
parseFormData
useAsyncListData
useLandmark
useListData
useResponsiveValueupdated
useTheme
VisuallyHidden
Components

ButtonGroup

Toolbar wrapper around a related cluster of buttons.

<ButtonGroup> gathers a small cluster of related actions into one toolbar. Reach for it whenever a row, item, or surface carries a few actions that belong together, like edit and delete on a list row, or the secondary actions in a panel header. The group keeps the buttons looking uniform and lets the user step through them with the arrow keys.

For a single action, use a <Button> on its own. When a cluster grows past three to five actions, fold the long tail into an <ActionMenu>.

Anatomy

GroupAction
  • Group: A toolbar wrapper around a related cluster of actions.
  • Action: Each pressable child of the group. Use a <Button> for a standard action, a <LinkButton> for one that navigates, or an <ActionMenu> at the end for the overflow tail. They all pick up the group's cascade, so you can mix them freely in one group.

A group does not even need a plain <Button>. A row of links, or a couple of links with a trailing <ActionMenu>, is just as valid.

Appearance

The appearance of a component can be customized using the variant and size props. These props adjust the visual style and dimensions of the component, available values are based on the active theme.

PropertyTypeDescription
variantsecondary | ghost | destructive-ghostThe available variants of this component.
sizedefault | small | large | iconThe available sizes of this component.

<ButtonGroup> carries no visual styling of its own beyond laying its actions out as a spaced row (or a column when orientation="vertical"). It is slot-aware: variant and size cascade to every action inside, so the cluster stays uniform without setting props on each one. When variant is unset the group cascades secondary, the same baseline as a standalone button. A variant or size set on a child always wins over the group default.

Usage

A <ButtonGroup> signals that its actions belong together, so keep one group to a single set of related actions. When a surface needs two unrelated sets, reach for two groups rather than one mixed cluster, so each toolbar still reads as one coherent unit.

Most toolbars are incidental chrome that sits beside the content, so let the cluster stay quiet and subordinate to the surface's title and to whatever primary action the page already owns. A group earns stronger emphasis only when it carries the surface's own main action, which is the exception rather than the rule.

Inline row actions

Lists and rows of equal-weight items often need a per-row cluster of quick actions like view, edit, and delete. Render them as a <ButtonGroup> so the cluster is exposed as a toolbar and stays arrow-key navigable. Set variant="ghost" so the buttons recede into the row until the user scans for them.

Summer Comedy Night
Sat, 14 Jun · Main Street Park
Harborfront Jazz Festival
Sun, 22 Jun · Harborfront Promenade
Oak Ridge Wedding Showcase
Sat, 5 Jul · Oak Ridge Barn
import {  Button,  ButtonGroup,  Inline,  LinkButton,  Stack,  Text,  Tooltip,} from '@marigold/components';import { Eye, Pencil, Trash2 } from '@marigold/icons';const events = [  {    id: 'evt-1',    title: 'Summer Comedy Night',    subtitle: 'Sat, 14 Jun · Main Street Park',  },  {    id: 'evt-2',    title: 'Harborfront Jazz Festival',    subtitle: 'Sun, 22 Jun · Harborfront Promenade',  },  {    id: 'evt-3',    title: 'Oak Ridge Wedding Showcase',    subtitle: 'Sat, 5 Jul · Oak Ridge Barn',  },];export default () => (  <Stack space="related">    {events.map(event => (      <Inline key={event.id} alignY="center" alignX="between" space="group">        <Stack space="tight">          <Text weight="medium">{event.title}</Text>          <Text size="xs" color="secondary">            {event.subtitle}          </Text>        </Stack>        <ButtonGroup          aria-label={`Actions for ${event.title}`}          variant="ghost"          size="icon"        >          <Tooltip.Trigger>            <Button aria-label="View">              <Eye />            </Button>            <Tooltip>View</Tooltip>          </Tooltip.Trigger>          <Tooltip.Trigger>            <LinkButton href="#" aria-label="Edit">              <Pencil />            </LinkButton>            <Tooltip>Edit</Tooltip>          </Tooltip.Trigger>          <Tooltip.Trigger>            <Button variant="destructive-ghost" aria-label="Delete">              <Trash2 />            </Button>            <Tooltip>Delete</Tooltip>          </Tooltip.Trigger>        </ButtonGroup>      </Inline>    ))}  </Stack>);

For destructive row actions, reach for destructive-ghost so the danger reads without overpowering the row. A variant set on the child always wins over one set on the group, so a single destructive button can sit inside an otherwise uniform cluster. Place the destructive action last so the user reads the safer ones first, and keep the action order identical on every row so the cluster stays predictable to scan. See Button: Destructive buttons for the variants.

Container actions

Surfaces like <Panel>, <Dialog>, and <Drawer> carry their own set of secondary actions like bookmark, share, settings, and dismiss. These actions belong to the container, not to the page, so they should stay subordinate to the container's title and to any primary <Button> it might host.

Event details

Public information visible to ticket buyers.

Harborfront Jazz Festival
Sunday, 22 June · 18:00 · Harborfront Promenade
import {  Button,  ButtonGroup,  Description,  Panel,  Stack,  Text,  Title,  Tooltip,} from '@marigold/components';import { Bookmark, Settings, Share2 } from '@marigold/icons';export default () => (  <Panel>    <Panel.Header>      <Title>Event details</Title>      <Description>Public information visible to ticket buyers.</Description>      <ButtonGroup size="icon" aria-label="Event actions">        <Tooltip.Trigger>          <Button aria-label="Bookmark">            <Bookmark />          </Button>          <Tooltip>Bookmark</Tooltip>        </Tooltip.Trigger>        <Tooltip.Trigger>          <Button aria-label="Share">            <Share2 />          </Button>          <Tooltip>Share</Tooltip>        </Tooltip.Trigger>        <Tooltip.Trigger>          <Button aria-label="Settings">            <Settings />          </Button>          <Tooltip>Settings</Tooltip>        </Tooltip.Trigger>      </ButtonGroup>    </Panel.Header>    <Panel.Content>      <Stack space="tight">        <Text weight="medium">Harborfront Jazz Festival</Text>        <Text size="xs" color="secondary">          Sunday, 22 June · 18:00 · Harborfront Promenade        </Text>      </Stack>    </Panel.Content>  </Panel>);

The container can publish shared defaults to every <Button> inside via ButtonContext. A Panel header, for example, can publish the variant and size for every button it contains, and individual call sites stay free of repetitive props. See Slot-Driven Composition for how container providers wire this up.

Standalone toolbar

Above an editor, a comment box, or any content area that benefits from a cluster of related one-shot actions, reach for <ButtonGroup> as a standalone toolbar.

import { Button, ButtonGroup, Tooltip } from '@marigold/components';import { AtSign, Image, Link, Paperclip, SmilePlus } from '@marigold/icons';export default () => (  <ButtonGroup aria-label="Compose actions" variant="ghost" size="icon">    <Tooltip.Trigger>      <Button aria-label="Insert link">        <Link />      </Button>      <Tooltip>Insert link</Tooltip>    </Tooltip.Trigger>    <Tooltip.Trigger>      <Button aria-label="Insert image">        <Image />      </Button>      <Tooltip>Insert image</Tooltip>    </Tooltip.Trigger>    <Tooltip.Trigger>      <Button aria-label="Attach file">        <Paperclip />      </Button>      <Tooltip>Attach file</Tooltip>    </Tooltip.Trigger>    <Tooltip.Trigger>      <Button aria-label="Mention someone">        <AtSign />      </Button>      <Tooltip>Mention</Tooltip>    </Tooltip.Trigger>    <Tooltip.Trigger>      <Button aria-label="Insert emoji">        <SmilePlus />      </Button>      <Tooltip>Emoji</Tooltip>    </Tooltip.Trigger>  </ButtonGroup>);

Accessible labels

Compact toolbars lean on icon-only buttons, and an icon on its own has no text for assistive technology to announce.

import { Button, ButtonGroup, Tooltip } from '@marigold/components';import { Copy, Download, Printer } from '@marigold/icons';export default () => (  <ButtonGroup aria-label="Report actions" variant="ghost" size="icon">    <Tooltip.Trigger>      <Button aria-label="Copy link">        <Copy />      </Button>      <Tooltip>Copy link</Tooltip>    </Tooltip.Trigger>    <Tooltip.Trigger>      <Button aria-label="Download report">        <Download />      </Button>      <Tooltip>Download</Tooltip>    </Tooltip.Trigger>    <Tooltip.Trigger>      <Button aria-label="Print report">        <Printer />      </Button>      <Tooltip>Print</Tooltip>    </Tooltip.Trigger>  </ButtonGroup>);

Give every icon-only button an aria-label with a short action phrase like Copy link, and pair it with a <Tooltip> so sighted users discover the same label on hover or focus. The two serve different audiences and both are needed. Label the toolbar itself as well: pass an aria-label to the <ButtonGroup> so screen readers announce what the cluster is for, and make it specific when the toolbar repeats per row, like Actions for {item.name}.

Keep it accessible

A tooltip is not a replacement for an aria-label. Tooltips are not reliably reachable on touch devices or by every assistive technology, so the accessible name has to live on the button. See Tooltip: Accessibility and Button: Icons and labels.

Overflow menu

Keep the toolbar to three to five visible actions. Past that it becomes a wall of buttons that competes with the content around it.

import { ActionMenu, Button, ButtonGroup, Tooltip } from '@marigold/components';import { Eye, Pencil } from '@marigold/icons';export default () => (  <ButtonGroup aria-label="Event actions" variant="ghost" size="icon">    <Tooltip.Trigger>      <Button aria-label="View">        <Eye />      </Button>      <Tooltip>View</Tooltip>    </Tooltip.Trigger>    <Tooltip.Trigger>      <Button aria-label="Edit">        <Pencil />      </Button>      <Tooltip>Edit</Tooltip>    </Tooltip.Trigger>    <ActionMenu aria-label="More event actions">      <ActionMenu.Item id="duplicate">Duplicate</ActionMenu.Item>      <ActionMenu.Item id="archive">Archive</ActionMenu.Item>      <ActionMenu.Item id="delete">Delete</ActionMenu.Item>    </ActionMenu>  </ButtonGroup>);

Move the long tail into an <ActionMenu> as the last item in the group, rather than spreading extra buttons across the row. The menu trigger is a slot-aware button, so it picks up the group's size and cascade like any other child and stays visually part of the cluster. See ActionMenu for how many items it should hold and how to label it.

One primary action

A toolbar usually stays at one level of emphasis, so every action reads as equal-weight chrome. Some surfaces carry one action that outranks the rest, like Publish in a page header or Save in a section header. There you can promote exactly one button to primary so the eye lands on it, while the others stay at the group default.

import { Button, ButtonGroup } from '@marigold/components';export default () => (  <ButtonGroup aria-label="Document actions">    <Button>Preview</Button>    <Button>Save draft</Button>    <Button variant="primary">Publish</Button>  </ButtonGroup>);

Set primary on that one button, never on the <ButtonGroup>. A variant on the group cascades to every child, which would turn the whole cluster primary and cancel the emphasis. Keep it to a single primary per toolbar, and avoid competing primaries that leave the user without a clear next step. For the general rule of one primary per section and where it should sit, see Button: Visual hierarchy and Placement and order.

Props

Did you know? You can explore, test, and customize props live in Marigold's storybook. Watch the effects they have in real-time!
View ButtonGroup stories

Prop

Type

Accessibility props (4)

Prop

Type

DOM event handlers (64)

Prop

Type

Alternative components

  • Button: The button that lives inside a group. It adapts to the group's cascade.
  • ActionMenu: Use the kebab-menu pattern when there are too many actions to fit in a Group.
  • ActionBar: Use for the bar around bulk-selection actions.
  • ToggleButton: Use <ToggleButton.Group> when each button should turn a function on and off and keep that state, instead of firing a one-shot action.
  • Tabs: Use for switching between views or mutually exclusive options, instead of a cluster of independent actions.
Last update: 8 days ago

Button

Buttons allow users to trigger actions.

Link

Interactive component to help users navigate to new pages.

On this page

AnatomyAppearanceUsageInline row actionsContainer actionsStandalone toolbarAccessible labelsOverflow menuOne primary actionPropsAlternative components