shadimage
motokoui
uibuttonsource: ok · hit

Button

A polished button with primary, secondary, and semantic variants.

npx shadcn add "https://www.motokoui.com/r/button.json"
free

No selection tags yet — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light
ui-primitives@radix-ui/react-slot

Registry dependencies

utils

Files1 · source cached 10/15/2026, 4:41:10 AM

registry/motokoui/button.tsx
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"

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

const buttonVariants = cva(
  [
    "relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-2",
    "overflow-hidden border-0 font-medium outline-none select-none",
    "transition-[transform,box-shadow,background-color,color,opacity] duration-200 ease-[cubic-bezier(0.2,0,0,1)]",
    "after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit] after:border-[1.5px] after:transition-[opacity,border-color,box-shadow] after:duration-200 after:ease-[cubic-bezier(0.2,0,0,1)]",
    "active:scale-[0.99]",
    "disabled:pointer-events-none disabled:opacity-50",
    "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
    "focus-visible:ring-2 focus-visible:ring-ring/50",
  ].join(" "),
  {
    variants: {
      variant: {
        primary: [
          "bg-linear-to-b from-[#2C2C2C] to-[#282828] fill-white text-white",
          "shadow-[inset_2px_0_8px_2px_rgba(248,248,248,0.2)]",
          "after:border-white/20 after:mask-[linear-gradient(to_top,transparent_0,black_100%)]",
          "hover:shadow-[inset_2px_0_8px_2px_rgba(248,248,248,0)] hover:after:opacity-40",
          "active:shadow-[inset_0_2px_6px_2px_rgba(0,0,0,0.35)] active:after:opacity-20",
          "dark:from-[#f7f7f7] dark:to-white dark:text-black dark:fill-black",
          "dark:shadow-[inset_2px_0_8px_2px_rgba(24,24,24,0.12)]",
          "dark:after:border-white/50 dark:after:shadow-[inset_2px_0_8px_2px_rgba(24,24,24,0.25)]",
          "dark:hover:after:opacity-0 dark:hover:shadow-[inset_2px_0_8px_2px_rgba(24,24,24,0)]",
          "dark:active:shadow-[inset_0_2px_6px_2px_rgba(0,0,0,0.12)]",
        ].join(" "),
        secondary: [
          "bg-linear-to-b from-[#F3F3F3] to-[#E8E8E7] fill-current text-foreground",
          "shadow-[inset_2px_0_8px_2px_rgba(255,255,255,0.75)]",
          "after:border-black/10 after:mask-[linear-gradient(to_top,transparent_0,black_100%)]",
          "hover:shadow-[inset_2px_0_8px_2px_rgba(255,255,255,0)] hover:after:opacity-40",
          "active:shadow-[inset_0_2px_6px_2px_rgba(0,0,0,0.1)] active:after:opacity-20",
          "dark:from-[#2A2A2A] dark:to-[#222222] dark:text-white",
          "dark:shadow-[inset_2px_0_8px_2px_rgba(255,255,255,0.08)]",
          "dark:after:border-white/15",
          "dark:hover:after:opacity-0 dark:hover:shadow-[inset_2px_0_8px_2px_rgba(255,255,255,0)]",
          "dark:active:shadow-[inset_0_2px_6px_2px_rgba(0,0,0,0.35)]",
        ].join(" "),
        outline: [
          "bg-transparent fill-current text-foreground",
          "shadow-[inset_2px_0_8px_2px_rgba(255,255,255,0)]",
          "after:border-black/15 after:opacity-100 after:mask-[linear-gradient(to_top,transparent_0,black_100%)]",
          "hover:bg-[#F4F4F4] hover:shadow-[inset_2px_0_8px_2px_rgba(255,255,255,0.7)] hover:after:opacity-45",
          "active:bg-[#ECECEC] active:shadow-[inset_0_2px_6px_2px_rgba(0,0,0,0.08)] active:after:opacity-25",
          "dark:text-white dark:after:border-white/20",
          "dark:hover:bg-[#2A2A2A] dark:hover:shadow-[inset_2px_0_8px_2px_rgba(255,255,255,0.06)] dark:hover:after:opacity-40",
          "dark:active:bg-[#222222] dark:active:shadow-[inset_0_2px_6px_2px_rgba(0,0,0,0.35)]",
        ].join(" "),
        ghost: [
          "bg-transparent fill-current text-foreground",
          "shadow-[inset_2px_0_8px_2px_rgba(255,255,255,0)]",
          "after:border-black/10 after:opacity-0 after:mask-[linear-gradient(to_top,transparent_0,black_100%)]",
          "hover:bg-[#F3F3F3] hover:shadow-[inset_2px_0_8px_2px_rgba(255,255,255,0.7)] hover:after:opacity-100",
          "active:bg-[#EBEBEB] active:shadow-[inset_0_2px_6px_2px_rgba(0,0,0,0.08)] active:after:opacity-40",
          "dark:text-white dark:after:border-white/12",
          "dark:hover:bg-[#2A2A2A] dark:hover:shadow-[inset_2px_0_8px_2px_rgba(255,255,255,0.06)] dark:hover:after:opacity-100",
          "dark:active:bg-[#222222] dark:active:shadow-[inset_0_2px_6px_2px_rgba(0,0,0,0.35)]",
        ].join(" "),
        destructive: [
          "bg-linear-to-b from-[#E5484D] to-[#D1393E] fill-white text-white",
          "shadow-[inset_2px_0_8px_2px_rgba(255,255,255,0.22)]",
          "after:border-white/25 after:mask-[linear-gradient(to_top,transparent_0,black_100%)]",
          "hover:shadow-[inset_2px_0_8px_2px_rgba(255,255,255,0)] hover:after:opacity-40",
          "active:shadow-[inset_0_2px_6px_2px_rgba(0,0,0,0.28)] active:after:opacity-20",
          "dark:from-[#FF6369] dark:to-[#E5484D]",
          "dark:shadow-[inset_2px_0_8px_2px_rgba(255,255,255,0.18)]",
          "dark:hover:after:opacity-30",
          "dark:active:shadow-[inset_0_2px_6px_2px_rgba(0,0,0,0.35)]",
        ].join(" "),
        link: [
          "h-auto rounded-none bg-transparent p-0 text-foreground underline-offset-4",
          "shadow-none after:hidden",
          "hover:underline hover:opacity-80",
          "active:opacity-60 active:scale-100",
        ].join(" "),
        default: [
          "bg-linear-to-b from-[#2C2C2C] to-[#282828] fill-white text-white",
          "shadow-[inset_2px_0_8px_2px_rgba(248,248,248,0.2)]",
          "after:border-white/20 after:mask-[linear-gradient(to_top,transparent_0,black_100%)]",
          "hover:shadow-[inset_2px_0_8px_2px_rgba(248,248,248,0)] hover:after:opacity-40",
          "active:shadow-[inset_0_2px_6px_2px_rgba(0,0,0,0.35)] active:after:opacity-20",
          "dark:from-[#f7f7f7] dark:to-white dark:text-black dark:fill-black",
          "dark:shadow-[inset_2px_0_8px_2px_rgba(24,24,24,0.12)]",
          "dark:after:border-white/50 dark:after:shadow-[inset_2px_0_8px_2px_rgba(24,24,24,0.25)]",
          "dark:hover:after:opacity-0 dark:hover:shadow-[inset_2px_0_8px_2px_rgba(24,24,24,0)]",
          "dark:active:shadow-[inset_0_2px_6px_2px_rgba(0,0,0,0.12)]",
        ].join(" "),
      },
      size: {
        default: "h-12 rounded-3xl px-7 text-sm",
        sm: "h-10 rounded-3xl px-5 text-sm",
        lg: "h-14 rounded-3xl px-8 text-base",
        icon: "size-10 justify-center rounded-full p-0",
      },
    },
    defaultVariants: {
      variant: "primary",
      size: "default",
    },
  }
)

export type ButtonVariantProps = VariantProps<typeof buttonVariants>

export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>, ButtonVariantProps {
  asChild?: boolean
  isDisabled?: boolean
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  (
    {
      className,
      variant = "primary",
      size = "default",
      asChild = false,
      isDisabled,
      onClick,
      type,
      ...props
    },
    ref
  ) => {
    const Comp = asChild ? Slot : "button"

    return (
      <Comp
        ref={ref}
        data-slot="button"
        type={asChild ? undefined : type || "button"}
        disabled={isDisabled || props.disabled}
        onClick={onClick}
        className={cn(buttonVariants({ variant, size }), className)}
        {...props}
      />
    )
  }
)
Button.displayName = "Button"

export { Button, buttonVariants }

More from motokoui

Browse all components in this library →