Figures are buttons. Enter or Space opens the page. Escape closes it.
The close control is in the page’s tab order. Focus rings stay visible.
Close, a click outside the page, or Escape dismisses it and returns focus to the figure.
The open page is a role="dialog" labelled “Travel policy”. The hover card is aria-hidden; it also shows on keyboard focus.
A touch tap opens the page. It doesn’t show the hover card.
Motion follows prefers-reduced-motion.
Rules
Cite claims, not whole answers.
Peek in place; don’t leave the page.
Highlight the exact line.
No source? Say so.
Action Review
A confirm-before-action card. The message explains the change and the policy. The card is the decision.
Use it when an agent is about to pay, send, or delete. Skip it for a reversible, low-stakes step.
It follows A2UI's component model and structure: Card, Text, Row, Button, TextField, with Latch's own soft card style. Citation and the receipt are Latch extensions.
State
The card shows the change, before → after, and the message cites §3.2.
Edit opens the amount in place. Enter or Done applies it.
Approve leaves a receipt in the thread: who and when. Undo stays open for 10 minutes.
A decline leaves a receipt too.
After undoMs, Undo goes away and the receipt reads “Can’t be undone”. Mounted here with undoMs: 1000.
Usage
Embed
Latch.mount(element, { demo: "action-review" });
Where it ships
Action Review ships in the site’s embed bundle (vendor/latch/action-review.js, loaded by latch.js). It isn’t in the latch repo or the latch-ui package yet.
Props
mount(element, options)
Option
Type
Default
Notes
demo
"action-review"
"citations"
Pass "action-review" to mount this card.
undoMs
number
600000
How long Undo stays open after a decision (10 minutes).
returns
() => void
—
Removes the demo and its listeners.
Internal state (read-only)
Field
Type
Notes
mode
"review" | "receipt"
Card or receipt.
editing
boolean
Amount field open.
decision
"approve" | "decline" | null
Set when you decide.
expired
boolean
Undo window closed.
Accessibility
Decisions are read out in an aria-live="polite" region, e.g. “Approved. $3,850. PO-1042 …”.
The §3.2 citation is a button labelled “Spend policy §3.2, under $5,000 needs one approver”.
Escape closes the policy card. It does not approve or decline.
The amount field uses inputmode="decimal", aria-invalid and aria-describedby for its error. Enter applies the edit.
After an edit or Undo, focus moves to the primary button.
When the undo window ends, “This can’t be undone.” is announced.
Rules
Explain in the message, decide in the card.
Show the change, not the intent. The card is before → after.
Every action cites its permission with a Citation chip in the message.
The primary button names the action. Nothing is pre-selected.
Edit in place before approving, without starting over.
Every outcome leaves a receipt in the thread: who and when. Undo stays open for 10 minutes. A decline leaves a receipt too.
One surface in the thread. After the citation, the primary action is next. Escape closes the policy card. It does not approve or decline.