Back to buttons
import { Button as ButtonPrimitive } from "@base-ui/react/button"
import { cva, type VariantProps } from "class-variance-authority"

import { cn } from "@/lib/utils"

const buttonVariants = cva(
  "group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground hover:bg-primary/80",
        outline:
          "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
        secondary:
          "bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
        ghost:
          "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
        destructive:
          "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
        link: "text-primary underline-offset-4 hover:underline",
      },
      size: {
        default:
          "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
        xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
        sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
        lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
        icon: "size-8",
        "icon-xs":
          "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
        "icon-sm":
          "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
        "icon-lg": "size-9",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
)

function Button({
  className,
  variant = "default",
  size = "default",
  ...props
}: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
  return (
    <ButtonPrimitive
      data-slot="button"
      className={cn(buttonVariants({ variant, size, className }))}
      {...props}
    />
  )
}

export { Button, buttonVariants }

Button Group 12 - Action Dropdown

By ShadcnSpace

Try the interaction, copy the React source files, or download the complete example.

About Button Group 12 - Action Dropdown

Button Group 12 - Action Dropdown is an animated React and Tailwind CSS button by ShadcnSpace. Try the interaction, copy the React source, or download the example with creator credit.

Effects and categories
Hover effects, Animated
Using this example
Copy a TSX file or download the original source and standalone demo. Test keyboard focus, motion and contrast in your own project.

Usage, Tailwind and licensing questions

Read the source files

components/shadcn-space/button-group/button-group-12.tsx

"use client";

import { useState } from "react";
import { Button } from "@/components/ui/button";
import {
  ButtonGroup,
  ButtonGroupSeparator,
} from "@/components/ui/button-group";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import {
  ChevronDownIcon,
  PlayIcon,
  GitCommitIcon,
  GitPullRequestIcon,
  HistoryIcon,
} from "lucide-react";
import { motion } from "motion/react";

const menuVariants = {
  hidden: { opacity: 0, scale: 0.97, y: -4 },
  visible: {
    opacity: 1,
    scale: 1,
    y: 0,
    transition: {
      type: "spring",
      stiffness: 400,
      damping: 24,
      staggerChildren: 0.04,
      delayChildren: 0.02,
    },
  },
};

const itemVariants = {
  hidden: { opacity: 0, y: -5 },
  visible: {
    opacity: 1,
    y: 0,
    transition: {
      type: "spring",
      stiffness: 350,
      damping: 22,
    },
  },
};

const ActionExecuteButtonGroup = () => {
  const [open, setOpen] = useState(false);
  const [isHovered, setIsHovered] = useState(false);
  const [isTriggerHovered, setIsTriggerHovered] = useState(false);

  return (
    <ButtonGroup className="**:data-[slot=button]:border-r-0">
      <Button
        className="group relative overflow-hidden cursor-pointer"
        onMouseEnter={() => setIsHovered(true)}
        onMouseLeave={() => setIsHovered(false)}
      >
        <motion.span
          animate={isHovered ? { x: 2, scale: 1.05 } : { x: 0, scale: 1 }}
          transition={{ type: "spring", stiffness: 450, damping: 16 }}
          className="flex items-center justify-center mr-1"
        >
          <PlayIcon aria-hidden="true" className="fill-current size-4" />
        </motion.span>
        <span>Execute</span>
      </Button>
      <ButtonGroupSeparator className="bg-primary/72" />
      <DropdownMenu open={open} onOpenChange={setOpen}>
        <DropdownMenuTrigger
          render={
            <Button
              size="icon"
              className="border-primary-foreground/20 rounded-l-none border-l cursor-pointer"
              onMouseEnter={() => setIsTriggerHovered(true)}
              onMouseLeave={() => setIsTriggerHovered(false)}
            />
          }
        >
          <motion.div
            animate={{
              rotate: open ? 180 : 0,
            }}
            transition={{
              type: "spring",
              stiffness: 250,
              damping: 18,
            }}
            className="flex items-center justify-center"
          >
            <ChevronDownIcon aria-hidden="true" className="size-4" />
          </motion.div>
        </DropdownMenuTrigger>
        <DropdownMenuContent align="end" className="w-48">
          <motion.div
            initial="hidden"
            animate="visible"
            variants={menuVariants}
            className="flex flex-col gap-0.5"
          >
            <motion.div variants={itemVariants}>
              <DropdownMenuItem className="cursor-pointer">
                <GitCommitIcon aria-hidden="true" />
                <span>Commit & Push</span>
              </DropdownMenuItem>
            </motion.div>
            <motion.div variants={itemVariants}>
              <DropdownMenuItem className="cursor-pointer">
                <GitPullRequestIcon aria-hidden="true" />
                <span>Commit & Sync</span>
              </DropdownMenuItem>
            </motion.div>
            <motion.div variants={itemVariants}>
              <DropdownMenuItem className="cursor-pointer">
                <HistoryIcon aria-hidden="true" />
                <span>Amend Last Commit</span>
              </DropdownMenuItem>
            </motion.div>
          </motion.div>
        </DropdownMenuContent>
      </DropdownMenu>
    </ButtonGroup>
  );
}

export default ActionExecuteButtonGroup;

components/ui/button.tsx

import { Button as ButtonPrimitive } from "@base-ui/react/button"
import { cva, type VariantProps } from "class-variance-authority"

import { cn } from "@/lib/utils"

const buttonVariants = cva(
  "group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground hover:bg-primary/80",
        outline:
          "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
        secondary:
          "bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
        ghost:
          "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
        destructive:
          "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
        link: "text-primary underline-offset-4 hover:underline",
      },
      size: {
        default:
          "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
        xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
        sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
        lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
        icon: "size-8",
        "icon-xs":
          "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
        "icon-sm":
          "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
        "icon-lg": "size-9",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
)

function Button({
  className,
  variant = "default",
  size = "default",
  ...props
}: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
  return (
    <ButtonPrimitive
      data-slot="button"
      className={cn(buttonVariants({ variant, size, className }))}
      {...props}
    />
  )
}

export { Button, buttonVariants }

components/ui/dropdown-menu.tsx

"use client"

import * as React from "react"
import { Menu as MenuPrimitive } from "@base-ui/react/menu"

import { cn } from "@/lib/utils"
import { ChevronRightIcon, CheckIcon } from "lucide-react"

