{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "button-group", "title": "Button Group", "description": "Groups related buttons together with consistent spacing and connected styling.", "dependencies": [ "class-variance-authority" ], "registryDependencies": [ "button", "separator" ], "files": [ { "path": "packages/ui/src/button-group.tsx", "content": "\"use client\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport type * as React from \"react\";\n\nimport { cn } from \"./utils\";\n\n// =============================================================================\n// ButtonGroup\n// =============================================================================\nconst buttonGroupVariants = cva(\n [\n \"flex w-fit items-stretch rounded-lg shadow-xs\",\n \"*:focus-visible:relative *:focus-visible:z-10\",\n \"[&>[data-input]]:focus-within:relative [&>[data-input]]:focus-within:z-10\",\n \"[&>input]:flex-1\",\n \"[&_button]:active:scale-100!\",\n // Strip individual child shadows — the group container owns the shadow\n \"[&>[data-slot=button]]:shadow-none\",\n \"[&>[data-input]]:shadow-none\",\n \"[&>[data-slot=select-trigger]]:shadow-none\",\n \"[&>[data-slot=button-group-text]]:shadow-none\",\n // Nesting: add gap between nested ButtonGroups, remove parent shadow\n \"has-[>[data-slot=button-group]]:gap-2\",\n \"has-[>[data-slot=button-group]]:shadow-none\",\n \"[&>[data-slot=button-group]]:shadow-none\",\n // Select: handle hidden select element as last child\n \"has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-lg\",\n \"[&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit\",\n ].join(\" \"),\n {\n variants: {\n orientation: {\n horizontal: [\n // Rounding\n \"[&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-lg!\",\n \"[&>[data-slot]~[data-slot]]:rounded-l-none\",\n \"*:data-[slot]:rounded-r-none\",\n // Shared-edge border removal — both sides, uses ~ to skip hidden elements\n \"[&>[data-slot]~[data-slot]]:border-l-0\",\n \"[&>[data-slot]:has(~[data-slot])]:border-r-0\",\n // Universal separator for all adjacent slotted siblings\n \"[&>[data-slot]~[data-slot]]:before:content-['']\",\n \"[&>[data-slot]~[data-slot]]:before:absolute\",\n \"[&>[data-slot]~[data-slot]]:before:left-0\",\n \"[&>[data-slot]~[data-slot]]:before:top-[25%]\",\n \"[&>[data-slot]~[data-slot]]:before:w-px\",\n \"[&>[data-slot]~[data-slot]]:before:h-[50%]\",\n \"[&>[data-slot]~[data-slot]]:before:bg-current\",\n \"[&>[data-slot]~[data-slot]]:before:opacity-15\",\n \"[&>[data-slot]~[data-slot]]:before:rounded-sm\",\n // Button → input separator: is void and can't have ::before,\n // so place ::after on the preceding button instead\n \"[&>[data-slot]:has(+[data-input])]:after:content-['']\",\n \"[&>[data-slot]:has(+[data-input])]:after:absolute\",\n \"[&>[data-slot]:has(+[data-input])]:after:right-0\",\n \"[&>[data-slot]:has(+[data-input])]:after:top-[25%]\",\n \"[&>[data-slot]:has(+[data-input])]:after:w-px\",\n \"[&>[data-slot]:has(+[data-input])]:after:h-[50%]\",\n \"[&>[data-slot]:has(+[data-input])]:after:bg-current\",\n \"[&>[data-slot]:has(+[data-input])]:after:opacity-15\",\n \"[&>[data-slot]:has(+[data-input])]:after:rounded-sm\",\n ].join(\" \"),\n vertical: [\n \"flex-col\",\n // Rounding\n \"[&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-lg!\",\n \"[&>[data-slot]~[data-slot]]:rounded-t-none\",\n \"*:data-[slot]:rounded-b-none\",\n // Shared-edge border removal — both sides, uses ~ to skip hidden elements\n \"[&>[data-slot]~[data-slot]]:border-t-0\",\n \"[&>[data-slot]:has(~[data-slot])]:border-b-0\",\n // Universal separator for all adjacent slotted siblings\n \"[&>[data-slot]~[data-slot]]:before:content-['']\",\n \"[&>[data-slot]~[data-slot]]:before:absolute\",\n \"[&>[data-slot]~[data-slot]]:before:top-0\",\n \"[&>[data-slot]~[data-slot]]:before:left-[25%]\",\n \"[&>[data-slot]~[data-slot]]:before:h-px\",\n \"[&>[data-slot]~[data-slot]]:before:w-[50%]\",\n \"[&>[data-slot]~[data-slot]]:before:bg-current\",\n \"[&>[data-slot]~[data-slot]]:before:opacity-15\",\n \"[&>[data-slot]~[data-slot]]:before:rounded-sm\",\n // Button → input separator: is void and can't have ::before\n \"[&>[data-slot]:has(+[data-input])]:after:content-['']\",\n \"[&>[data-slot]:has(+[data-input])]:after:absolute\",\n \"[&>[data-slot]:has(+[data-input])]:after:bottom-0\",\n \"[&>[data-slot]:has(+[data-input])]:after:left-[25%]\",\n \"[&>[data-slot]:has(+[data-input])]:after:h-px\",\n \"[&>[data-slot]:has(+[data-input])]:after:w-[50%]\",\n \"[&>[data-slot]:has(+[data-input])]:after:bg-current\",\n \"[&>[data-slot]:has(+[data-input])]:after:opacity-15\",\n \"[&>[data-slot]:has(+[data-input])]:after:rounded-sm\",\n \"[&>[data-slot]~[data-slot]]:before:rounded-sm\",\n ].join(\" \"),\n },\n },\n defaultVariants: { orientation: \"horizontal\" },\n }\n);\n\nexport interface ButtonGroupProps\n extends React.HTMLAttributes,\n VariantProps {\n /**\n * The layout direction of the button group.\n * @default \"horizontal\"\n */\n orientation?: \"horizontal\" | \"vertical\";\n}\n\nconst ButtonGroup = ({\n className,\n orientation = \"horizontal\",\n ref,\n ...props\n}: ButtonGroupProps & React.RefAttributes) => (\n \n);\nButtonGroup.displayName = \"ButtonGroup\";\n\n// =============================================================================\n// ButtonGroupText\n// =============================================================================\nexport interface ButtonGroupTextProps\n extends React.HTMLAttributes {}\n\nconst ButtonGroupText = ({\n className,\n ref,\n ...props\n}: ButtonGroupTextProps & React.RefAttributes) => (\n \n);\nButtonGroupText.displayName = \"ButtonGroupText\";\n\nexport { ButtonGroup, ButtonGroupText, buttonGroupVariants };\n", "type": "registry:ui" } ], "type": "registry:ui" }