# Swipe Row (Lumen Halo): prompt.md (v1.0.0)

- id: `lumen-swipe-row` · version 1.0.0 · component · pro (Pro)
- category: Actions
- build: Base UI (one set of files; its dependencies follow the build)
- install (this build): `npx shadcn@latest add @beautiful-ui-pro/lumen-swipe-row`
- npm dependencies: none
- registry dependencies: utils, https://beautiful-ui.dev/r/lumen-foundation.json
- docs: https://beautiful-ui.dev/components/lumen-swipe-row
- The install command carries everything this item needs (files, CSS, tokens, npm and registry dependencies). Prefer it to copying source by hand.

A row that slides aside to show what you can do with it: drag past halfway and it stays open; keep going and a meter of cells fills, so a release runs the outer action and a row that removes itself folds away. The same actions are one press away on the row's Actions button.

## Build it
- Stack: React 19 (`ref` is a plain prop), TypeScript, Tailwind CSS v4 utilities, a shadcn-initialised project with the `@/*` alias.
- Packages: none beyond React.
- Files: `components/ui/lumen/controls/swipe-row.tsx`.
- Registry dependencies, installed with it automatically: shadcn `utils` (cn), `lumen-foundation`.
- Builds: one set of files for both, but its dependencies come in Base UI and Radix builds. Install the one that matches the project (see Install): a free item's bare URL installs the Base UI build of it and its dependencies.
- Exports to keep: `SwipeRow`, and every exported type.
- CSS: the install merges this item's rules (the registry `css` field) into your global stylesheet, in `@layer components`, and adds the lumen foundation (tokens, keyframes, motion levels) once. Nothing to import by hand.
- Re-running `add` (or `--overwrite`) re-applies those rules: put overrides in your own CSS, never in the installed rules.
- Tokens: retheme with the `--lumen-*` custom properties (`--lumen-accent`, `--lumen-accent-foreground`, `--lumen-accent-text`, `--lumen-bad`, `--lumen-bad-text`, `--lumen-chart-accent`, `--lumen-chart-accent-foreground`, `--lumen-chart-accent-text`, `--lumen-focus`, `--lumen-good`, `--lumen-good-text`, `--lumen-hairline`, `--lumen-ink`, `--lumen-muted-ink`, `--lumen-series-1`, `--lumen-series-2`, `--lumen-series-3`, `--lumen-series-4`, `--lumen-series-5`, `--lumen-series-6`, `--lumen-warn`, `--lumen-warn-text`); this item's CSS also reads `--lumen-font-sans`, `--lumen-radius-k`, `--lumen-swipe-ground`, `--lumen-swipe-x`. Never add Tailwind colour classes inside the component.

```tsx
import { SwipeRow } from "@/components/ui/lumen/controls/swipe-row";
```

## Parts

| Part | data-slot | What it is for |
|---|---|---|
| `SwipeRow` | — | The row, its rails and the Actions button. |

## Sound
- Keep every `data-slot` and `data-sound` attribute: the sound layer reads them.
- Installing this item adds no audio. Nothing plays until the app mounts `GlassSoundProvider` once (install: `npx shadcn@latest add https://beautiful-ui.dev/r/glass-sound.json`, import from `@/components/beautiful-ui/glass-sound`); `GlassSoundToggle` is its mute control. Without a provider the audio engine never loads.

