Skip to content

[WEB-8152] Add ButtonGroup & SplitButton, true up button-family states to Figma#228

Open
codingwolf-at wants to merge 10 commits into
mainfrom
WEB-8152/button-design-docs
Open

[WEB-8152] Add ButtonGroup & SplitButton, true up button-family states to Figma#228
codingwolf-at wants to merge 10 commits into
mainfrom
WEB-8152/button-design-docs

Conversation

@codingwolf-at

@codingwolf-at codingwolf-at commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Description

Adds two new button-family components and trues up the existing button system against the Figma "Global components" sheets.

New components

  • ButtonGroup (elements + components): a connected action row per Figma "Button Group" — the container owns the raised surface, outer border, dividers, and clipped corners; ButtonGroupButton segments are transparent/borderless with their own tighter geometry (sm/md/lg, no xl) and an inset focus ring. The ready-made adds role="group" and shares magnitude with every segment via context (segment prop still wins).
  • SplitButton (elements + components): a main-action Button plus a chevron IconButton grafted as the surrounding Menu's trigger. Primary lays the segments out as two discrete pills (1px gap, facing corners soft-squared to 2px); secondary connects them into one outline, collapsing the doubled border so the leading segment's end border is the divider. Neutral-only, primary/secondary only (Figma ships no tertiary/ghost/danger split). Frame child selectors deliberately avoid :last-child — Base UI appends focus-guard spans inside the frame while the menu is open (regression-tested). Docs/stories anchor the menu with MenuContent align="end" so it drops below the control flush with its end edge.

