Skip to content

Proposal Card

Weigh a proposed change against the current plan, then approve or dismiss it: a reroute, a reassignment, a new slot.

import { ProposalCard, ProposalFact, ProposalOption } from '@youj/react'

Installation & usage

npm i @youj/react

For an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.

Use a proposal card where someone, often an agent, suggests a change and a person decides: a reroute that saves half an hour, a load moved to another driver, a meeting moved to a free slot. It says where the proposal came from, what it is, then weighs the options side by side, the proposed one drawn as a dashed accent outline like a proposed route on a Route Map or a proposed run on Time Lanes. Its impact reads as figures, and your buttons decide it.

import { Button, ProposalCard } from '@youj/react'

const [status, setStatus] = useState<'pending' | 'approved' | 'dismissed'>('pending')

<ProposalCard
  source="Route agent · 92% confidence"
  title="Reroute load 4821 via Ridge Rd"
  options={[
    { id: 'current', label: 'Harbor Fwy · current', value: '15:49', detail: '+47 min', tone: 'danger' },
    { id: 'proposed', label: 'Ridge Rd · proposed', value: '15:18', detail: '−31 min', proposed: true },
  ]}
  facts={[{ label: 'Time saved', value: '31 min', tone: 'success' }]}
  status={status}
  resolution="Approved · driver notified"
  actions={<Button size="sm" onClick={() => setStatus('approved')}>Approve reroute</Button>}
/>

options are the plans side by side, usually the current one and the proposal (proposed: true). Each has a label, a value in mono (an ETA, a price) and a detail for how it moves against plan, inked by its tone. facts are the impact as a row of figures. note is a line over the actions: why it needs a person. You own status: pending shows actions; approved makes the proposal solid, steps the other options back and folds the actions away for your resolution; dismissed fades the proposal and keeps the plan.

Reference

Propsoptions, facts, status… (11)

Props

ProposalCard props
PropType
sourceReactNode

Where it came from, as an eyebrow: an agent's name, a confidence, an age.

–
titleRequiredReactNode–
descriptionReactNode–
optionsProposalOption[]

The options side by side: usually the current plan, then the proposal.

–
factsProposalFact[]

The impact as figures: time saved, extra distance, the window kept.

–
noteReactNode

A line over the actions: why it needs a person ("Route changes need approval").

–
actionsReactNode

Your buttons while it's pending.

–
statusProposalStatus
'pending''approved''dismissed'

default 'pending'

'pending'
resolutionReactNode

What shows once it's decided, in place of the actions: "Approved by you · 11:58".

–
headingLevel2 | 3 | 4

The title's heading level.

default 3

3
Other props pass through: extends ComponentProps<'article'>.

Options

ProposalOption props
PropType
idRequiredstring–
labelRequiredReactNode

"Harbor Fwy · current".

–
valueReactNode

The figure the options are weighed by: "15:49", "$1,842".

–
detailReactNode

How it moves against plan: "+47 min".

–
toneProposalTone
'neutral''success''warning''danger''accent'

The figure's and detail's ink. Default neutral; a proposed option defaults to accent.

–
proposedboolean

The option being proposed: a dashed accent outline until it's approved.

–
StylingParts and data attributes

Styling

Parts: proposal-card (with data-status). Each option row carries data-proposed and, once stepped back, data-muted. The card takes the theme's radius, border and card surface; pass className to place it, for example over a map.

MotionRows, actions and resolution · reduced motion

Motion

Motion tokens
MomentTokens
Decidedoption rows change their outline, tint and opacity
duration.fastease.standard

140ms · cubic-bezier(0.2, 0, 0, 1)

140mscubic-bezier(0.2, 0, 0, 1)
Actions out, resolution inthe two trade places through Collapse; a reversal continues from the current height
duration.baseease.standard

220ms · cubic-bezier(0.2, 0, 0, 1)

220mscubic-bezier(0.2, 0, 0, 1)

Reduced motion

Rows change tone in place, and the actions and resolution swap at once.

Reduced motion

Every change lands in place. The status still reads from the outline, the struck-through figure and the resolution text.

AccessibilityArticle, proposed row, resolution

Accessibility

  • The card is an article named by its title; headingLevel sets the title's level (3 by default).
  • The proposed option says so to screen readers ("Ridge Rd · proposed, Proposed"), from the labels' proposed, translated by the language packs.
  • Facts are a description list: each figure is read with its label.
  • The resolution sits in a polite status region, so approving or dismissing is announced.