From 1c6f588a7ed5787c5bb438885b74c380394fce26 Mon Sep 17 00:00:00 2001 From: Kevin Abatan Date: Thu, 25 Dec 2025 22:12:17 +0100 Subject: [PATCH 1/4] chore: add FRONTEND_PORT env var to allow worktrees --- apps/docs/app.config.ts | 5 ++++- apps/docs/tsconfig.json | 2 -- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/docs/app.config.ts b/apps/docs/app.config.ts index b836b7e6..fa4254d6 100644 --- a/apps/docs/app.config.ts +++ b/apps/docs/app.config.ts @@ -20,7 +20,10 @@ export default defineConfig( } }, vite: { - plugins: [tailwindcss()] + plugins: [tailwindcss()], + server: { + port: parseInt(process.env.FRONTEND_PORT || "5173", 10) + } } }, { diff --git a/apps/docs/tsconfig.json b/apps/docs/tsconfig.json index 047b6294..cbd39639 100644 --- a/apps/docs/tsconfig.json +++ b/apps/docs/tsconfig.json @@ -22,10 +22,8 @@ "noFallthroughCasesInSwitch": true, "noUncheckedIndexedAccess": true, "noImplicitOverride": true, - // Some stricter flags (disabled by default) "noUnusedLocals": true, "noUnusedParameters": true, - "noPropertyAccessFromIndexSignature": true, "baseUrl": ".", "paths": { "~/*": ["./src/*"] From 395c6ea4988546b6ee6f22457443184d450948ab Mon Sep 17 00:00:00 2001 From: Kevin Abatan Date: Fri, 26 Dec 2025 04:19:30 +0100 Subject: [PATCH 2/4] chore: adding trees to gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index dc1b5dd4..ad08f192 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,10 @@ Thumbs.db # Claude .claude logs +trees/ + +# Worktree environment configs +**/.ports.env app.config.timestamp* From 055d25f743087a9d393619108efcd6aa8abc9ac0 Mon Sep 17 00:00:00 2001 From: Kevin Abatan Date: Fri, 26 Dec 2025 06:46:25 +0100 Subject: [PATCH 3/4] feat: sync button-group component from shadcn UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Updated ButtonGroup component to match latest shadcn UI v4 implementation - Updated buttonGroupVariants with latest class structure - Simplified ButtonGroupText to remove extra styling not in shadcn - Updated ButtonGroupSeparator to match shadcn implementation - Added comprehensive documentation with API reference - Added Button Group to sidebar navigation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- apps/docs/src/config/docs.ts | 4 + apps/docs/src/registry/ui/button-group.tsx | 25 ++--- .../routes/docs/components/button-group.mdx | 96 +++++++++++++++++++ 3 files changed, 113 insertions(+), 12 deletions(-) create mode 100644 apps/docs/src/routes/docs/components/button-group.mdx diff --git a/apps/docs/src/config/docs.ts b/apps/docs/src/config/docs.ts index a893e129..f84add5f 100644 --- a/apps/docs/src/config/docs.ts +++ b/apps/docs/src/config/docs.ts @@ -47,6 +47,10 @@ export const docsConfig: Config = { title: "Button", href: "/docs/components/button" }, + { + title: "Button Group", + href: "/docs/components/button-group" + }, { title: "Checkbox", href: "/docs/components/checkbox" diff --git a/apps/docs/src/registry/ui/button-group.tsx b/apps/docs/src/registry/ui/button-group.tsx index 00bd2bd6..10eb5608 100644 --- a/apps/docs/src/registry/ui/button-group.tsx +++ b/apps/docs/src/registry/ui/button-group.tsx @@ -13,14 +13,14 @@ import { cn } from "~/lib/utils" import { Separator } from "~/registry/ui/separator" const buttonGroupVariants = cva( - "flex w-fit items-stretch has-[>[data-slot=button-group]]:gap-2 [&>*]:focus-visible:relative [&>*]:focus-visible:z-10 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1", + "cn-button-group flex w-fit items-stretch [&>*]:focus-visible:z-10 [&>*]:focus-visible:relative [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1 ", { variants: { orientation: { horizontal: - "[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none", + "cn-button-group-orientation-horizontal [&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none", vertical: - "flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none" + "cn-button-group-orientation-vertical flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none" } }, defaultVariants: { @@ -35,10 +35,10 @@ const ButtonGroup: Component = (props) => { const [local, others] = splitProps(props, ["class", "orientation"]) return (
) @@ -51,26 +51,27 @@ const ButtonGroupText = ( return ( ) } -const ButtonGroupSeparator: Component> = (rawProps) => { +type ButtonGroupSeparatorProps = ComponentProps + +const ButtonGroupSeparator: Component = (rawProps) => { const props = mergeProps({ orientation: "vertical" } as const, rawProps) const [local, others] = splitProps(props, ["class", "orientation"]) return ( ) diff --git a/apps/docs/src/routes/docs/components/button-group.mdx b/apps/docs/src/routes/docs/components/button-group.mdx new file mode 100644 index 00000000..fef95873 --- /dev/null +++ b/apps/docs/src/routes/docs/components/button-group.mdx @@ -0,0 +1,96 @@ +--- +title: Button Group +description: A container that groups related buttons together with consistent styling. +--- + +::::tab-group[preview] +:::tab[Preview] + + + +::: +:::tab[Code] + +```file="~/registry/examples/button-group-demo" frame=none showLineNumbers + +``` + +::: +:::: + +## Installation + +### CLI + +```package-exec +solidui-cli@latest add button-group +``` + +### Manual + +Install the following dependencies: + +```package-install +@kobalte/core +``` + +Copy and paste the following code into your project. + +```file="~/registry/ui/button-group.tsx" showLineNumbers + +``` + +## Usage + +```tsx +import { ButtonGroup } from "~/components/ui/button-group"; +``` + +```tsx + + + + +``` + +## API Reference + +### ButtonGroup + +Container component for grouping buttons. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | The layout direction of the button group | +| `class` | `string` | - | Additional CSS classes | + +### ButtonGroupSeparator + +A visual separator for dividing buttons within a group. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `orientation` | `"horizontal" \| "vertical"` | `"vertical"` | The orientation of the separator | +| `class` | `string` | - | Additional CSS classes | + +### ButtonGroupText + +A text container component within a button group. + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `as` | `ValidComponent` | `"div"` | The component to render as (polymorphic) | +| `class` | `string` | - | Additional CSS classes | + +## Accessibility + +- The button group has `role="group"` for proper semantic meaning +- Use Tab key to navigate between buttons +- Apply `aria-label` or `aria-labelledby` to the ButtonGroup for clear identification + +## Notes + +- Button groups are used when you want to group buttons that perform actions +- For toggling state, consider using a Toggle Group component instead +- Buttons with the `outline` variant don't require separators as they already have visible borders +- Button groups can be nested for more complex layouts with spacing From 13e7aa323bd9b4d104a52fd9f8ae39104a617023 Mon Sep 17 00:00:00 2001 From: Kevin Abatan Date: Fri, 26 Dec 2025 18:07:56 +0100 Subject: [PATCH 4/4] fix: format & better alignment with shadcn examples --- apps/docs/src/registry/__index__.tsx | 126 +++++++++ apps/docs/src/registry/examples/_registry.ts | 99 +++++++ .../examples/button-group-dropdown-menu.tsx | 71 +++++ .../examples/button-group-input-group.tsx | 50 ++++ .../registry/examples/button-group-input.tsx | 16 ++ .../registry/examples/button-group-nested.tsx | 36 +++ .../examples/button-group-orientation.tsx | 17 ++ .../registry/examples/button-group-select.tsx | 56 ++++ .../examples/button-group-separator.tsx | 16 ++ .../registry/examples/button-group-size.tsx | 47 ++++ .../registry/examples/button-group-split.tsx | 16 ++ apps/docs/src/registry/ui/button-group.tsx | 19 +- .../routes/docs/components/button-group.mdx | 257 ++++++++++++++++-- 13 files changed, 790 insertions(+), 36 deletions(-) create mode 100644 apps/docs/src/registry/examples/button-group-dropdown-menu.tsx create mode 100644 apps/docs/src/registry/examples/button-group-input-group.tsx create mode 100644 apps/docs/src/registry/examples/button-group-input.tsx create mode 100644 apps/docs/src/registry/examples/button-group-nested.tsx create mode 100644 apps/docs/src/registry/examples/button-group-orientation.tsx create mode 100644 apps/docs/src/registry/examples/button-group-select.tsx create mode 100644 apps/docs/src/registry/examples/button-group-separator.tsx create mode 100644 apps/docs/src/registry/examples/button-group-size.tsx create mode 100644 apps/docs/src/registry/examples/button-group-split.tsx diff --git a/apps/docs/src/registry/__index__.tsx b/apps/docs/src/registry/__index__.tsx index 7eddc1d9..b90ec29b 100644 --- a/apps/docs/src/registry/__index__.tsx +++ b/apps/docs/src/registry/__index__.tsx @@ -312,6 +312,132 @@ export const Index: Record = { categories: undefined, meta: undefined, }, + "button-group-dropdown-menu": { + name: "button-group-dropdown-menu", + description: "", + type: "registry:example", + registryDependencies: ["button","button-group","dropdown-menu"], + component: lazy(() => import("~/registry/examples/button-group-dropdown-menu.tsx")), + files: [{ + path: "registry/examples/button-group-dropdown-menu.tsx", + type: "registry:example", + target: "" + }], + categories: undefined, + meta: undefined, + }, + "button-group-input-group": { + name: "button-group-input-group", + description: "", + type: "registry:example", + registryDependencies: ["button","button-group","input-group"], + component: lazy(() => import("~/registry/examples/button-group-input-group.tsx")), + files: [{ + path: "registry/examples/button-group-input-group.tsx", + type: "registry:example", + target: "" + }], + categories: undefined, + meta: undefined, + }, + "button-group-input": { + name: "button-group-input", + description: "", + type: "registry:example", + registryDependencies: ["button","button-group","input"], + component: lazy(() => import("~/registry/examples/button-group-input.tsx")), + files: [{ + path: "registry/examples/button-group-input.tsx", + type: "registry:example", + target: "" + }], + categories: undefined, + meta: undefined, + }, + "button-group-nested": { + name: "button-group-nested", + description: "", + type: "registry:example", + registryDependencies: ["button","button-group"], + component: lazy(() => import("~/registry/examples/button-group-nested.tsx")), + files: [{ + path: "registry/examples/button-group-nested.tsx", + type: "registry:example", + target: "" + }], + categories: undefined, + meta: undefined, + }, + "button-group-orientation": { + name: "button-group-orientation", + description: "", + type: "registry:example", + registryDependencies: ["button","button-group"], + component: lazy(() => import("~/registry/examples/button-group-orientation.tsx")), + files: [{ + path: "registry/examples/button-group-orientation.tsx", + type: "registry:example", + target: "" + }], + categories: undefined, + meta: undefined, + }, + "button-group-select": { + name: "button-group-select", + description: "", + type: "registry:example", + registryDependencies: ["button","button-group","select"], + component: lazy(() => import("~/registry/examples/button-group-select.tsx")), + files: [{ + path: "registry/examples/button-group-select.tsx", + type: "registry:example", + target: "" + }], + categories: undefined, + meta: undefined, + }, + "button-group-separator": { + name: "button-group-separator", + description: "", + type: "registry:example", + registryDependencies: ["button","button-group"], + component: lazy(() => import("~/registry/examples/button-group-separator.tsx")), + files: [{ + path: "registry/examples/button-group-separator.tsx", + type: "registry:example", + target: "" + }], + categories: undefined, + meta: undefined, + }, + "button-group-size": { + name: "button-group-size", + description: "", + type: "registry:example", + registryDependencies: ["button","button-group"], + component: lazy(() => import("~/registry/examples/button-group-size.tsx")), + files: [{ + path: "registry/examples/button-group-size.tsx", + type: "registry:example", + target: "" + }], + categories: undefined, + meta: undefined, + }, + "button-group-split": { + name: "button-group-split", + description: "", + type: "registry:example", + registryDependencies: ["button","button-group"], + component: lazy(() => import("~/registry/examples/button-group-split.tsx")), + files: [{ + path: "registry/examples/button-group-split.tsx", + type: "registry:example", + target: "" + }], + categories: undefined, + meta: undefined, + }, "button-icon": { name: "button-icon", description: "", diff --git a/apps/docs/src/registry/examples/_registry.ts b/apps/docs/src/registry/examples/_registry.ts index 6cd90966..3aa05fa1 100644 --- a/apps/docs/src/registry/examples/_registry.ts +++ b/apps/docs/src/registry/examples/_registry.ts @@ -78,6 +78,105 @@ export const examples: Registry["items"] = [ } ] }, + { + name: "button-group-dropdown-menu", + type: "registry:example", + registryDependencies: ["button", "button-group", "dropdown-menu"], + files: [ + { + path: "examples/button-group-dropdown-menu.tsx", + type: "registry:example" + } + ] + }, + { + name: "button-group-input-group", + type: "registry:example", + registryDependencies: ["button", "button-group", "input-group"], + files: [ + { + path: "examples/button-group-input-group.tsx", + type: "registry:example" + } + ] + }, + { + name: "button-group-input", + type: "registry:example", + registryDependencies: ["button", "button-group", "input"], + files: [ + { + path: "examples/button-group-input.tsx", + type: "registry:example" + } + ] + }, + { + name: "button-group-nested", + type: "registry:example", + registryDependencies: ["button", "button-group"], + files: [ + { + path: "examples/button-group-nested.tsx", + type: "registry:example" + } + ] + }, + { + name: "button-group-orientation", + type: "registry:example", + registryDependencies: ["button", "button-group"], + files: [ + { + path: "examples/button-group-orientation.tsx", + type: "registry:example" + } + ] + }, + { + name: "button-group-select", + type: "registry:example", + registryDependencies: ["button", "button-group", "select"], + files: [ + { + path: "examples/button-group-select.tsx", + type: "registry:example" + } + ] + }, + { + name: "button-group-separator", + type: "registry:example", + registryDependencies: ["button", "button-group"], + files: [ + { + path: "examples/button-group-separator.tsx", + type: "registry:example" + } + ] + }, + { + name: "button-group-size", + type: "registry:example", + registryDependencies: ["button", "button-group"], + files: [ + { + path: "examples/button-group-size.tsx", + type: "registry:example" + } + ] + }, + { + name: "button-group-split", + type: "registry:example", + registryDependencies: ["button", "button-group"], + files: [ + { + path: "examples/button-group-split.tsx", + type: "registry:example" + } + ] + }, { name: "button-icon", type: "registry:example", diff --git a/apps/docs/src/registry/examples/button-group-dropdown-menu.tsx b/apps/docs/src/registry/examples/button-group-dropdown-menu.tsx new file mode 100644 index 00000000..ef9a83ee --- /dev/null +++ b/apps/docs/src/registry/examples/button-group-dropdown-menu.tsx @@ -0,0 +1,71 @@ +"use client" + +import { + AlertTriangleIcon, + CheckIcon, + ChevronDownIcon, + CopyIcon, + ShareIcon, + TrashIcon, + UserRoundXIcon, + VolumeOffIcon +} from "lucide-solid" + +import { Button } from "~/registry/ui/button" +import { ButtonGroup } from "~/registry/ui/button-group" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger +} from "~/registry/ui/dropdown-menu" + +export default function ButtonGroupDropdown() { + return ( + + + + + + + + + + + Mute Conversation + + + + Mark as Read + + + + Report Conversation + + + + Block User + + + + Share Conversation + + + + Copy Conversation + + + + + + + Delete Conversation + + + + + + ) +} diff --git a/apps/docs/src/registry/examples/button-group-input-group.tsx b/apps/docs/src/registry/examples/button-group-input-group.tsx new file mode 100644 index 00000000..ae2aabc5 --- /dev/null +++ b/apps/docs/src/registry/examples/button-group-input-group.tsx @@ -0,0 +1,50 @@ +import { createSignal } from "solid-js" + +import { AudioLinesIcon, PlusIcon } from "lucide-solid" + +import { Button } from "~/registry/ui/button" +import { ButtonGroup } from "~/registry/ui/button-group" +import { + InputGroup, + InputGroupAddon, + InputGroupButton, + InputGroupInput +} from "~/registry/ui/input-group" +import { Tooltip, TooltipContent, TooltipTrigger } from "~/registry/ui/tooltip" + +export default function ButtonGroupInputGroup() { + const [voiceEnabled, setVoiceEnabled] = createSignal(false) + + return ( + + + + + + + + + + setVoiceEnabled(!voiceEnabled)} + size="icon-xs" + > + + + Voice Mode + + + + + + ) +} diff --git a/apps/docs/src/registry/examples/button-group-input.tsx b/apps/docs/src/registry/examples/button-group-input.tsx new file mode 100644 index 00000000..753d3a5c --- /dev/null +++ b/apps/docs/src/registry/examples/button-group-input.tsx @@ -0,0 +1,16 @@ +import { SearchIcon } from "lucide-solid" + +import { Button } from "~/registry/ui/button" +import { ButtonGroup } from "~/registry/ui/button-group" +import { Input } from "~/registry/ui/input" + +export default function ButtonGroupInput() { + return ( + + + + + ) +} diff --git a/apps/docs/src/registry/examples/button-group-nested.tsx b/apps/docs/src/registry/examples/button-group-nested.tsx new file mode 100644 index 00000000..9bbe7035 --- /dev/null +++ b/apps/docs/src/registry/examples/button-group-nested.tsx @@ -0,0 +1,36 @@ +import { ArrowLeftIcon, ArrowRightIcon } from "lucide-solid" + +import { Button } from "~/registry/ui/button" +import { ButtonGroup } from "~/registry/ui/button-group" + +export default function ButtonGroupNested() { + return ( + + + + + + + + + + + + + + ) +} diff --git a/apps/docs/src/registry/examples/button-group-orientation.tsx b/apps/docs/src/registry/examples/button-group-orientation.tsx new file mode 100644 index 00000000..6426f633 --- /dev/null +++ b/apps/docs/src/registry/examples/button-group-orientation.tsx @@ -0,0 +1,17 @@ +import { MinusIcon, PlusIcon } from "lucide-solid" + +import { Button } from "~/registry/ui/button" +import { ButtonGroup } from "~/registry/ui/button-group" + +export default function ButtonGroupOrientation() { + return ( + + + + + ) +} diff --git a/apps/docs/src/registry/examples/button-group-select.tsx b/apps/docs/src/registry/examples/button-group-select.tsx new file mode 100644 index 00000000..3310fffa --- /dev/null +++ b/apps/docs/src/registry/examples/button-group-select.tsx @@ -0,0 +1,56 @@ +import { createSignal } from "solid-js" + +import { ArrowRightIcon } from "lucide-solid" + +import { Button } from "~/registry/ui/button" +import { ButtonGroup } from "~/registry/ui/button-group" +import { Input } from "~/registry/ui/input" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/registry/ui/select" + +const CURRENCIES = [ + { + value: "$", + label: "US Dollar" + }, + { + value: "€", + label: "Euro" + }, + { + value: "£", + label: "British Pound" + } +] + +export default function ButtonGroupSelect() { + const [currency, setCurrency] = createSignal("$") + + return ( + + + + + + + + + + ) +} diff --git a/apps/docs/src/registry/examples/button-group-separator.tsx b/apps/docs/src/registry/examples/button-group-separator.tsx new file mode 100644 index 00000000..fff0a803 --- /dev/null +++ b/apps/docs/src/registry/examples/button-group-separator.tsx @@ -0,0 +1,16 @@ +import { Button } from "~/registry/ui/button" +import { ButtonGroup, ButtonGroupSeparator } from "~/registry/ui/button-group" + +export default function ButtonGroupSeparatorDemo() { + return ( + + + + + + ) +} diff --git a/apps/docs/src/registry/examples/button-group-size.tsx b/apps/docs/src/registry/examples/button-group-size.tsx new file mode 100644 index 00000000..7de42e33 --- /dev/null +++ b/apps/docs/src/registry/examples/button-group-size.tsx @@ -0,0 +1,47 @@ +import { PlusIcon } from "lucide-solid" + +import { Button } from "~/registry/ui/button" +import { ButtonGroup } from "~/registry/ui/button-group" + +export default function ButtonGroupSize() { + return ( +
+ + + + + + + + + + + + + + + + + + +
+ ) +} diff --git a/apps/docs/src/registry/examples/button-group-split.tsx b/apps/docs/src/registry/examples/button-group-split.tsx new file mode 100644 index 00000000..9c63695e --- /dev/null +++ b/apps/docs/src/registry/examples/button-group-split.tsx @@ -0,0 +1,16 @@ +import { Plus } from "lucide-solid" + +import { Button } from "~/registry/ui/button" +import { ButtonGroup, ButtonGroupSeparator } from "~/registry/ui/button-group" + +export default function ButtonGroupSplit() { + return ( + + + + + + ) +} diff --git a/apps/docs/src/registry/ui/button-group.tsx b/apps/docs/src/registry/ui/button-group.tsx index 10eb5608..b493df91 100644 --- a/apps/docs/src/registry/ui/button-group.tsx +++ b/apps/docs/src/registry/ui/button-group.tsx @@ -13,7 +13,7 @@ import { cn } from "~/lib/utils" import { Separator } from "~/registry/ui/separator" const buttonGroupVariants = cva( - "cn-button-group flex w-fit items-stretch [&>*]:focus-visible:z-10 [&>*]:focus-visible:relative [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1 ", + "cn-button-group flex w-fit items-stretch [&>*]:focus-visible:relative [&>*]:focus-visible:z-10 [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1", { variants: { orientation: { @@ -35,10 +35,10 @@ const ButtonGroup: Component = (props) => { const [local, others] = splitProps(props, ["class", "orientation"]) return (
) @@ -50,10 +50,7 @@ const ButtonGroupText = ( const [local, others] = splitProps(props, ["class"]) return ( ) @@ -66,12 +63,12 @@ const ButtonGroupSeparator: Component = (rawProps) => const [local, others] = splitProps(props, ["class", "orientation"]) return ( ) diff --git a/apps/docs/src/routes/docs/components/button-group.mdx b/apps/docs/src/routes/docs/components/button-group.mdx index fef95873..8f1cf92f 100644 --- a/apps/docs/src/routes/docs/components/button-group.mdx +++ b/apps/docs/src/routes/docs/components/button-group.mdx @@ -53,44 +53,251 @@ import { ButtonGroup } from "~/components/ui/button-group"; ``` +## Accessibility + +- The button group has `role="group"` for proper semantic meaning +- Use Tab key to navigate between buttons +- Apply `aria-label` or `aria-labelledby` to the ButtonGroup for clear identification + +```tsx showLineNumbers + + + + +``` + +## ButtonGroup vs ToggleGroup + +- Use the `ButtonGroup` component when you want to group buttons that perform an action. +- Use the `ToggleGroup` component when you want to group buttons that toggle a state. + +## Examples + +### Orientation + +::::tab-group[orientation] +:::tab[Preview] + + + +::: +:::tab[Code] + +```file="~/registry/examples/button-group-orientation" frame=none showLineNumbers + +``` + +::: +:::: + +### Size + +::::tab-group[size] +:::tab[Preview] + + + +::: +:::tab[Code] + +```file="~/registry/examples/button-group-size" frame=none showLineNumbers + +``` + +::: +:::: + +### Nested + +::::tab-group[nested] +:::tab[Preview] + + + +::: +:::tab[Code] + +```file="~/registry/examples/button-group-nested" frame=none showLineNumbers + +``` + +::: +:::: + +### Separator + +::::tab-group[separator] +:::tab[Preview] + + + +::: +:::tab[Code] + +```file="~/registry/examples/button-group-separator" frame=none showLineNumbers + +``` + +::: +:::: + +### Split + +::::tab-group[split] +:::tab[Preview] + + + +::: +:::tab[Code] + +```file="~/registry/examples/button-group-split" frame=none showLineNumbers + +``` + +::: +:::: + +### Input + +::::tab-group[input] +:::tab[Preview] + + + +::: +:::tab[Code] + +```file="~/registry/examples/button-group-input" frame=none showLineNumbers + +``` + +::: +:::: + +### Input Group + +::::tab-group[input-group] +:::tab[Preview] + + + +::: +:::tab[Code] + +```file="~/registry/examples/button-group-input-group" frame=none showLineNumbers + +``` + +::: +:::: + +### Dropdown Menu + +::::tab-group[dropdown-menu] +:::tab[Preview] + + + +::: +:::tab[Code] + +```file="~/registry/examples/button-group-dropdown-menu" frame=none showLineNumbers + +``` + +::: +:::: + +### Select + +::::tab-group[select] +:::tab[Preview] + + + +::: +:::tab[Code] + +```file="~/registry/examples/button-group-select" frame=none showLineNumbers + +``` + +::: +:::: + ## API Reference ### ButtonGroup -Container component for grouping buttons. +The `ButtonGroup` component is a container that groups related buttons together with consistent styling. -| Prop | Type | Default | Description | -|------|------|---------|-------------| -| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | The layout direction of the button group | -| `class` | `string` | - | Additional CSS classes | +| Prop | Type | Default | +| ------------- | ---------------------------- | -------------- | +| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | + +```tsx + + + + +``` + +Nest multiple button groups to create complex layouts with spacing. See the [nested](#nested) example for more details. + +```tsx + + + + +``` ### ButtonGroupSeparator -A visual separator for dividing buttons within a group. +The `ButtonGroupSeparator` component visually divides buttons within a group. -| Prop | Type | Default | Description | -|------|------|---------|-------------| -| `orientation` | `"horizontal" \| "vertical"` | `"vertical"` | The orientation of the separator | -| `class` | `string` | - | Additional CSS classes | +| Prop | Type | Default | +| ------------- | ---------------------------- | ------------ | +| `orientation` | `"horizontal" \| "vertical"` | `"vertical"` | -### ButtonGroupText +```tsx + + + + + +``` -A text container component within a button group. +### ButtonGroupText -| Prop | Type | Default | Description | -|------|------|---------|-------------| -| `as` | `ValidComponent` | `"div"` | The component to render as (polymorphic) | -| `class` | `string` | - | Additional CSS classes | +Use this component to display text within a button group. -## Accessibility +| Prop | Type | Default | +| --------- | --------- | ------- | +| `asChild` | `boolean` | `false` | -- The button group has `role="group"` for proper semantic meaning -- Use Tab key to navigate between buttons -- Apply `aria-label` or `aria-labelledby` to the ButtonGroup for clear identification +```tsx + + Text + + +``` -## Notes +Use the `as` prop to render a custom component as the text, for example a label. + +```tsx showLineNumbers +import { ButtonGroupText } from "~/components/ui/button-group" +import { Label } from "~/components/ui/label" + +export function ButtonGroupTextDemo() { + return ( + + + Text + + + + ) +} +``` -- Button groups are used when you want to group buttons that perform actions -- For toggling state, consider using a Toggle Group component instead -- Buttons with the `outline` variant don't require separators as they already have visible borders -- Button groups can be nested for more complex layouts with spacing