function DropdownMenu({ ...props }: MenuPrimitive.Root.Props) {
  return <MenuPrimitive.Root data-slot="dropdown-menu" {...props} />
}

function DropdownMenuPortal({ ...props }: MenuPrimitive.Portal.Props) {
  return <MenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
}

function DropdownMenuTrigger({ ...props }: MenuPrimitive.Trigger.Props) {
  return <MenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />
}

function DropdownMenuContent({
  align = "start",
  alignOffset = 0,
  side = "bottom",
  sideOffset = 4,
  className,
  ...props
}: MenuPrimitive.Popup.Props &
  Pick<
    MenuPrimitive.Positioner.Props,
    "align" | "alignOffset" | "side" | "sideOffset"
  >) {
  return (
    <MenuPrimitive.Portal>
      <MenuPrimitive.Positioner
        className="isolate z-50 outline-none"
        align={align}
        alignOffset={alignOffset}
        side={side}
        sideOffset={sideOffset}
      >
        <MenuPrimitive.Popup
          data-slot="dropdown-menu-content"
          className={cn("z-50 max-h-(--available-height) w-(--anchor-width) min-w-32 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:overflow-hidden data-closed:fade-out-0 data-closed:zoom-out-95", className )}
          {...props}
        />
      </MenuPrimitive.Positioner>
    </MenuPrimitive.Portal>
  )
}

function DropdownMenuGroup({ ...props }: MenuPrimitive.Group.Props) {
  return <MenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
}

function DropdownMenuLabel({
  className,
  inset,
  ...props
}: MenuPrimitive.GroupLabel.Props & {
  inset?: boolean
}) {
  return (
    <MenuPrimitive.GroupLabel
      data-slot="dropdown-menu-label"
      data-inset={inset}
      className={cn(
        "px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-7",
        className
      )}
      {...props}
    />
  )
}

function DropdownMenuItem({
  className,
  inset,
  variant = "default",
  ...props
}: MenuPrimitive.Item.Props & {
  inset?: boolean
  variant?: "default" | "destructive"
}) {
  return (
    <MenuPrimitive.Item
      data-slot="dropdown-menu-item"
      data-inset={inset}
      data-variant={variant}
      className={cn(
        "group/dropdown-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive",
        className
      )}
      {...props}
    />
  )
}

function DropdownMenuSub({ ...props }: MenuPrimitive.SubmenuRoot.Props) {
  return <MenuPrimitive.SubmenuRoot data-slot="dropdown-menu-sub" {...props} />
}

function DropdownMenuSubTrigger({
  className,
  inset,
  children,
  ...props
}: MenuPrimitive.SubmenuTrigger.Props & {
  inset?: boolean
}) {
  return (
    <MenuPrimitive.SubmenuTrigger
      data-slot="dropdown-menu-sub-trigger"
      data-inset={inset}
      className={cn(
        "flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-popup-open:bg-accent data-popup-open:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
        className
      )}
      {...props}
    >
      {children}
      <ChevronRightIcon className="ml-auto" />
    </MenuPrimitive.SubmenuTrigger>
  )
}

