shadimage
rawkitui
uibuttonsource: ok · hit

Button

Tactile Neo-Brutalist button with hard black shadow and press physics.

npx shadcn add "https://rawkitui.zerodegree.tech/r/button.json"
free

Visual style

brutalist

Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.

Dependencies

Bundle weight · light
class-variance-authorityclsxtailwind-merge

Files1 · source cached 10/9/2026, 5:56:13 AM

components/ui/button.tsx
'use client'

import * as React from 'react'
import { cva, type VariantProps } from 'class-variance-authority'
import { Slot } from '@radix-ui/react-slot'
import { Loader2 } from 'lucide-react'
import { cn } from '@/lib/utils'

const shadowColorHexMap = {
  ink: 'var(--rk-shadow-color, #18181b)',
  yellow: '#FACC15',
  orange: '#FB923C',
  violet: '#A78BFA',
  mint: '#4ADE80',
  pink: '#F472B6',
  sky: '#38BDF8',
}

const buttonVariants = cva(
  'inline-flex items-center justify-center whitespace-nowrap font-sans font-extrabold text-sm tracking-wide rk-border focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-black focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 transition-all select-none cursor-pointer',
  {
    variants: {
      variant: {
        primary: 'bg-rk-primary text-rk-ink hover:bg-rk-primary-hover',
        yellow: 'bg-rk-primary text-rk-ink hover:bg-rk-primary-hover',
        secondary: 'bg-rk-secondary text-rk-ink hover:bg-rk-secondary-hover',
        accent: 'bg-rk-accent text-rk-ink hover:bg-rk-accent-hover',
        mint: 'bg-rk-mint text-rk-ink hover:bg-rk-mint-hover',
        peach: 'bg-rk-peach text-rk-ink hover:bg-rk-peach-hover',
        sky: 'bg-rk-sky text-rk-ink hover:bg-rk-sky-hover',
        pink: 'bg-rk-pink text-rk-ink hover:bg-rk-pink-hover',
        black: 'bg-rk-ink text-rk-canvas hover:bg-rk-ink-hover',
        outline: 'bg-rk-surface text-rk-ink hover:bg-rk-canvas',
      },
      shape: {
        default: 'rounded-2xl',
        pill: 'rounded-full',
        fab: 'rounded-full !w-12 !h-12 !p-0 shrink-0 aspect-square flex items-center justify-center text-lg',
        square: 'rounded-xl',
      },
      size: {
        sm: 'h-9 px-4 text-xs',
        md: 'h-11 px-6 text-sm',
        lg: 'h-13 px-8 text-base font-extrabold',
        icon: 'h-11 w-11 !p-0 shrink-0 aspect-square flex items-center justify-center',
      },
    },
    defaultVariants: {
      variant: 'primary',
      shape: 'pill',
      size: 'md',
    },
  }
)

export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  isLoading?: boolean
  asChild?: boolean
  shadowColor?: keyof typeof shadowColorHexMap
  shadowStyle?: 'hard' | 'soft' | 'none'
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  (
    {
      className,
      variant,
      shape,
      size,
      isLoading = false,
      asChild = false,
      shadowColor = 'ink',
      shadowStyle = 'hard',
      children,
      disabled,
      ...props
    },
    ref
  ) => {
    const Comp = asChild ? Slot : 'button'
    const isSquareOrFab = shape === 'fab' || size === 'icon'

    const style = {
      '--rk-btn-shadow-color': shadowColorHexMap[shadowColor] ?? '#18181b',
    } as React.CSSProperties

    return (
      <Comp
        ref={ref}
        disabled={disabled || isLoading}
        data-shadow-style={shadowStyle}
        style={style}
        className={cn(
          'rk-btn',
          buttonVariants({ variant, shape, size, className }),
          isSquareOrFab && '!p-0 !px-0 !py-0 shrink-0 aspect-square'
        )}
        {...props}
      >
        {isLoading ? (
          <span className="flex items-center justify-center gap-2">
            <Loader2 className="w-4 h-4 animate-spin stroke-[3]" />
            {!isSquareOrFab && children}
          </span>
        ) : (
          children
        )}
      </Comp>
    )
  }
)
Button.displayName = 'Button'

export { Button, buttonVariants }

More from rawkitui

Browse all components in this library →