# Slipway — Animated Section Block for React

> App-download section block: headline, a rating row whose stars fill in sequence while the review count rolls on Tally drums, Detent platform tabs that swap store badges and requirements, and a QR card with a seeded module grid and scan-line sweep. A CSS phone frame runs a swipeable, auto-advancing screen carousel with dot pagination and a floating callout chip tethered by a drawn leader line.

- Page: https://crucibleui.com/components/sections/slipway
- Category: [sections](https://crucibleui.com/components/sections)
- Tier: pro (one-time purchase, see https://crucibleui.com/pricing)
- Engine: motion
- npm dependencies: motion

## Install

```bash
npx shadcn@latest add @crucible/slipway
```

Pro install requires adding the token-bearing `@crucible` registry to components.json (issued at https://crucibleui.com/account after purchase).

Import from `@/components/crucible/slipway`.

## Props (`SlipwayProps`)

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `title?` | `React.ReactNode` | `"Your whole day, in your pocket."` | Headline. |
| `subtitle?` | `React.ReactNode` | — | Supporting line under the headline. |
| `eyebrow?` | `React.ReactNode \| false` | `"Now on {platform labels}"` | Small pill above the headline. Pass `false` to hide. |
| `platforms?` | `SlipwayPlatform[]` | — | Platform tabs, each with its own badges and requirements line. Omit for iOS / Android / Desktop samples. |
| `platform?` | `string` | — | Controlled selected platform id. |
| `defaultPlatform?` | `string` | `first platform` | Initial platform id when uncontrolled. |
| `onPlatformChange?` | `(id: string) => void` | — | Fires when the platform tab changes. |
| `onBadgeClick?` | `(platformId: string, badge: SlipwayBadge, event: React.MouseEvent<HTMLAnchorElement>) => void` | — | Fires when a store badge is activated (before navigation). |
| `screens?` | `SlipwayScreen[]` | — | Phone screens (auto-advancing carousel). Omit for 4 seeded sample screens. |
| `onScreenChange?` | `(index: number) => void` | — | Fires with the new index whenever the visible screen changes. |
| `rating?` | `SlipwayRating \| false` | `{ value: 4.8, count: 12480 }` | Rating row config, or `false` to hide it. |
| `url?` | `string` | — | Download URL — seeds the decorative QR pattern and is what "Copy link" copies. |
| `qr?` | `React.ReactNode \| false` | — | Your own QR node (e.g. a real generated QR code) to replace the decorative seeded pattern, or `false` to hide the QR card entirely. |
| `interval?` | `number` | `4200` | Milliseconds each screen stays up during the idle loop. |
| `accentColor?` | `string` | `"#2dd4bf"` | Accent hex for stars, focus rings, chip, scan line and screen highlights. |
| `paused?` | `boolean` | `false` | Freeze the ambient idle loop (auto-advance + QR scan sweep). |
| `ref?` | `React.Ref<SlipwayHandle>` | — | Imperative handle (`replay()`, `goTo(i)`). React 19 ref-as-prop. |
| `className?` | `string` | — | Extra classes for the `<section>` root. |

Also accepts: `Omit<React.ComponentPropsWithoutRef<"section">, "title" | "children">`.

## Usage example

```tsx
"use client";

import * as React from "react";

import { Slipway, type SlipwayHandle } from "@/components/crucible/slipway";

export default function SlipwayDemo() {
  const ref = React.useRef<SlipwayHandle>(null);

  return (
    <div className="relative isolate flex h-full min-h-[640px] w-full flex-col overflow-hidden rounded-xl bg-neutral-950">
      <p className="relative z-10 pt-5 text-center text-xs text-white/45">Swipe the phone — switch platforms.</p>
      <Slipway ref={ref} className="relative z-10 bg-transparent" />
      <div className="relative z-10 flex justify-center pb-4">
        <button
          type="button"
          onClick={() => ref.current?.replay()}
          className="rounded text-xs text-white/40 underline underline-offset-4 transition-colors hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/60"
        >
          Replay intro
        </button>
      </div>
    </div>
  );
}
```

## Accessibility & performance

Honors `prefers-reduced-motion` with a designed static frame or fade (never a blank box) and passes `className` through.
