Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Buttons

Pro

Stitch Button for React

A button with a stitched line just inside its edge and soft inner shading, like stitched leather. For React with Base UI, when a button should feel handmade.

Framework
UI library

The Stitch Button has a dashed line running just inside its edge, with soft shading along the top and bottom that makes it look padded. On hover or keyboard focus the stitching shows a little clearer, and while it's pressed the shading fades, so it looks pressed flat.

Use it where the brand is about making things: a craft shop, a fashion label, a DIY app. It's a strong texture, so keep it to one or two buttons a page. It wraps the Button, so every variant and size works, and the stitch and shading are drawn in the label's color, so they suit each variant. It's CSS only, short opacity transitions with no JavaScript and no animation library; with reduced motion the stitch and the shading change at once instead of easing.

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Usage

import { StitchButton } from "@/components/ui/stitch-button";
<StitchButton>Add to bag</StitchButton>

Demos

Dotted Stitch

stitch="dotted" swaps the dashes for a finer dotted line, closer to hand sewing. Use it on smaller buttons, where dashes get too few to read as stitching.

Preview width

Variants and Sizes

The stitch takes the label's color at a little under half strength, so it shows on outline, secondary and destructive as well as on the primary fill. It sits the same distance from the edge at every size.

Preview width

Accessibility

The stitch and shading are decoration and hidden from assistive tech: the button is announced and activated as any Button. Holding Space shows the flattened, pressed look, as a pointer press does.

Enter
Activate the button.
Space
Activate the button.

FAQ

How do I change the color?

Pick a variant, or pass a background and text color in className, such as bg-secondary text-secondary-foreground. The stitch and shading follow the text color, so they always match.

How is the stitch drawn?

It's a 1px dashed border in the label's color on a layer set 3px inside the button, at 45% opacity (70% on hover). The shading is a second layer with two inset shadows, one along the top and one along the bottom.

What happens when it's pressed?

The shading layer fades to 30%, so the button looks pressed flat, and it returns as soon as it's released. Only its opacity changes, never the shadows themselves, so the press is cheap to draw.

Does it work in dark mode?

Yes. Everything is drawn from the button's own colors, which already change with the mode.

Does the stitch follow the corners?

Yes, it takes the button's radius, so it follows square, rounded and pill-shaped buttons alike.

What happens with reduced motion?

The pressed look and the clearer stitch on hover still show, but at once instead of easing.

API Reference

StitchButton

data-slot="stitch-button-shading"

PropType
children?ReactNode

The label, inside the stitching.

focusableWhenDisabled?boolean

Whether the button should be focusable when disabled.

Default
false
nativeButton?boolean

Whether the component renders a native <button> element when replacing it via the render prop. Set to false if the rendered element is not a button (for example, <div>).

Default
true
stitch?"dashed" | "dotted"

The stitch line: dashed like machine stitching, dotted for a finer, hand-sewn look.

Type
"dashed" | "dotted"
Default
"dashed"
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

All 35 buttons