AppCrumble mascotAppCrumble
← Back to Crumbs
InterfacesReactTypeScriptTailwind

Rattan Button

A button woven to look like rattan cane, with the press handling to match.

Live preview

Code

rattan-button.tsx
"use client";

import * as React from "react";

export interface RattanButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  children: React.ReactNode;
}

export const RattanButton = React.forwardRef<HTMLButtonElement, RattanButtonProps>(
  ({ children, className = "", ...props }, ref) => {
    return (
      <button
        ref={ref}
        {...props}
        className={[
          "relative inline-flex items-center justify-center gap-2 overflow-hidden",
          "rounded-full border-2 border-[#5c3a21] px-7 py-3",
          "font-bold text-[15px] text-[#381d09] dark:text-[#fbe8cf]",
          "bg-[#ecb47c] bg-[length:16px_16px]",
          "bg-[image:repeating-linear-gradient(45deg,rgba(92,58,33,0.28)_0_2px,transparent_2px_8px),repeating-linear-gradient(-45deg,rgba(92,58,33,0.28)_0_2px,transparent_2px_8px)]",
          "shadow-[inset_0_1px_0_rgba(255,236,205,0.6),inset_0_-3px_4px_rgba(92,58,33,0.35),0_4px_0_#5c3a21,0_8px_14px_rgba(92,58,33,0.35)]",
          "transition-transform duration-150 ease-out",
          "focus-visible:outline-none",
          "hover:brightness-105",
          "active:translate-y-[3px] active:shadow-[inset_0_1px_0_rgba(255,236,205,0.6),inset_0_-3px_4px_rgba(92,58,33,0.35),0_1px_0_#5c3a21,0_2px_6px_rgba(92,58,33,0.3)]",
          "focus-visible:ring-2 focus-visible:ring-[#8a5a2b] focus-visible:ring-offset-2 focus-visible:ring-offset-[#fff8ef] dark:focus-visible:ring-offset-[#1c1410]",
          "disabled:cursor-not-allowed disabled:opacity-50 disabled:saturate-50",
          "dark:border-[#3a2413] dark:bg-[#8a5a2b] dark:bg-[image:repeating-linear-gradient(45deg,rgba(58,36,19,0.5)_0_2px,transparent_2px_8px),repeating-linear-gradient(-45deg,rgba(58,36,19,0.5)_0_2px,transparent_2px_8px)]",
          className,
        ]
          .filter(Boolean)
          .join(" ")}
      >
        <span className="pointer-events-none absolute inset-x-0 top-0 h-1/2 rounded-t-full bg-gradient-to-b from-white/25 to-transparent" />
        <span className="relative">{children}</span>
      </button>
    );
  },
);

RattanButton.displayName = "RattanButton";

Usage

usage.tsx
import { RattanButton } from "@/components/rattan-button";

export function AddToBasket() {
  return <RattanButton onClick={() => {}}>Add to basket</RattanButton>;
}
Dependencies
None - just React and Tailwind.
Responsive
Sizes to its content with padding, like a native button - drop it anywhere from a compact toolbar to a full-width mobile CTA.
Dark mode
Switches to a darker walnut weave with lighter grout lines via Tailwind's dark: variant - no extra props needed.