Button

PreviousNext

React aria components Button with shadcn characteristics.

Docs
ouiui

Preview

Loading preview…
registry/default/ui/oui-button.tsx
"use client";

import type { VariantProps } from "class-variance-authority";
import {
  disabledStyles,
  focusVisibleStyles,
} from "@/registry/default/ui/oui-base";
import { cva } from "class-variance-authority";
import * as Rac from "react-aria-components";
import { twMerge } from "tailwind-merge";

/**
 * Derived from shadcn Button.
 * Rac.Button does not support aria-invalid or have data-invalid. We keep the aria-invalid styles for now.
 */
export const buttonVariants = cva(
  [
    focusVisibleStyles,
    disabledStyles,
    "aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
    "inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
  ],
  {
    variants: {
      variant: {
        default:
          "bg-primary text-primary-foreground data-hovered:bg-primary/90",
        destructive:
          "bg-destructive text-white data-focus-visible:ring-destructive/20 data-hovered:bg-destructive/90 dark:bg-destructive/60 dark:data-focus-visible:ring-destructive/40",
        outline:
          "border bg-background shadow-xs data-hovered:bg-accent data-hovered:text-accent-foreground dark:border-input dark:bg-input/30 dark:data-hovered:bg-input/50",
        secondary:
          "bg-secondary text-secondary-foreground data-hovered:bg-secondary/80",
        ghost:
          "data-hovered:bg-accent data-hovered:text-accent-foreground dark:data-hovered:bg-accent/50",
        link: "text-primary underline-offset-4 data-hovered:underline",
      },
      size: {
        default: "h-9 px-4 py-2 has-[>svg]:px-3",
        sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
        lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
        icon: "size-9",
        "icon-sm": "size-8",
        "icon-lg": "size-10",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  },
);

/**
 * Returns a className function to style a react aria component as a button.
 *
 * @example
 * <Rac.Link
 *   href="#"
 *   className={buttonClassName({ variant: "outline" })}
 * >
 *   Button Link
 * </Rac.Link>
 */
export function buttonClassName<T>(
  props?: VariantProps<typeof buttonVariants>,
  className?: string | ((renderProps: T) => string),
) {
  return Rac.composeRenderProps(className, (className) =>
    twMerge(buttonVariants(props), className),
  );
}

export function Button({
  variant,
  size,
  className,
  ...props
}: React.ComponentProps<typeof Rac.Button> &
  VariantProps<typeof buttonVariants>) {
  return (
    <Rac.Button
      data-slot="button"
      className={buttonClassName({ variant, size }, className)}
      {...props}
    />
  );
}

Installation

npx shadcn@latest add @oui/oui-button

Usage

import { OuiButton } from "@/components/ui/oui-button"
<OuiButton />