# Consequence preview

A React component in [Beamline](https://beamline.io/)'s [Actions](https://beamline.io/components/actions) category. Live demo: https://beamline.io/components/consequence-preview

A risky action that shows what it will do before it runs: one headline sentence, what blocks it and what it also reaches as bands, then the rest as cards with a number each (changes, tells with the people's initials, keeps, can't be undone, not known); with a time dimension the affected items stand as dated tiles and a cut you drag lights them, rippling out from this one, while the counts and the button (named for the result) follow.

## Use it for

- Before an action that reaches beyond the row in front of you: reversing a payment, publishing a roster, changing a recurring booking, deleting a segment, recategorising history.
- The host computes the effects (`effects`, `summary`, `confirmLabel`); the part presents them and never invents one.

## Not for

- A plain destructive action with nothing beyond its own row → [confirm-morph](https://beamline.io/components/confirm-morph).
- A blocking question that needs the whole screen → [alert-dialog](https://beamline.io/components/alert-dialog).

## Anatomy

- trigger: the action's own button when folded
- glance: beside the trigger when folded, the kinds it reaches with their counts; the trigger is described by it
- sheet (unfolded in place, or open from the start): the sentence as the headline, focused when a person opens it and read once; live afterwards only when there is no strip
- bands across the sheet: blocked by (an ordered chain, each step with its action), also reaches (the warning tone)
- time strip (optional): a dated tile per item (past dimmer, locked hatched with a lock), the selected tile ringed, now marked, the cut in the gap after the last tile in scope, snap words (This one · From here on · All, history too); on a narrow room the tiles keep their light and the dates move to the ends
- cards: per kind the word and glyph, the count large, the label, up to three named examples with "and N more"; the people a kind tells as initials beside their names
- actions: Cancel and the result-named confirm; the reason when it waits
- receipt: the result, View before, Undo with its time left

## States

- rest (button and glance)
- open
- loading (rows keep their shape, confirm waits)
- error (blocked, Try again)
- blocked (confirm disabled with the reason)
- working
- done (receipt with Undo)
- failed (said, Try again)
- undo refused (said on the receipt; Undo stays)

## Keyboard

- Enter or Space on the trigger opens; focus moves to the sentence
- Tab through the rows' actions, the strip and the buttons
- On the strip: ←/→ between This one, From here on and All; Shift+←/→ one item at a time; Home/End to All and From here on
- Escape folds the sheet back with nothing changed; focus returns to the trigger

## Motion

The button unfolds in place into the sheet (the shared height morph) and folds back into the receipt. Moving the cut lights the tiles in scope with a ripple out from this one (a step per tile away from it); while the strip is held the lighting follows the hand with no ripple. A card whose count changed rises into its new number once. Reduced motion places at once.

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `label` (required) | `ReactNode` | — | The action at rest: "Publish roster", "Reverse payment". |
| `icon` | `ReactNode` | — |  |
| `tone` | `"danger" \| "neutral"` | — | Danger for a destructive action, neutral otherwise. Default "danger". |
| `effects` | `ConsequenceEffect[]` | — | The effects, by kind, for the current scope. Recompute them in `onScopeChange`. |
| `status` | `ConsequenceStatus` | — | Whether the host is still computing the effects ("loading"), has them ("ready") or could not ("error"). A loading or failed check never offers the confirm button. Default "ready". |
| `summary` | `ReactNode` | — | One plain sentence written from the effects: "Changes 24 shifts for 9 people and notifies them tonight." |
| `confirmLabel` (required) | `string` | — | The confirm button's words: the result, never "OK": "Publish 24 shifts, notify 9 people". |
| `onConfirm` | `(() => void \| Promise<unknown>)` | — | Runs the action; return a promise and the button shows working until it settles. |
| `onRetry` | `(() => void)` | — | Called by "Try again" after a failed check. |
| `strip` | `ConsequenceStrip` | — | A time dimension: the items the action could reach, as a strip with a cut. |
| `scope` | `ConsequenceScope` | — | Controlled scope; omit to use defaultScope. |
| `defaultScope` | `ConsequenceScope` | — | Initial scope when uncontrolled. Default "one" (the selected item alone). |
| `onScopeChange` | `((scope: ConsequenceScope) => void)` | — | Reports the scope as the cut moves, for an instant local answer. |
| `receipt` | `string` | — | The receipt after the action: "Published 24 shifts". Default the confirm label. |
| `undoFor` | `number` | — | Seconds the receipt offers Undo; 0 offers none. Default 600. |
| `onUndo` | `(() => void \| Promise<unknown>)` | — |  |
| `onViewBefore` | `(() => void)` | — | Opens the record as it was before the action ("View before" on the receipt). |
| `open` | `boolean` | — | Whether the sheet is open; omit to use defaultOpen. |
| `defaultOpen` | `boolean` | — | Initial open state when uncontrolled; defaults to false. |
| `onOpenChange` | `((open: boolean) => void)` | — | Reports an open-state change; no callback by default. |
| `disabled` | `boolean` | — |  |
| `className` | `string` | — | Classes on the documented visual root; no override by default. |
| `classNames` | `ConsequencePreviewClassNames` | — | Classes for the named visual parts; no overrides by default. |
| `style` | `CSSProperties` | — | Inline styles on the documented visual root; no overrides by default. |
| `ref` | `Ref<HTMLDivElement>` | — | Receives the documented native target or imperative handle; omitted by default. |
| `onScopeCommit` | `((scope: ConsequenceScope) => void)` | — | The scope settled: a drag released, a key, a snap, a tap. Recompute effects on the server here. |

## Built from

- [Avatar](https://beamline.io/components/avatar.md)
- [Button](https://beamline.io/components/button.md)
- [Spinner](https://beamline.io/components/spinner.md)

## Used in

- [Care plan](https://beamline.io/components/care-plan.md)
- [Gift batches](https://beamline.io/components/gift-batches.md)
- [Workout logger](https://beamline.io/components/workout-logger.md)
- [Workout planner](https://beamline.io/components/workout-planner.md)

## Dependencies

`lucide-react`

## Import

```tsx
import { ConsequencePreview } from "@beamline/consequence-preview";
```

## Get it

Consequence preview is part of Beamline UI. Install the parts you need in your React app as compiled packages or editable source. [Get Beamline](https://beamline.io/checkout?pack=complete) · [Connect your agent](https://beamline.io/connect)

## More in Actions

- [Theme switch](https://beamline.io/components/theme-switch): The light/dark control: the page changes in one of four transitions starting from the button (reveal, eclipse, split, rise), and the icon turns between…
- [Confirm morph](https://beamline.io/components/confirm-morph): A destructive action that asks in place: the button morphs into its question, then into working, then into the result with Undo or Retry, without a dialog.
- [Swipe actions](https://beamline.io/components/swipe-actions): Row actions behind a swipe on touch screens and a two-finger swipe on a trackpad, and at the row's end for pointer and keyboard (a ⋯ menu in a narrow…
- [Hold to confirm](https://beamline.io/components/hold-to-confirm): Confirms a costly action by holding instead of clicking: a fill sweeps across while held and drains if let go early; pointer, touch, Space and Enter all…
- [Split button](https://beamline.io/components/split-button): One obvious action joined to a menu of close alternatives; both halves are real buttons and the menu uses the shared menu rows.
- [Copy button](https://beamline.io/components/copy-button): Copies a value and confirms in place: the icon becomes a check and the label says Copied; a refused copy says so instead of pretending.
- [Button](https://beamline.io/components/button): The action control: five weights and a brand fill, a buy/sell tone, sm/md/lg and a 44 px touch size, icon-only, a loading state that keeps its size and…

Part of [Beamline UI: polished, editable React components and complete screens for custom applications](https://beamline.io/). [Build your own style from any look; tokens, fonts, radii, motion; not only colours](https://beamline.io/docs/customize).