Button-family true-up (Figma)

  • control-chrome: per-prominence disabled/loading palettes (primary/danger fill → solid layer-disabled pill; secondary keeps its outline with border-subtle so it doesn't vanish on the canvas; tertiary/ghost drop their fill), ghost hover now uses the solid layer-3 ramp, secondary-danger rests transparent. All disabled keys are mirrored on aria-disabled/aria-busy so soft-disabled loading styles correctly.
  • New ControlChromePair closed type + helper: danger exists only on primary/secondary, so invalid prominence × tone combos are now unrepresentable in Button/IconButton props.
  • Loading spinner moved to the trailing slot on Button/AnchorButton (matches Figma); the label no longer opacity-dims — muting comes from the chrome. Documented the deliberate LoaderCircle-instead-of-Figma-radial spinner decision.
  • AnchorButton: underline moved onto the label only (flanking icons stay un-underlined); disabled keeps the underline and only mutes color; loading supported.
  • Storybook preview canvas switched bg-canvasbg-layer-2 so layer-3-keyed fills (tertiary rest) are visible; AGENTS.md updated for the icon node-prop convention.
  • Stories throughout: elements-tier pinned-state showcases with CSS canaries; components-tier interaction tests (activation independence, menu open/close, context sizing, disabled tab order, loading focusability).

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • Feature (non-breaking change which adds functionality)
  • Improvement (change that would cause existing functionality to not work as expected)
  • Code refactoring
  • Performance improvements
  • Documentation update

Test Scenarios

  • Storybook → Components/ButtonGroup: click each segment independently; verify group magnitude sizes all segments and a disabled segment is skipped by Tab.
  • Components/SplitButton: click the main segment (action fires, menu stays closed), click the chevron (menu opens below, right-aligned); verify the chevron's right corners stay rounded while the menu is open.
  • SplitButton primary vs secondary: primary renders two gapped pills; secondary renders one connected outline with a single 1px divider (no doubled border).
  • Elements/Button States: verify new disabled/loading fills per prominence (secondary keeps its outline, tertiary/ghost go transparent) and ghost hover shows the solid grey fill.
  • Button/AnchorButton with loading: spinner appears at the trailing edge, control stays focusable but non-activatable; AnchorButton underline covers text only.
  • vp check and full Storybook suite green (817 tests).

Snapshots

Screenshot 2026-07-15 at 5 06 48 PM Screenshot 2026-07-15 at 5 07 01 PM Screenshot 2026-07-15 at 5 07 59 PM Screenshot 2026-07-15 at 5 08 18 PM

Figma URL

- Introduced `ButtonGroup` and `ButtonGroupButton` components to create a connected button group with shared magnitude via context.
- Implemented `SplitButton` component, combining a main action button with a menu-triggering icon button, maintaining consistent styling and behavior.
- Added stories for both components to demonstrate various states, including interaction tests and size variations, ensuring comprehensive coverage for UI configurations.
- Established context for managing button magnitudes within the `ButtonGroup`, enhancing flexibility and usability across button segments.
@github-actions

Copy link
Copy Markdown

📚 Storybook preview: https://pr-228-propel-storybook.vamsi-906.workers.dev

…yling

- Adjusted `MenuContent` alignment to "end" in multiple SplitButton stories for consistent presentation.
- Enhanced interaction tests to ensure proper focus handling and visual consistency of the SplitButton trigger.
- Updated comments in the variants file to clarify styling behavior related to disabled states and border handling.
- Refined variant classes to ensure proper border and divider behavior for primary and secondary segments.
…havior

- Added detailed comments regarding the selection of the `LoaderCircle` glyph over the radial spinner from Figma, emphasizing adherence to using lucide icons.
- Enhanced documentation on the spinner's animation behavior, specifying the purpose of the `--animate-spinner-reveal` and perpetual `spin` animations for improved clarity.
…te handling

- Updated loading state behavior in `Button` and `AnchorButton` components to display a trailing spinner instead of replacing the label icon.
- Enhanced documentation to clarify the loading state effects on the button and anchor button labels.
- Adjusted styles in `preview.css` to ensure consistent background and text colors in light and dark modes.
- Improved story examples for both components to reflect the new loading state behavior and ensure clarity in usage.
- Deleted the `AllVariantsDebug` story and associated debug functions to clean up the button stories.
- Removed unused constants and comments related to the debug sheet, streamlining the code for better maintainability.
- Ensured that the button stories focus on relevant examples and usage without the clutter of temporary debugging elements.
…ndling

- Introduced a `controlChromePair` utility to standardize the application of prominence and tone across button and icon-button components.
- Updated button and icon-button stories to reflect the new control chrome structure, ensuring consistent styling and behavior.
- Enhanced loading state handling in button components, maintaining focus and accessibility during async operations.
- Improved documentation and comments for clarity on component behavior and usage.
…roved prominence handling

- Added `argTypes` for `magnitude` and `prominence` in SplitButton and ButtonGroup stories to standardize control options.
- Updated SplitButton component to remove the tone prop, enforcing a neutral tone for consistency.
- Refined story examples to better demonstrate the visual differences between primary and secondary prominence styles.
- Improved documentation and comments for clarity on component behavior and usage.
@codingwolf-at codingwolf-at changed the title Add ButtonGroup and SplitButton components with context support [WIP] [WEB-8152] Add ButtonGroup & SplitButton, true up button-family states to Figma Jul 14, 2026
@makeplane

makeplane Bot commented Jul 14, 2026

Copy link
Copy Markdown

Linked to Plane Work Item(s)

This comment was auto-generated by Plane

…ing loading states

- Updated button and icon-button stories to replace `userEvent.click` with `fireEvent.click` for better compatibility with loading states.
- Enhanced comments to clarify the rationale behind direct event dispatching when buttons are in a loading state.
- Ensured consistent behavior and accessibility during async operations across button components.
- Added a decorator to center align buttons in multi-button showcases, ensuring a uniform presentation.
- Updated individual button story renders to use fragments instead of divs for cleaner structure.
- Improved overall story organization and visual consistency across IconButton examples.
@codingwolf-at codingwolf-at force-pushed the WEB-8152/button-design-docs branch from 8920169 to e313281 Compare July 14, 2026 14:46
@codingwolf-at codingwolf-at requested a review from aaryan610 July 14, 2026 15:26
// (not `layer-transparent-hover` — 4%/6% black alpha reads as “no fill” on white).
"bg-layer-transparent text-secondary",
"hover:bg-layer-transparent-hover active:bg-layer-transparent-active",
"hover:bg-layer-3 active:bg-layer-3-hover",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Figma’s ghost hover/active states use transparent-hover / transparent-active overlays for both Button and IconButton, rather than the opaque layer-3 ramp. Could we keep hover:bg-layer-transparent-hover active:bg-layer-transparent-active here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

"disabled:border-disabled disabled:bg-layer-disabled disabled:text-disabled disabled:shadow-none",
// Figma: outlined pill stays. Use `border-subtle` (not `border-disabled`) — disabled
// token is the same as `--bg-canvas`, so the stroke vanishes on the Storybook/page canvas.
"disabled:border-subtle disabled:bg-layer-2 disabled:text-disabled disabled:shadow-none",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The disabled secondary spec keeps a transparent fill, but this applies bg-layer-2. On non-canvas surfaces that will show as a filled chip—could disabled, aria-disabled, and busy states remain transparent?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

…d state visibility

- Modified styles for disabled button variants to ensure the background is transparent while maintaining the border color, improving visual clarity.
- Adjusted hover and active states to utilize transparent overlays, ensuring consistent appearance across different surfaces.
- Enhanced comments for better understanding of design decisions based on Figma specifications.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants