feat(button-group): add ButtonGroup component#640
Open
stritt wants to merge 4 commits into
Open
Conversation
Joins related buttons into a single segmented control. Layout-only: children keep their own variant, size, and shape, while ButtonGroup flattens inner corners and overlaps borders so adjacent buttons share one seam. - Horizontal (default) and vertical orientation - Works for action rows, icon toolbars, and split buttons (primary action + dropdown trigger) - role="group" by default, overridable (e.g. role="toolbar") - Uses only kumo semantic tokens (no raw colors, no dark: variant) Includes unit tests, docs page + demos, HomeGrid showcase entry, sidebar nav entry, registry category mapping, and a changeset.
commit: |
Contributor
Docs PreviewCommit: |
Contributor
…t corners - Remove vertical orientation; ButtonGroup is horizontal-only - Fix seam jumping on hover: only lift z-index on keyboard focus, never on hover, so the shared 1px seam stays put (later sibling paints on top) - Make corner rounding robust to runtime-injected elements (dropdown popup / backdrop / focus guards) by scoping the join selectors to button/a via :*-of-type, so outer corners stay rounded when a menu opens
…ne control Each kumo Button ships its own shadow-xs. Inside a group those drop shadows overlap at the seams, making the middle button look elevated/boxed and leaving a clipped-corner artifact on its trailing edge. Drop child shadows within the group; the shared rings provide all the definition needed.
… dark box Root cause of the boxed/dark middle segment: kumo's Button draws a near-black ring on *any* focus (focus:ring-kumo-focus/50), including mouse clicks. Inside a segmented group that renders as a jarring dark box on the clicked segment. - Reset the ring to the resting line color for mouse focus (:focus:not(:focus-visible)); the fill/selected state is the feedback - Keep the keyboard focus-visible ring (lifted) for a11y Verified in a real browser across resting / hover / mouse-click / keyboard-focus for the segmented control, the primary split button (incl. open dropdown), and the icon toolbar.
mattrothenberg
requested changes
Jul 18, 2026
mattrothenberg
left a comment
Collaborator
There was a problem hiding this comment.
Love this. We should probably use Base UI's Toolbar primitive here, no? https://base-ui.com/react/components/toolbar
ArshVermaGit
left a comment
There was a problem hiding this comment.
Looks good to me. The component is implemented cleanly, with clear docs, demos, and test coverage. It fits well with the existing component library. LGTM.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.








































































Summary
Adds a
ButtonGroupcomponent that visually joins related buttons into a single segmented control — action rows, icon toolbars, and split buttons (a primary action next to a dropdown trigger).Motivated by a hand-rolled split-button snippet that flattened corners manually and used raw Tailwind colors (
border-blue-700 dark:border-blue-600), which violate kumo's styling rules.ButtonGroupmakes this a first-class, reusable component built entirely on semantic tokens.Design
variant,size, andshape. The group flattens inner corners and overlaps borders (-1px) so adjacent buttons share a single seam — the seam color comes from each button's own ring, so it works for every variant without hardcoded colors.horizontal(default) andvertical.role="group"by default, overridable (e.g.role="toolbar"witharia-label). Interacted buttons lift viaz-indexso focus rings aren't clipped by the overlap.dark:— passes the custom color lint.What's included
KUMO_BUTTON_GROUP_VARIANTS,_DEFAULT_VARIANTS,_STYLING),forwardRef,displayName/components/button-group) with 6 demos (basic, split button, icon toolbar, icons+labels, vertical, sizes)Action) + changeset (minor)Testing
pnpm --filter @cloudflare/kumo test button-group→ 9/9 passpnpm --filter @cloudflare/kumo typecheck→ cleanpnpm --filter @cloudflare/kumo lint→ no button-group issuespnpm --filter @cloudflare/kumo build+ docs build (72 pages, button-group page renders)Reviews
bonk has reviewed the change
automated review not possible because: new component with visual/interaction behavior best verified by a human in the preview link
Tests
Tests included/updated