function DropdownMenuSubContent({
  align = "start",
  alignOffset = -3,
  side = "right",
  sideOffset = 0,
  className,
  ...props
}: React.ComponentProps<typeof DropdownMenuContent>) {
  return (
    <DropdownMenuContent
      data-slot="dropdown-menu-sub-content"
      className={cn("w-auto min-w-[96px] rounded-lg bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
      align={align}
      alignOffset={alignOffset}
      side={side}
      sideOffset={sideOffset}
      {...props}
    />
  )
}

function DropdownMenuCheckboxItem({
  className,
  children,
  checked,
  inset,
  ...props
}: MenuPrimitive.CheckboxItem.Props & {
  inset?: boolean
}) {
  return (
    <MenuPrimitive.CheckboxItem
      data-slot="dropdown-menu-checkbox-item"
      data-inset={inset}
      className={cn(
        "relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
        className
      )}
      checked={checked}
      {...props}
    >
      <span
        className="pointer-events-none absolute right-2 flex items-center justify-center"
        data-slot="dropdown-menu-checkbox-item-indicator"
      >
        <MenuPrimitive.CheckboxItemIndicator>
          <CheckIcon
          />
        </MenuPrimitive.CheckboxItemIndicator>
      </span>
      {children}
    </MenuPrimitive.CheckboxItem>
  )
}

function DropdownMenuRadioGroup({ ...props }: MenuPrimitive.RadioGroup.Props) {
  return (
    <MenuPrimitive.RadioGroup
      data-slot="dropdown-menu-radio-group"
      {...props}
    />
  )
}

function DropdownMenuRadioItem({
  className,
  children,
  inset,
  ...props
}: MenuPrimitive.RadioItem.Props & {
  inset?: boolean
}) {
  return (
    <MenuPrimitive.RadioItem
      data-slot="dropdown-menu-radio-item"
      data-inset={inset}
      className={cn(
        "relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
        className
      )}
      {...props}
    >
      <span
        className="pointer-events-none absolute right-2 flex items-center justify-center"
        data-slot="dropdown-menu-radio-item-indicator"
      >
        <MenuPrimitive.RadioItemIndicator>
          <CheckIcon
          />
        </MenuPrimitive.RadioItemIndicator>
      </span>
      {children}
    </MenuPrimitive.RadioItem>
  )
}

function DropdownMenuSeparator({
  className,
  ...props
}: MenuPrimitive.Separator.Props) {
  return (
    <MenuPrimitive.Separator
      data-slot="dropdown-menu-separator"
      className={cn("-mx-1 my-1 h-px bg-border", className)}
      {...props}
    />
  )
}

function DropdownMenuShortcut({
  className,
  ...props
}: React.ComponentProps<"span">) {
  return (
    <span
      data-slot="dropdown-menu-shortcut"
      className={cn(
        "ml-auto text-xs tracking-widest text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground",
        className
      )}
      {...props}
    />
  )
}

export {
  DropdownMenu,
  DropdownMenuPortal,
  DropdownMenuTrigger,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuLabel,
  DropdownMenuItem,
  DropdownMenuCheckboxItem,
  DropdownMenuRadioGroup,
  DropdownMenuRadioItem,
  DropdownMenuSeparator,
  DropdownMenuShortcut,
  DropdownMenuSub,
  DropdownMenuSubTrigger,
  DropdownMenuSubContent,
}

components/ui/button-group.tsx

import { mergeProps } from "@base-ui/react/merge-props"
import { useRender } from "@base-ui/react/use-render"
import { cva, type VariantProps } from "class-variance-authority"

import { cn } from "@/lib/utils"
import { Separator } from "@/components/ui/separator"

const buttonGroupVariants = cva(
  "flex w-fit items-stretch *:focus-visible:relative *:focus-visible:z-10 has-[>[data-slot=button-group]]:gap-2 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-lg [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1",
  {
    variants: {
      orientation: {
        horizontal:
          "*:data-slot:rounded-r-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-lg! [&>[data-slot]~[data-slot]]:rounded-l-none [&>[data-slot]~[data-slot]]:border-l-0",
        vertical:
          "flex-col *:data-slot:rounded-b-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-lg! [&>[data-slot]~[data-slot]]:rounded-t-none [&>[data-slot]~[data-slot]]:border-t-0",
      },
    },
    defaultVariants: {
      orientation: "horizontal",
    },
  }
)

function ButtonGroup({
  className,
  orientation,
  ...props
}: React.ComponentProps<"div"> & VariantProps<typeof buttonGroupVariants>) {
  return (
    <div
      role="group"
      data-slot="button-group"
      data-orientation={orientation}
      className={cn(buttonGroupVariants({ orientation }), className)}
      {...props}
    />
  )
}

function ButtonGroupText({
  className,
  render,
  ...props
}: useRender.ComponentProps<"div">) {
  return useRender({
    defaultTagName: "div",
    props: mergeProps<"div">(
      {
        className: cn(
          "flex items-center gap-2 rounded-lg border bg-muted px-2.5 text-sm font-medium [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
          className
        ),
      },
      props
    ),
    render,
    state: {
      slot: "button-group-text",
    },
  })
}

function ButtonGroupSeparator({
  className,
  orientation = "vertical",
  ...props
}: React.ComponentProps<typeof Separator>) {
  return (
    <Separator
      data-slot="button-group-separator"
      orientation={orientation}
      className={cn(
        "relative self-stretch bg-input data-horizontal:mx-px data-horizontal:w-auto data-vertical:my-px data-vertical:h-auto",
        className
      )}
      {...props}
    />
  )
}

export {
  ButtonGroup,
  ButtonGroupSeparator,
  ButtonGroupText,
  buttonGroupVariants,
}

lib/utils.ts

import { config } from "@/config";
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

export function capitalize(str: string) {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
type GroupBy<T, K extends keyof T> = Record<string, T[]>;

export function groupBy<T, K extends keyof T>(
  array: T[],
  key: K
): GroupBy<T, K> {
  return array.reduce((acc, item) => {
    const keyValue = String(item[key]);
    if (!acc[keyValue]) {
      acc[keyValue] = [];
    }
    acc[keyValue].push(item);
    return acc;
  }, {} as GroupBy<T, K>);
}

export function absoluteUrl(path: string) {
  return process.env.NODE_ENV === "development"
    ? `http://localhost:3000/${path}`
    : `${config.appUrl}/${path}`;
}

components/ui/separator.tsx

"use client"

import { Separator as SeparatorPrimitive } from "@base-ui/react/separator"

import { cn } from "@/lib/utils"

function Separator({
  className,
  orientation = "horizontal",
  ...props
}: SeparatorPrimitive.Props) {
  return (
    <SeparatorPrimitive
      data-slot="separator"
      orientation={orientation}
      className={cn(
        "shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
        className
      )}
      {...props}
    />
  )
}

export { Separator }

config.ts

export const config = {
  appUrl:
    process.env.NODE_ENV === "production"
      ? process.env.VERCEL_PROJECT_PRODUCTION_URL ??
      process.env.NEXT_PUBLIC_APP_URL!
      : "http://localhost:3000",
  social: {
    github: "https://github.com/shadcnspace/shadcnspace",
    twitter: "https://twitter.com/",
  },
};

demo.tsx

export { default as default } from "@/components/shadcn-space/button-group/button-group-12";

CSS

/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
@layer properties;
@layer theme, base, components, utilities;
@layer theme {
  :root, :host {
    --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
      "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
      "Segoe UI Symbol", "Noto Color Emoji";
    --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
      "Courier New", monospace;
    --spacing: 0.25rem;
    --text-xs: 0.75rem;
    --text-xs--line-height: calc(1 / 0.75);
    --text-sm: 0.875rem;
    --text-sm--line-height: calc(1.25 / 0.875);
    --font-weight-medium: 500;
    --tracking-widest: 0.1em;
    --radius-md: calc(var(--radius) - 2px);
    --default-transition-duration: 150ms;
    --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
    --default-font-family: var(--font-sans);
    --default-font-feature-settings: "cv02", "cv03", "cv04", "cv11";
    --default-mono-font-family: var(--font-mono);
  }
}
@layer base {
  *, ::after, ::before, ::backdrop, ::file-selector-button {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
    border: 0 solid;
  }
  html, :host {
    line-height: 1.5;
    -webkit-text-size-adjust: 100%;
    tab-size: 4;
    font-family: var(--default-font-family, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");
    font-feature-settings: var(--default-font-feature-settings, normal);
    font-variation-settings: var(--default-font-variation-settings, normal);
    -webkit-tap-highlight-color: transparent;
  }
  hr {
    height: 0;
    color: inherit;
    border-top-width: 1px;
  }
  abbr:where([title]) {
    -webkit-text-decoration: underline dotted;
    text-decoration: underline dotted;
  }
  h1, h2, h3, h4, h5, h6 {
    font-size: inherit;
    font-weight: inherit;
  }
  a {
    color: inherit;
    -webkit-text-decoration: inherit;
    text-decoration: inherit;
  }
  b, strong {
    font-weight: bolder;
  }
  code, kbd, samp, pre {
    font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);
    font-feature-settings: var(--default-mono-font-feature-settings, normal);
    font-variation-settings: var(--default-mono-font-variation-settings, normal);
    font-size: 1em;
  }
  small {
    font-size: 80%;
  }
  sub, sup {
    font-size: 75%;
    line-height: 0;
    position: relative;
    vertical-align: baseline;
  }
  sub {
    bottom: -0.25em;
  }
  sup {
    top: -0.5em;
  }
  table {
    text-indent: 0;
    border-color: inherit;
    border-collapse: collapse;
  }
  :-moz-focusring:where(:not(iframe)) {
    outline: auto;
  }
  progress {
    vertical-align: baseline;
  }
  summary {
    display: list-item;
  }
  ol, ul, menu {
    list-style: none;
  }
  img, svg, video, canvas, audio, iframe, embed, object {
    display: block;
    vertical-align: middle;
  }
  img, video {
    max-width: 100%;
    height: auto;
  }
  button, input, select, optgroup, textarea, ::file-selector-button {
    font: inherit;
    font-feature-settings: inherit;
    font-variation-settings: inherit;
    letter-spacing: inherit;
    color: inherit;
    border-radius: 0;
    background-color: transparent;
    opacity: 1;
  }
  :where(select:is([multiple], [size])) optgroup {
    font-weight: bolder;
  }
  :where(select:is([multiple], [size])) optgroup option {
    padding-inline-start: 20px;
  }
  ::file-selector-button {
    margin-inline-end: 4px;
  }
  ::placeholder {
    opacity: 1;
  }
  @supports (not (-webkit-appearance: -apple-pay-button))  or (contain-intrinsic-size: 1px) {
    ::placeholder {
      color: currentcolor;
      @supports (color: color-mix(in lab, red, red)) {
        color: color-mix(in oklab, currentcolor 50%, transparent);
      }
    }
  }
  textarea {
    resize: vertical;
  }
  ::-webkit-search-decoration {
    -webkit-appearance: none;
  }
  ::-webkit-date-and-time-value {
    min-height: 1lh;
    text-align: inherit;
  }
  ::-webkit-datetime-edit {
    display: inline-flex;
  }
  ::-webkit-datetime-edit-fields-wrapper {
    padding: 0;
  }
  ::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field {
    padding-block: 0;
  }
  ::-webkit-calendar-picker-indicator {
    line-height: 1;
  }
  :-moz-ui-invalid {
    box-shadow: none;
  }
  button, input:where([type="button"], [type="reset"], [type="submit"]), ::file-selector-button {
    appearance: button;
  }
  ::-webkit-inner-spin-button, ::-webkit-outer-spin-button {
    height: auto;
  }
  [hidden]:where(:not([hidden="until-found"])) {
    display: none !important;
  }
}
@layer utilities {
  .pointer-events-none {
    pointer-events: none;
  }
  .visible {
    visibility: visible;
  }
  .absolute {
    position: absolute;
  }
  .relative {
    position: relative;
  }
  .right-2 {
    right: calc(var(--spacing) * 2);
  }
  .isolate {
    isolation: isolate;
  }
  .z-50 {
    z-index: 50;
  }
  .-mx-1 {
    margin-inline: calc(var(--spacing) * -1);
  }
  .my-1 {
    margin-block: var(--spacing);
  }
  .mr-1 {
    margin-right: var(--spacing);
  }
  .ml-auto {
    margin-left: auto;
  }
  .flex {
    display: flex;
  }
  .hidden {
    display: none;
  }
  .inline-flex {
    display: inline-flex;
  }
  .size-4 {
    width: calc(var(--spacing) * 4);
    height: calc(var(--spacing) * 4);
  }
  .size-6 {
    width: calc(var(--spacing) * 6);
    height: calc(var(--spacing) * 6);
  }
  .size-7 {
    width: calc(var(--spacing) * 7);
    height: calc(var(--spacing) * 7);
  }
  .size-8 {
    width: calc(var(--spacing) * 8);
    height: calc(var(--spacing) * 8);
  }
  .size-9 {
    width: calc(var(--spacing) * 9);
    height: calc(var(--spacing) * 9);
  }
  .h-6 {
    height: calc(var(--spacing) * 6);
  }
  .h-7 {
    height: calc(var(--spacing) * 7);
  }
  .h-8 {
    height: calc(var(--spacing) * 8);
  }
  .h-9 {
    height: calc(var(--spacing) * 9);
  }
  .h-px {
    height: 1px;
  }
  .max-h-\(--available-height\) {
    max-height: var(--available-height);
  }
  .w-\(--anchor-width\) {
    width: var(--anchor-width);
  }
  .w-48 {
    width: calc(var(--spacing) * 48);
  }
  .w-auto {
    width: auto;
  }
  .w-fit {
    width: fit-content;
  }
  .min-w-32 {
    min-width: calc(var(--spacing) * 32);
  }
  .min-w-\[96px\] {
    min-width: 96px;
  }
  .shrink-0 {
    flex-shrink: 0;
  }
  .origin-\(--transform-origin\) {
    transform-origin: var(--transform-origin);
  }
  .cursor-default {
    cursor: default;
  }
  .cursor-pointer {
    cursor: pointer;
  }
  .flex-col {
    flex-direction: column;
  }
  .items-center {
    align-items: center;
  }
  .items-stretch {
    align-items: stretch;
  }
  .justify-center {
    justify-content: center;
  }
  .gap-0\.5 {
    gap: calc(var(--spacing) * 0.5);
  }
  .gap-1 {
    gap: var(--spacing);
  }
  .gap-1\.5 {
    gap: calc(var(--spacing) * 1.5);
  }
  .gap-2 {
    gap: calc(var(--spacing) * 2);
  }
  .self-stretch {
    align-self: stretch;
  }
  .overflow-hidden {
    overflow: hidden;
  }
  .overflow-x-hidden {
    overflow-x: hidden;
  }
  .overflow-y-auto {
    overflow-y: auto;
  }
  .rounded-\[min\(var\(--radius-md\)\,10px\)\] {
    border-radius: min(var(--radius-md), 10px);
  }
  .rounded-\[min\(var\(--radius-md\)\,12px\)\] {
    border-radius: min(var(--radius-md), 12px);
  }
  .rounded-lg {
    border-radius: var(--radius);
  }
  .rounded-md {
    border-radius: calc(var(--radius) - 2px);
  }
  .rounded-l-none {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
  }
  .border {
    border-style: var(--tw-border-style);
    border-width: 1px;
  }
  .border-l {
    border-left-style: var(--tw-border-style);
    border-left-width: 1px;
  }
  .border-border {
    border-color: var(--border);
  }
  .border-primary-foreground\/20 {
    border-color: var(--primary-foreground);
    @supports (color: color-mix(in lab, red, red)) {
      border-color: color-mix(in oklab, var(--primary-foreground) 20%, transparent);
    }
  }
  .border-transparent {
    border-color: transparent;
  }
  .bg-background {
    background-color: var(--background);
  }
  .bg-border {
    background-color: var(--border);
  }
  .bg-destructive\/10 {
    background-color: var(--destructive);
    @supports (color: color-mix(in lab, red, red)) {
      background-color: color-mix(in oklab, var(--destructive) 10%, transparent);
    }
  }
  .bg-input {
    background-color: var(--input);
  }
  .bg-muted {
    background-color: var(--muted);
  }
  .bg-popover {
    background-color: var(--popover);
  }
  .bg-primary {
    background-color: var(--primary);
  }
  .bg-primary\/72 {
    background-color: var(--primary);
    @supports (color: color-mix(in lab, red, red)) {
      background-color: color-mix(in oklab, var(--primary) 72%, transparent);
    }
  }
  .bg-secondary {
    background-color: var(--secondary);
  }
  .bg-clip-padding {
    background-clip: padding-box;
  }
  .fill-current {
    fill: currentcolor;
  }
  .p-1 {
    padding: var(--spacing);
  }
  .px-1\.5 {
    padding-inline: calc(var(--spacing) * 1.5);
  }
  .px-2 {
    padding-inline: calc(var(--spacing) * 2);
  }
  .px-2\.5 {
    padding-inline: calc(var(--spacing) * 2.5);
  }
  .py-1 {
    padding-block: var(--spacing);
  }
  .pr-8 {
    padding-right: calc(var(--spacing) * 8);
  }
  .pl-1\.5 {
    padding-left: calc(var(--spacing) * 1.5);
  }
  .text-sm {
    font-size: var(--text-sm);
    line-height: var(--tw-leading, var(--text-sm--line-height));
  }
  .text-xs {
    font-size: var(--text-xs);
    line-height: var(--tw-leading, var(--text-xs--line-height));
  }
  .text-\[0\.8rem\] {
    font-size: 0.8rem;
  }
  .font-medium {
    --tw-font-weight: var(--font-weight-medium);
    font-weight: var(--font-weight-medium);
  }
  .tracking-widest {
    --tw-tracking: var(--tracking-widest);
    letter-spacing: var(--tracking-widest);
  }
  .whitespace-nowrap {
    white-space: nowrap;
  }
  .text-destructive {
    color: var(--destructive);
  }
  .text-muted-foreground {
    color: var(--muted-foreground);
  }
  .text-popover-foreground {
    color: var(--popover-foreground);
  }
  .text-primary {
    color: var(--primary);
  }
  .text-primary-foreground {
    color: var(--primary-foreground);
  }
  .text-secondary-foreground {
    color: var(--secondary-foreground);
  }
  .underline-offset-4 {
    text-underline-offset: 4px;
  }
  .shadow-lg {
    --tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
    box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
  }
  .shadow-md {
    --tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
    box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
  }
  .ring-1 {
    --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
    box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
  }
  .ring-foreground\/10 {
    --tw-ring-color: var(--foreground);
    @supports (color: color-mix(in lab, red, red)) {
      --tw-ring-color: color-mix(in oklab, var(--foreground) 10%, transparent);
    }
  }
  .outline-hidden {
    --tw-outline-style: none;
    outline-style: none;
    @media (forced-colors: active) {
      outline: 2px solid transparent;
      outline-offset: 2px;
    }
  }
  .outline {
    outline-style: var(--tw-outline-style);
    outline-width: 1px;
  }
  .transition {
    transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, content-visibility, overlay, pointer-events;
    transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
    transition-duration: var(--tw-duration, var(--default-transition-duration));
  }
  .transition-all {
    transition-property: all;
    transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
    transition-duration: var(--tw-duration, var(--default-transition-duration));
  }
  .duration-100 {
    --tw-duration: 100ms;
    transition-duration: 100ms;
  }
  .outline-none {
    --tw-outline-style: none;
    outline-style: none;
  }
  .select-none {
    -webkit-user-select: none;
    user-select: none;
  }
  .group-focus\/dropdown-menu-item\:text-accent-foreground:is(:where(.group\/dropdown-menu-item):focus *) {
    color: var(--accent-foreground);
  }
  @media (hover: hover) {
    .hover\:bg-\[color-mix\(in_oklch\,var\(--secondary\)\,var\(--foreground\)_5\%\)\]:hover {
      background-color: var(--secondary);
    }
    @supports (color: color-mix(in lab, red, red)) {
      .hover\:bg-\[color-mix\(in_oklch\,var\(--secondary\)\,var\(--foreground\)_5\%\)\]:hover {
        background-color: color-mix(in oklch,var(--secondary),var(--foreground) 5%);
      }
    }
    .hover\:bg-destructive\/20:hover {
      background-color: var(--destructive);
    }
    @supports (color: color-mix(in lab, red, red)) {
      .hover\:bg-destructive\/20:hover {
        background-color: color-mix(in oklab, var(--destructive) 20%, transparent);
      }
    }
    .hover\:bg-muted:hover {
      background-color: var(--muted);
    }
    .hover\:bg-primary\/80:hover {
      background-color: var(--primary);
    }
    @supports (color: color-mix(in lab, red, red)) {
      .hover\:bg-primary\/80:hover {
        background-color: color-mix(in oklab, var(--primary) 80%, transparent);
      }
    }
    .hover\:text-foreground:hover {
      color: var(--foreground);
    }
    .hover\:underline:hover {
      text-decoration-line: underline;
    }
  }
  .focus\:bg-accent:focus {
    background-color: var(--accent);
  }
  .focus\:text-accent-foreground:focus {
    color: var(--accent-foreground);
  }
  :is(.focus\:\*\*\:text-accent-foreground:focus *) {
    color: var(--accent-foreground);
  }
  :is(.not-data-\[variant\=destructive\]\:focus\:\*\*\:text-accent-foreground:not([data-variant="destructive"]):focus *) {
    color: var(--accent-foreground);
  }
  .focus-visible\:border-destructive\/40:focus-visible {
    border-color: var(--destructive);
    @supports (color: color-mix(in lab, red, red)) {
      border-color: color-mix(in oklab, var(--destructive) 40%, transparent);
    }
  }
  .focus-visible\:border-ring:focus-visible {
    border-color: var(--ring);
  }
  .focus-visible\:ring-3:focus-visible {
    --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
    box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
  }
  .focus-visible\:ring-destructive\/20:focus-visible {
    --tw-ring-color: var(--destructive);
    @supports (color: color-mix(in lab, red, red)) {
      --tw-ring-color: color-mix(in oklab, var(--destructive) 20%, transparent);
    }
  }
  .focus-visible\:ring-ring\/50:focus-visible {
    --tw-ring-color: var(--ring);
    @supports (color: color-mix(in lab, red, red)) {
      --tw-ring-color: color-mix(in oklab, var(--ring) 50%, transparent);
    }
  }
  :is(.\*\:focus-visible\:relative > *):focus-visible {
    position: relative;
  }
  :is(.\*\:focus-visible\:z-10 > *):focus-visible {
    z-index: 10;
  }
  .active\:not-aria-\[haspopup\]\:translate-y-px:active:not([aria-haspopup]) {
    --tw-translate-y: 1px;
    translate: var(--tw-translate-x) var(--tw-translate-y);
  }
  .disabled\:pointer-events-none:disabled {
    pointer-events: none;
  }
  .disabled\:opacity-50:disabled {
    opacity: 50%;
  }
  :where([data-slot="button-group"]) .in-data-\[slot\=button-group\]\:rounded-lg {
    border-radius: var(--radius);
  }
  .has-data-\[icon\=inline-end\]\:pr-1\.5:has([data-icon="inline-end"]) {
    padding-right: calc(var(--spacing) * 1.5);
  }
  .has-data-\[icon\=inline-end\]\:pr-2:has([data-icon="inline-end"]) {
    padding-right: calc(var(--spacing) * 2);
  }
  .has-data-\[icon\=inline-start\]\:pl-1\.5:has([data-icon="inline-start"]) {
    padding-left: calc(var(--spacing) * 1.5);
  }
  .has-data-\[icon\=inline-start\]\:pl-2:has([data-icon="inline-start"]) {
    padding-left: calc(var(--spacing) * 2);
  }
  .has-\[\>\[data-slot\=button-group\]\]\:gap-2:has( > [data-slot=button-group]) {
    gap: calc(var(--spacing) * 2);
  }
  .aria-expanded\:bg-muted[aria-expanded="true"] {
    background-color: var(--muted);
  }
  .aria-expanded\:bg-secondary[aria-expanded="true"] {
    background-color: var(--secondary);
  }
  .aria-expanded\:text-foreground[aria-expanded="true"] {
    color: var(--foreground);
  }
  .aria-expanded\:text-secondary-foreground[aria-expanded="true"] {
    color: var(--secondary-foreground);
  }
  .aria-invalid\:border-destructive[aria-invalid="true"] {
    border-color: var(--destructive);
  }
  .aria-invalid\:ring-3[aria-invalid="true"] {
    --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
    box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
  }
  .aria-invalid\:ring-destructive\/20[aria-invalid="true"] {
    --tw-ring-color: var(--destructive);
    @supports (color: color-mix(in lab, red, red)) {
      --tw-ring-color: color-mix(in oklab, var(--destructive) 20%, transparent);
    }
  }
  .data-closed\:overflow-hidden[data-closed] {
    overflow: hidden;
  }
  .data-disabled\:pointer-events-none[data-disabled] {
    pointer-events: none;
  }
  .data-disabled\:opacity-50[data-disabled] {
    opacity: 50%;
  }
  .data-horizontal\:mx-px[data-horizontal] {
    margin-inline: 1px;
  }
  .data-horizontal\:h-px[data-horizontal] {
    height: 1px;
  }
  .data-horizontal\:w-auto[data-horizontal] {
    width: auto;
  }
  .data-horizontal\:w-full[data-horizontal] {
    width: 100%;
  }
  .data-inset\:pl-7[data-inset] {
    padding-left: calc(var(--spacing) * 7);
  }
  .data-open\:bg-accent[data-open] {
    background-color: var(--accent);
  }
  .data-open\:text-accent-foreground[data-open] {
    color: var(--accent-foreground);
  }
  .data-popup-open\:bg-accent[data-popup-open] {
    background-color: var(--accent);
  }
  .data-popup-open\:text-accent-foreground[data-popup-open] {
    color: var(--accent-foreground);
  }
  :is(.\*\:data-slot\:rounded-r-none > *)[data-slot] {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
  }
  :is(.\*\:data-slot\:rounded-b-none > *)[data-slot] {
    border-bottom-right-radius: 0;
    border-bottom-left-radius: 0;
  }
  .data-vertical\:my-px[data-vertical] {
    margin-block: 1px;
  }
  .data-vertical\:h-auto[data-vertical] {
    height: auto;
  }
  .data-vertical\:w-px[data-vertical] {
    width: 1px;
  }
  .data-vertical\:self-stretch[data-vertical] {
    align-self: stretch;
  }
  :is(.\*\*\:data-\[slot\=button\]\:border-r-0 *)[data-slot="button"] {
    border-right-style: var(--tw-border-style);
    border-right-width: 0px;
  }
  .data-\[variant\=destructive\]\:text-destructive[data-variant="destructive"] {
    color: var(--destructive);
  }
  .data-\[variant\=destructive\]\:focus\:bg-destructive\/10[data-variant="destructive"]:focus {
    background-color: var(--destructive);
    @supports (color: color-mix(in lab, red, red)) {
      background-color: color-mix(in oklab, var(--destructive) 10%, transparent);
    }
  }
  .data-\[variant\=destructive\]\:focus\:text-destructive[data-variant="destructive"]:focus {
    color: var(--destructive);
  }
  .dark\:border-input:where(.dark, .dark *) {
    border-color: var(--input);
  }
  .dark\:bg-destructive\/20:where(.dark, .dark *) {
    background-color: var(--destructive);
    @supports (color: color-mix(in lab, red, red)) {
      background-color: color-mix(in oklab, var(--destructive) 20%, transparent);
    }
  }
  .dark\:bg-input\/30:where(.dark, .dark *) {
    background-color: var(--input);
    @supports (color: color-mix(in lab, red, red)) {
      background-color: color-mix(in oklab, var(--input) 30%, transparent);
    }
  }
  @media (hover: hover) {
    .dark\:hover\:bg-destructive\/30:where(.dark, .dark *):hover {
      background-color: var(--destructive);
    }
    @supports (color: color-mix(in lab, red, red)) {
      .dark\:hover\:bg-destructive\/30:where(.dark, .dark *):hover {
        background-color: color-mix(in oklab, var(--destructive) 30%, transparent);
      }
    }
    .dark\:hover\:bg-input\/50:where(.dark, .dark *):hover {
      background-color: var(--input);
    }
    @supports (color: color-mix(in lab, red, red)) {
      .dark\:hover\:bg-input\/50:where(.dark, .dark *):hover {
        background-color: color-mix(in oklab, var(--input) 50%, transparent);
      }
    }
    .dark\:hover\:bg-muted\/50:where(.dark, .dark *):hover {
      background-color: var(--muted);
    }
    @supports (color: color-mix(in lab, red, red)) {
      .dark\:hover\:bg-muted\/50:where(.dark, .dark *):hover {
        background-color: color-mix(in oklab, var(--muted) 50%, transparent);
      }
    }
  }
  .dark\:focus-visible\:ring-destructive\/40:where(.dark, .dark *):focus-visible {
    --tw-ring-color: var(--destructive);
    @supports (color: color-mix(in lab, red, red)) {
      --tw-ring-color: color-mix(in oklab, var(--destructive) 40%, transparent);
    }
  }
  .dark\:aria-invalid\:border-destructive\/50:where(.dark, .dark *)[aria-invalid="true"] {
    border-color: var(--destructive);
    @supports (color: color-mix(in lab, red, red)) {
      border-color: color-mix(in oklab, var(--destructive) 50%, transparent);
    }
  }
  .dark\:aria-invalid\:ring-destructive\/40:where(.dark, .dark *)[aria-invalid="true"] {
    --tw-ring-color: var(--destructive);
    @supports (color: color-mix(in lab, red, red)) {
      --tw-ring-color: color-mix(in oklab, var(--destructive) 40%, transparent);
    }
  }
  .dark\:data-\[variant\=destructive\]\:focus\:bg-destructive\/20:where(.dark, .dark *)[data-variant="destructive"]:focus {
    background-color: var(--destructive);
    @supports (color: color-mix(in lab, red, red)) {
      background-color: color-mix(in oklab, var(--destructive) 20%, transparent);
    }
  }
  .\[\&_svg\]\:pointer-events-none svg {
    pointer-events: none;
  }
  .\[\&_svg\]\:shrink-0 svg {
    flex-shrink: 0;
  }
  .\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-3 svg:not([class*='size-']) {
    width: calc(var(--spacing) * 3);
    height: calc(var(--spacing) * 3);
  }
  .\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-3\.5 svg:not([class*='size-']) {
    width: calc(var(--spacing) * 3.5);
    height: calc(var(--spacing) * 3.5);
  }
  .\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-4 svg:not([class*='size-']) {
    width: calc(var(--spacing) * 4);
    height: calc(var(--spacing) * 4);
  }
  :is(.data-\[variant\=destructive\]\:\*\:\[svg\]\:text-destructive[data-variant="destructive"] > *):is(svg) {
    color: var(--destructive);
  }
  .has-\[select\[aria-hidden\=true\]\:last-child\]\:\[\&\>\[data-slot\=select-trigger\]\:last-of-type\]\:rounded-r-lg:has(:is(select[aria-hidden=true]:last-child)) > [data-slot=select-trigger]:last-of-type {
    border-top-right-radius: var(--radius);
    border-bottom-right-radius: var(--radius);
  }
  .\[\&\>\[data-slot\=select-trigger\]\:not\(\[class\*\=\'w-\'\]\)\]\:w-fit > [data-slot=select-trigger]:not([class*='w-']) {
    width: fit-content;
  }
  .\[\&\>\[data-slot\]\:not\(\:has\(\~\[data-slot\]\)\)\]\:rounded-r-lg\! > [data-slot]:not(:has( ~ [data-slot])) {
    border-top-right-radius: var(--radius) !important;
    border-bottom-right-radius: var(--radius) !important;
  }
  .\[\&\>\[data-slot\]\:not\(\:has\(\~\[data-slot\]\)\)\]\:rounded-b-lg\! > [data-slot]:not(:has( ~ [data-slot])) {
    border-bottom-right-radius: var(--radius) !important;
    border-bottom-left-radius: var(--radius) !important;
  }
  .\[\&\>\[data-slot\]\~\[data-slot\]\]\:rounded-t-none > [data-slot] ~ [data-slot] {
    border-top-left-radius: 0;
    border-top-right-radius: 0;
  }
  .\[\&\>\[data-slot\]\~\[data-slot\]\]\:rounded-l-none > [data-slot] ~ [data-slot] {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
  }
  .\[\&\>\[data-slot\]\~\[data-slot\]\]\:border-t-0 > [data-slot] ~ [data-slot] {
    border-top-style: var(--tw-border-style);
    border-top-width: 0px;
  }
  .\[\&\>\[data-slot\]\~\[data-slot\]\]\:border-l-0 > [data-slot] ~ [data-slot] {
    border-left-style: var(--tw-border-style);
    border-left-width: 0px;
  }
  .\[\&\>input\]\:flex-1 > input {
    flex: 1;
  }
}
:root {
  --background: #fff;
  --foreground: #111;
  --primary: #171717;
  --primary-foreground: #fff;
  --secondary: #eee;
  --secondary-foreground: #171717;
  --muted: #eee;
  --muted-foreground: #666;
  --accent: #eee;
  --accent-foreground: #171717;
  --border: #ddd;
  --input: #ddd;
  --ring: #777;
  --destructive: #dc2626;
  --destructive-foreground: #fff;
  --popover: #fff;
  --popover-foreground: #111;
  --success: #15803d;
  --success-foreground: #fff;
  --radius: .625rem;
  --hairline: 1px;
}
.dark {
  --background: #090909;
  --foreground: #fafafa;
  --primary: #fafafa;
  --primary-foreground: #171717;
  --secondary: #262626;
  --secondary-foreground: #fafafa;
  --muted: #262626;
  --muted-foreground: #a3a3a3;
  --accent: #262626;
  --accent-foreground: #fafafa;
  --border: #333;
  --input: #333;
  --popover: #171717;
  --popover-foreground: #fafafa;
}
:root {
  --radius: 0.5rem;
  --background: oklch(1 0 0);
  --foreground: oklch(0.1296 0.0274 261.69);
  --card: oklch(1 0 0);
  --card-foreground: oklch(0.1296 0.0274 261.69);
  --popover: oklch(1 0 0);
  --popover-foreground: oklch(0.1296 0.0274 261.69);
  --primary: oklch(0.1296 0.0274 261.69);
  --primary-foreground: oklch(0.9276 0.0058 264.53);
  --secondary: oklch(0.967 0.0029 264.54);
  --secondary-foreground: oklch(0.1296 0.0274 261.69);
  --muted: oklch(0.967 0.0029 264.54);
  --muted-foreground: oklch(0.4461 0.0263 256.8);
  --accent: oklch(0.967 0.0029 264.54);
  --accent-foreground: oklch(0.1296 0.0274 261.69);
  --destructive: oklch(0.6368 0.2078 25.33);
  --border: oklch(0.9276 0.0058 264.53);
  --input: oklch(0.922 0 0);
  --ring: oklch(0.7137 0.0192 261.32);
  --chart-1: oklch(63.681% 0.20784 25.315);
  --chart-2: oklch(78.453% 0.13254 181.95);
  --chart-3: oklch(62.31% 0.1881 259.83);
  --chart-4: oklch(87.896% 0.15335 91.611);
  --chart-5: oklch(75.763% 0.15899 55.922);
  --sidebar: oklch(0.985 0 0);
  --sidebar-foreground: oklch(0.1296 0.0274 261.69);
  --sidebar-primary: oklch(0.1296 0.0274 261.69);
  --sidebar-primary-foreground: oklch(0.9276 0.0058 264.53);
  --sidebar-accent: oklch(0.967 0.0029 264.54);
  --sidebar-accent-foreground: oklch(0.1296 0.0274 261.69);
  --sidebar-border: oklch(0.9276 0.0058 264.53);
  --sidebar-ring: oklch(0.7137 0.0192 261.32);
}
.dark {
  --background: oklch(0.1296 0.0274 261.69);
  --foreground: oklch(0.9276 0.0058 264.53);
  --card: oklch(0.1296 0.0274 261.69);
  --card-foreground: oklch(0.9276 0.0058 264.53);
  --popover: oklch(0.1296 0.0274 261.69);
  --popover-foreground: oklch(0.9276 0.0058 264.53);
  --primary: oklch(0.9276 0.0058 264.53);
  --primary-foreground: oklch(0.1296 0.0274 261.69);
  --secondary: oklch(0.3729 0.0306 259.73);
  --secondary-foreground: oklch(0.9276 0.0058 264.53);
  --muted: oklch(0.2781 0.0296 256.85);
  --muted-foreground: oklch(0.8717 0.0093 258.34);
  --accent: oklch(0.2781 0.0296 256.85);
  --accent-foreground: oklch(0.9276 0.0058 264.53);
  --destructive: oklch(0.6368 0.2078 25.33);
  --border: oklch(27.808% 0.02963 256.871 / 70%);
  --input: oklch(1 0 0 / 15%);
  --ring: oklch(0.4461 0.0263 256.8);
  --chart-1: oklch(63.681% 0.20784 25.315);
  --chart-2: oklch(78.453% 0.13254 181.95);
  --chart-3: oklch(62.31% 0.1881 259.83);
  --chart-4: oklch(87.896% 0.15335 91.611);
  --chart-5: oklch(75.763% 0.15899 55.922);
  --sidebar: oklch(0.205 0 0);
  --sidebar-foreground: oklch(0.9276 0.0058 264.53);
  --sidebar-primary: oklch(0.9276 0.0058 264.53);
  --sidebar-primary-foreground: oklch(0.1296 0.0274 261.69);
  --sidebar-accent: oklch(0.2781 0.0296 256.85);
  --sidebar-accent-foreground: oklch(0.9276 0.0058 264.53);
  --sidebar-border: var(--border);
  --sidebar-ring: oklch(0.4461 0.0263 256.8);
}
@property --tw-border-style {
  syntax: "*";
  inherits: false;
  initial-value: solid;
}
@property --tw-font-weight {
  syntax: "*";
  inherits: false;
}
@property --tw-tracking {
  syntax: "*";
  inherits: false;
}
@property --tw-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0 0 #0000;
}
@property --tw-shadow-color {
  syntax: "*";
  inherits: false;
}
@property --tw-shadow-alpha {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 100%;
}
@property --tw-inset-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0 0 #0000;
}
@property --tw-inset-shadow-color {
  syntax: "*";
  inherits: false;
}
@property --tw-inset-shadow-alpha {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 100%;
}
@property --tw-ring-color {
  syntax: "*";
  inherits: false;
}
@property --tw-ring-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0 0 #0000;
}
@property --tw-inset-ring-color {
  syntax: "*";
  inherits: false;
}
@property --tw-inset-ring-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0 0 #0000;
}
@property --tw-ring-inset {
  syntax: "*";
  inherits: false;
}
@property --tw-ring-offset-width {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}
@property --tw-ring-offset-color {
  syntax: "*";
  inherits: false;
  initial-value: #fff;
}
@property --tw-ring-offset-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0 0 #0000;
}
@property --tw-outline-style {
  syntax: "*";
  inherits: false;
  initial-value: solid;
}
@property --tw-duration {
  syntax: "*";
  inherits: false;
}
@property --tw-translate-x {
  syntax: "*";
  inherits: false;
  initial-value: 0;
}
@property --tw-translate-y {
  syntax: "*";
  inherits: false;
  initial-value: 0;
}
@property --tw-translate-z {
  syntax: "*";
  inherits: false;
  initial-value: 0;
}
@layer properties {
  @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
    *, ::before, ::after, ::backdrop {
      --tw-border-style: solid;
      --tw-font-weight: initial;
      --tw-tracking: initial;
      --tw-shadow: 0 0 #0000;
      --tw-shadow-color: initial;
      --tw-shadow-alpha: 100%;
      --tw-inset-shadow: 0 0 #0000;
      --tw-inset-shadow-color: initial;
      --tw-inset-shadow-alpha: 100%;
      --tw-ring-color: initial;
      --tw-ring-shadow: 0 0 #0000;
      --tw-inset-ring-color: initial;
      --tw-inset-ring-shadow: 0 0 #0000;
      --tw-ring-inset: initial;
      --tw-ring-offset-width: 0px;
      --tw-ring-offset-color: #fff;
      --tw-ring-offset-shadow: 0 0 #0000;
      --tw-outline-style: solid;
      --tw-duration: initial;
      --tw-translate-x: 0;
      --tw-translate-y: 0;
      --tw-translate-z: 0;
    }
  }
}