## Match the original
- Read `components/ui/lumen/controls/swipe-row.tsx` as the reference implementation before changing or recreating anything, and match it: sizes, colours per theme, motion timings, copy and behaviour.
- If you deviate (a prop you can't honour, a style you changed, a dependency you swapped), say so in your reply, part by part.
- Keep the accessibility contract, the keyboard map and the motion levels listed below.

## Use it when
- swipe actions, swipe to delete, swipe to archive, list row actions, inbox row, mobile list, Lumen
- Lists people triage quickly: inboxes, notifications, tasks, alerts
- Mobile-first screens where row buttons would crowd the content

### Not when
- Tables with many columns: use a row menu or Bulk Action Bar
- Actions that need a confirm step: use Safe Action inside the row

## Mistakes
- Put the most common, reversible action outermost on the end side
- Pair removing actions with an undo toast

## Usage

```tsx
"use client";

import * as React from "react";
import { Archive, Clock, MailOpen } from "lucide-react";
import { SwipeRow } from "@/components/ui/lumen/controls/swipe-row";

type Message = { id: string; from: string; subject: string; unread: boolean };

const initial: Message[] = [
  { id: "m1", from: "Priya at Acme", subject: "Renewal terms for next year", unread: true },
  { id: "m2", from: "Kitelabs billing", subject: "Your invoice for September", unread: false },
];

export function Inbox() {
  const [messages, setMessages] = React.useState(initial);
  const markRead = (id: string) => setMessages((ms) => ms.map((m) => (m.id === id ? { ...m, unread: false } : m)));
  const archive = (id: string) => setMessages((ms) => ms.filter((m) => m.id !== id));

  return (
    <div>
      {messages.map((m) => (
        <SwipeRow
          key={m.id}
          start={[{ id: "read", label: "Mark read", icon: <MailOpen size={16} />, tone: "accent", onAction: () => markRead(m.id) }]}
          end={[
            { id: "snooze", label: "Snooze", icon: <Clock size={16} />, tone: "warn", onAction: () => console.log("snooze", m.id) },
            { id: "archive", label: "Archive", icon: <Archive size={16} />, tone: "good", removes: true, onAction: () => archive(m.id) },
          ]}
        >
          <div className="grid p-3">
            <strong style={{ fontWeight: m.unread ? 600 : 400 }}>{m.from}</strong>
            <span>{m.subject}</span>
          </div>
        </SwipeRow>
      ))}
    </div>
  );
}
```

## Props

| Prop | Type | Default | What it does |
|---|---|---|---|
| `end` | `SwipeAction[]` |  | Revealed by swiping toward the start (right to left in LTR). The last one is the outer action a full swipe runs. |
| `start` | `SwipeAction[]` |  | Revealed by swiping toward the end (left to right in LTR). |
| `SwipeAction` | `{ id, label, icon?, tone?, onAction, removes? }` |  | tone: neutral, accent, good, warn or danger. removes: the row folds away, then onAction runs. |
| `fullSwipe` | `boolean` | `true` | A long swipe runs the outer action. |
| `commitAt` | `number` | `0.6` | The share of the row's width a full swipe must pass. |
| `actionsLabel` | `string` | `"Actions"` | The Actions button's accessible name (it opens the same actions without a gesture). |
| `disabled` | `boolean` | `false` | No dragging; the Actions button still works. |

Full docs: https://beautiful-ui.dev/components/lumen-swipe-row

## Customising
- Colours: set the `--lumen-*` tokens on `:root`, or on a container with the `lumen-scope` class to retheme one area. Add the `lumen-inherit` class to follow your shadcn palette instead (`--chart-N`, `--destructive`).
- Dark mode follows the `.dark` class on an ancestor (the shadcn and next-themes convention).
- Update later by re-running the install with `--overwrite` (review the diff if you edited it). Changelog: https://beautiful-ui.dev/r/changelog.json

## Keyboard

| Keys | Action |
|---|---|
| Enter or Space (Actions) | Open the actions and focus the first |
| Escape | Close and return to the Actions button |

## Motion inventory

| Interaction | What moves |
|---|---|
| Drag | The row follows the finger 1:1, heavier past the commit point |
| Long swipe | The outer tile stretches and six cells fill; they light when a release would run it |
| Remove | The row folds its height away |

## Accessibility contract (preserve when editing)
- Every action is a real button, reachable without a gesture through the row's Actions button
- Closed rails are inert, so Tab never lands on hidden buttons
- Vertical scrolling stays native (touch-action: pan-y); a drag only starts once it's clearly sideways

## Install

```bash
npx shadcn@latest add @beautiful-ui-pro/lumen-swipe-row
```

Pro item: needs the `@beautiful-ui-pro` registry in `components.json` and `BEAUTIFUL_UI_TOKEN` in `.env.local` (https://beautiful-ui.dev/account). Setup: https://beautiful-ui.dev/docs/pro. Your components.json `style` picks the build: radix-*, new-york and default get Radix, base-* gets Base UI.

## Credits
- Built on shadcn/ui (https://ui.shadcn.com)

The lumen foundation (the tokens listed above, keyframes and motion levels) installs once with the first component; its CSS is public at https://beautiful-ui.dev/r/lumen-foundation.json.
