Button

Displays a button or a component that looks like a button.

Preview

Installation

Copy and paste this into your project

// ~/components/ui/button.tsx

import { VariantProps, cva } from "class-variance-authority";
import {
  defineComponent,
  PropType,
  ButtonHTMLAttributes,
  ExtractPropTypes,
} from "vue";
import { ExtendProps, cn, renderAsChild } from "~/lib/utils";

export const buttonVariants = cva(
  "inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground hover:bg-primary/90",
        destructive:
          "bg-destructive text-destructive-foreground hover:bg-destructive/90",
        outline:
          "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
        secondary:
          "bg-secondary text-secondary-foreground hover:bg-secondary/80",
        ghost: "hover:bg-accent hover:text-accent-foreground",
        link: "text-primary underline-offset-4 hover:underline",
      },
      size: {
        default: "h-10 px-4 py-2",
        sm: "h-9 rounded-md px-3",
        lg: "h-11 rounded-md px-8",
        icon: "h-10 w-10",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
);

const props = {
  variant: {
    type: String as PropType<VariantProps<typeof buttonVariants>["variant"]>,
  },
  size: {
    type: String as PropType<VariantProps<typeof buttonVariants>["size"]>,
  },
  asChild: {
    type: Boolean as PropType<boolean | undefined>,
  },
};

export type ButtonProps = ExtendProps<
  ButtonHTMLAttributes & ExtractPropTypes<typeof props>
>;

const Button = defineComponent({
  props: props as unknown as ButtonProps,
  setup({ variant, size, asChild }, { slots, attrs }) {
    return () =>
      asChild ? (
        renderAsChild(slots, {
          ...attrs,
          class: cn(buttonVariants({ variant, size }), attrs.class ?? ""),
        })
      ) : (
        <button
          {...attrs}
          class={cn(buttonVariants({ variant, size }), attrs.class ?? "")}
        >
          {slots.default?.()}
        </button>
      );
  },
});

export { Button };

Usage

import { Button } from "~/components/ui/button";
<Button variant="secondary">Button</Button>

You can use the buttonVariants helper to create a link that looks like a button.

import { buttonVariants } from "~/components/ui/button"
<Link class="buttonVariants({ variant: 'outline' })">Click here</Link>

Alternatively, you can set the asChild parameter and nest the link component.

<Button as-child>
  <Link href="/login">Login</Link>
</Button>

Examples

Default

Secondary

Destructive

Outline

Ghost

Icon

With Icon

Loading