# Arrow Slide Button

> A call to action whose arrow slides out on hover while a fresh one slides in from the left. Pure CSS, solid or outline.

Category: Buttons · Free · Updated 2026-10-30 · Live demo: https://www.motionkitui.com/components/arrow-button

## When to use it

The main call to action on SaaS landing pages, pricing cards and sign-up flows, and the quieter outline version beside it.

## How it works

Inside a small disc sit two identical arrows: one in view and one waiting just out of sight to the left. On hover, a CSS transform moves the visible arrow out to the right while the waiting one slides into its place, and the disc's overflow hides the hand-over. The label nudges slightly left at the same time. It's all group-hover classes, so it needs no JavaScript and can be a server component.

## Installation

```bash
npx shadcn@latest add https://www.motionkitui.com/r/arrow-button.json
```

Or copy `components/motionkit/arrow-button.tsx` below. Requires react and Tailwind CSS v4. Free for personal projects; commercial use needs MotionKit Plus (https://www.motionkitui.com/pricing).

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `variant` | `"solid" \| "outline"` | `"solid"` | Main call to action, or a quieter one. |
| `children` | `ReactNode` |  | The label. |
| `...props` | `ButtonHTMLAttributes` |  | Anything a <button> accepts, such as onClick or type. |

## Accessibility

- **Keyboard: Pass.** A native <button> with a visible focus ring; Enter and Space work as usual.
- **Screen readers: Pass.** Announced with its label only; the arrows are hidden from assistive tech.
- **Reduced motion: Pass.** With reduced motion on, the arrows and label swap without sliding.
- **Colour contrast: Pass.** White on the solid button measures 4.9:1 (6.6:1 on hover); the outline version's text 17:1.

## Source

```tsx
import type { ComponentProps } from "react";

type ArrowButtonProps = ComponentProps<"button"> & {
  /** "solid" for the main call to action, "outline" for a quieter one beside it. */
  variant?: "solid" | "outline";
};

const Arrow = ({ className = "" }: { className?: string }) => (
  <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth={1.8} strokeLinecap="round" strokeLinejoin="round" className={`absolute size-3.5 transition-transform duration-300 ease-[cubic-bezier(0.65,0,0.35,1)] motion-reduce:transition-none ${className}`}>
    <path d="M3 8h10M9 4l4 4-4 4" />
  </svg>
);

/**
 * A call to action whose arrow slides out to the right on hover while a fresh one slides in from the left,
 * inside a small disc. Pure CSS, so it works without JavaScript and costs nothing.
 */
export function ArrowButton({ variant = "solid", className = "", children, ...props }: ArrowButtonProps) {
  const styles =
    variant === "solid"
      ? "bg-[#0E7C86] text-white hover:bg-[#0B6770] [--disc:rgb(255_255_255/0.18)]"
      : "border border-white/15 text-[#F6F1E8] hover:border-white/30 [--disc:rgb(95_174_180/0.18)]";
  return (
    <button
      {...props}
      className={`group inline-flex items-center gap-3 rounded-full py-2 pl-5 pr-2 text-sm font-medium transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#5FAEB4] disabled:pointer-events-none disabled:opacity-50 ${styles} ${className}`}
    >
      <span className="transition-transform duration-300 ease-[cubic-bezier(0.65,0,0.35,1)] group-hover:-translate-x-0.5 motion-reduce:transition-none">{children}</span>
      <span aria-hidden className="relative flex size-7 items-center justify-center overflow-hidden rounded-full bg-[var(--disc)]">
        <Arrow className="group-hover:translate-x-6" />
        <Arrow className="-translate-x-6 group-hover:translate-x-0" />
      </span>
    </button>
  );
}
```
