@edux-design/buttons
v0.1.1
Published
Headless-ish button primitives wired to the Edux design tokens. The package ships four components:
Readme
@edux-design/buttons
Headless-ish button primitives wired to the Edux design tokens. The package ships four components:
Button– primary action button with size + variant helpers.IconButton– circular/square icon surface that reuses the same states.MenuButton– trigger wrapper that pairs nicely with popovers/menus.ButtonGroup– segmented control with a sliding active state meant for contextual filters.
Everything is built with Tailwind utilities via the shared cx helper so styling stays consistent with the rest of the system.
Installation
pnpm add @edux-design/buttons @edux-design/utils
# or
npm install @edux-design/buttons @edux-design/utilsPeer deps: react@^19.1.0, react-dom@^19.1.0.
Usage
import { Button, IconButton, MenuButton } from "@edux-design/buttons";
import { Close, Chevron } from "@edux-design/icons";
export function Actions() {
return (
<div className="flex gap-4">
<Button size="small">Save</Button>
<Button isSecondary isStretched>
Cancel
</Button>
<Button isDanger disabled>
Delete
</Button>
<IconButton icon={<Close />} aria-label="Close dialog" />
<MenuButton caret={<Chevron />} onClick={() => null}>
Options
</MenuButton>
</div>
);
}ButtonGroup
import React from "react";
import { ButtonGroup } from "@edux-design/buttons";
import { Warning } from "@edux-design/icons";
export function SegmentedFilter() {
const [value, setValue] = React.useState("inbox");
return (
<ButtonGroup
value={value}
onChange={(nextValue) => setValue(nextValue)}
options={[
{ value: "inbox", label: "Label (11)" },
{ value: "drafts", label: "Label (11)" },
{
value: "alerts",
label: "Label",
endAdornment: <Warning className="h-4 w-4 text-warning-600" />,
},
]}
/>
);
}Provide options as strings or objects ({ value, label, startAdornment, endAdornment, disabled }). The active pill animates over the newly selected option and the group behaves like an accessible radiogroup (arrow keys + home/end). Use isStretched when the control should fill the parent width.
Key props
| Component | Important props |
| ------------- | --------------- |
| Button | size="small"|"medium"|"large", isSecondary, isDanger, isBare, isStretched, disabled |
| IconButton | icon, size, isBare, isDanger, disabled |
| MenuButton | Accepts all Button props plus caret slot and ARIA attributes for menu triggers |
| ButtonGroup | options, value / defaultValue, onChange, size, isStretched, per-option startAdornment / endAdornment, disabled |
Buttons warn in development if you pass conflicting variant booleans so you know which style wins.
Development
pnpm --filter @edux-design/buttons lint
pnpm --filter @edux-design/buttons check-types
pnpm --filter @edux-design/buttons buildStories live under src/demo (e.g. Button.stories.jsx, ButtonGroup.stories.jsx) for quick visual verification.
Notes
- Variants map directly to the design tokens defined in
@edux-design/tokens. - Hover/focus styles are already applied; avoid overriding unless you have a matching token.
- If you need new variants (ghost, link, destructive-secondary, etc.), add them in
Button.jsxand update the stories + README accordingly.
