Checkpoint

Approval Flow Animation for React

A request that has to clear your rules before it runs. A token drops out from under the request and drives down a lane, past one barrier per rule: each one swings open downward, the way the token is going, as its rule passes and the row ticks. A rule that needs sign-off keeps its barrier down and turns its row into Approve and Deny; a rule the request breaks stays shut, and the token knocks against it twice and is turned away. Every outcome rolls into a short log underneath.

  • AI
  • Interaction
  • Data

Uses clsx, tailwind-merge

Open in ChatGPTOpen in Claude
speed1.00×
dwell1600ms
<Checkpoint />

The demo follows as you drag.

Guardrails · Fernway support agent

read_ticket#4821 · Monstera arrived with a cracked pot
Queued
  1. RoleActs as a support teammate
  2. ScopeRead orders, issue refunds
  3. AmountRefunds over $500 need finance
  4. AuditEvery call lands in the trail
    Take this demo to your coding agent.
    Add this moment from KeyframeUI to our project and adapt it to our content.
    
    Checkpoint: A request that has to clear your rules before it runs. A token drops out from under the request and drives down a lane, past one barrier per rule: each one swings open downward, the way the token is going, as its rule passes and the row ticks. A rule that needs sign-off keeps its barrier down and turns its row into Approve and Deny; a rule the request breaks stays shut, and the token knocks against it twice and is turned away. Every outcome rolls into a short log underneath.
    This demo, Agent Guardrails: A plant shop's support agent: reading a ticket clears, a $1,240 refund waits for finance, and deleting a customer is blocked by its scope.
    
    1. Install it:
       npx shadcn@latest add @keyframeui/checkpoint-demo
       (If the CLI can't find @keyframeui, use https://keyframeui.com/r/checkpoint-demo.json instead.)
       It adds the demo, components/checkpoint-demo.tsx, and the component, components/ui/checkpoint.tsx.
    
    2. Make it ours: our copy, data, images and colors. Keep the structure the motion depends on. If we only need the effect, use <Checkpoint> on its own.
    
    3. These props set its feel. Change them before you change the file:
       - speed (default 1): How fast each request plays, as a multiple of the default pace: the token's legs, the barriers swinging open and shut, the rows ticking, a knock, the log line rolling in and the next request sliding in. `2` is twice as fast.
       - dwell (default 1600): How long an outcome stays up before the next request comes in, in ms. Only with autoplay.
    
    4. Don't change, in components/ui/checkpoint.tsx:
       - One async runner owns every move: WAAPI keyframes on the `data-part` hooks, each end state written inline as it finishes. React only decides which row's Approve / Deny can be reached.
       - Only transform and opacity move. A barrier swings down about its pivot, the way the token travels, so its tip stays clear of the token, and closes only once the token is past its reach.
       - Approve / Deny float over the row's right end and stay `inert` unless that row holds, so a hold never moves the row or focus; a decision made from the keyboard leaves focus on the checkpoint.
       - Off screen and on a hidden tab the runner waits between steps (`gate()`), and the approval timer and the waiting dot pause where they are.
       - Under reduced motion there's no token, travel, lifting or knocking: each rule resolves with an opacity change and the barriers only change color.
       Its timing is all in the TIMING block at the top of that file.
    
    Docs: https://keyframeui.com/components/checkpoint.md

    Installation

    Add the component on its own with the CLI, or copy the file in by hand.

    The CLI writes components/ui/checkpoint.tsx and installs clsx and tailwind-merge. It needs the @keyframeui registry in components.json; add it once per project.

    npx shadcn@latest add @keyframeui/checkpoint

    Examples

    Finished sections built with Checkpoint. Each one installs whole, with the component included.

    Release Gate

    Releases through tests, preview, migrations and a change freeze, where a Friday-evening deploy is turned away.

    N

    Release gate · northwind/production

    web@2.14.018 commits · maya.l
    Queued
    1. Tests1,284 passing on the release commit
    2. PreviewSigned off on the preview URL
    3. MigrationsDestructive changes need a DBA
    4. Change freezeNo deploys Fri 16:00 → Mon 09:00
      Take this demo to your coding agent.
      Add this moment from KeyframeUI to our project and adapt it to our content.
      
      Checkpoint: A request that has to clear your rules before it runs. A token drops out from under the request and drives down a lane, past one barrier per rule: each one swings open downward, the way the token is going, as its rule passes and the row ticks. A rule that needs sign-off keeps its barrier down and turns its row into Approve and Deny; a rule the request breaks stays shut, and the token knocks against it twice and is turned away. Every outcome rolls into a short log underneath.
      This demo, Release Gate: Releases through tests, preview, migrations and a change freeze, where a Friday-evening deploy is turned away.
      
      1. Install it:
         npx shadcn@latest add @keyframeui/checkpoint-deploy-demo
         (If the CLI can't find @keyframeui, use https://keyframeui.com/r/checkpoint-deploy-demo.json instead.)
         It adds the demo, components/checkpoint-deploy-demo.tsx, and the component, components/ui/checkpoint.tsx.
      
      2. Make it ours: our copy, data, images and colors. Keep the structure the motion depends on. If we only need the effect, use <Checkpoint> on its own.
      
      3. These props set its feel. Change them before you change the file:
         - speed (default 1): How fast each request plays, as a multiple of the default pace: the token's legs, the barriers swinging open and shut, the rows ticking, a knock, the log line rolling in and the next request sliding in. `2` is twice as fast.
         - dwell (default 1600): How long an outcome stays up before the next request comes in, in ms. Only with autoplay.
      
      4. Don't change, in components/ui/checkpoint.tsx:
         - One async runner owns every move: WAAPI keyframes on the `data-part` hooks, each end state written inline as it finishes. React only decides which row's Approve / Deny can be reached.
         - Only transform and opacity move. A barrier swings down about its pivot, the way the token travels, so its tip stays clear of the token, and closes only once the token is past its reach.
         - Approve / Deny float over the row's right end and stay `inert` unless that row holds, so a hold never moves the row or focus; a decision made from the keyboard leaves focus on the checkpoint.
         - Off screen and on a hidden tab the runner waits between steps (`gate()`), and the approval timer and the waiting dot pause where they are.
         - Under reduced motion there's no token, travel, lifting or knocking: each rule resolves with an opacity change and the barriers only change color.
         Its timing is all in the TIMING block at the top of that file.
      
      Docs: https://keyframeui.com/components/checkpoint.md

      Spend Policy

      A studio's company card policy: a flight waits on its receipt, and a casino charge is blocked by merchant category.

      Card policy · Tinta Studio

      3 cards live

      FIGMA* ANNUAL$45.00 · Lena Ortiz
      Queued
      1. MerchantSoftware, travel, meals, print
      2. Per chargeAnything larger goes through a PO
      3. ReceiptOver $75 needs one, or finance signs
      4. BudgetDesign team, Q4
        Take this demo to your coding agent.
        Add this moment from KeyframeUI to our project and adapt it to our content.
        
        Checkpoint: A request that has to clear your rules before it runs. A token drops out from under the request and drives down a lane, past one barrier per rule: each one swings open downward, the way the token is going, as its rule passes and the row ticks. A rule that needs sign-off keeps its barrier down and turns its row into Approve and Deny; a rule the request breaks stays shut, and the token knocks against it twice and is turned away. Every outcome rolls into a short log underneath.
        This demo, Spend Policy: A studio's company card policy: a flight waits on its receipt, and a casino charge is blocked by merchant category.
        
        1. Install it:
           npx shadcn@latest add @keyframeui/checkpoint-spend-demo
           (If the CLI can't find @keyframeui, use https://keyframeui.com/r/checkpoint-spend-demo.json instead.)
           It adds the demo, components/checkpoint-spend-demo.tsx, and the component, components/ui/checkpoint.tsx.
        
        2. Make it ours: our copy, data, images and colors. Keep the structure the motion depends on. If we only need the effect, use <Checkpoint> on its own.
        
        3. These props set its feel. Change them before you change the file:
           - speed (default 1): How fast each request plays, as a multiple of the default pace: the token's legs, the barriers swinging open and shut, the rows ticking, a knock, the log line rolling in and the next request sliding in. `2` is twice as fast.
           - dwell (default 1600): How long an outcome stays up before the next request comes in, in ms. Only with autoplay.
        
        4. Don't change, in components/ui/checkpoint.tsx:
           - One async runner owns every move: WAAPI keyframes on the `data-part` hooks, each end state written inline as it finishes. React only decides which row's Approve / Deny can be reached.
           - Only transform and opacity move. A barrier swings down about its pivot, the way the token travels, so its tip stays clear of the token, and closes only once the token is past its reach.
           - Approve / Deny float over the row's right end and stay `inert` unless that row holds, so a hold never moves the row or focus; a decision made from the keyboard leaves focus on the checkpoint.
           - Off screen and on a hidden tab the runner waits between steps (`gate()`), and the approval timer and the waiting dot pause where they are.
           - Under reduced motion there's no token, travel, lifting or knocking: each rule resolves with an opacity change and the barriers only change color.
           Its timing is all in the TIMING block at the top of that file.
        
        Docs: https://keyframeui.com/components/checkpoint.md

        Props

        rules Required

        TypeCheckpointRule[]

        The rules, top to bottom, as { id, label, detail?, value?, icon? }. label is the rule's name, detail one line under it, value a short mono tag on the right (hidden in boxes under 26rem) and icon a small glyph drawn in currentColor. Each rule gets a barrier in the lane.

        requests Required

        TypeCheckpointRequest[]

        The requests that come through, in order, as { id, label, meta?, icon?, verdicts?, reason? }. label is what's asking, in mono; meta says who sent it and what it carries; verdicts maps rule ids to "pass", "hold" or "block" (a rule left out passes); reason replaces the rule's name in the status when it's blocked.

        current

        Typestring

        The id of the request to run. Set it to drive the checkpoint yourself: each change runs that request once, sliding it in. Leave it out and autoplay runs them in turn.

        autoplay

        TypebooleanDefaulttrue

        Run the requests one after another and start over after the last. With false the first one runs once. It waits off screen and on a hidden tab.

        approveAfter

        Typenumber | falseDefault2400

        Ms a rule that needs sign-off waits before it approves itself, with a hairline filling under Approve, or false to wait for the visitor. Only with autoplay. Pointing at the buttons holds the timer.

        speed

        TypenumberDefault1

        How fast each request plays, as a multiple of the default pace: the token's legs, the barriers swinging open and shut, the rows ticking, a knock, the log line rolling in and the next request sliding in. 2 is twice as fast.

        dwell

        TypenumberDefault1600

        How long an outcome stays up before the next request comes in, in ms. Only with autoplay.

        approver

        TypestringDefault"a reviewer"

        Who signs off when a hold approves itself, as written in the log.

        log

        TypebooleanDefaulttrue

        Show the last three outcomes under the rules, newest on top, each with the time it was settled.

        onDecision

        Type(requestId: string, outcome: CheckpointOutcome, ruleId?: string) => void

        Called when a request is settled. outcome is "cleared", "blocked" or "denied", and ruleId is the rule that stopped it.

        className

        Typestring

        Extra classes for the root. Set the accent with --checkpoint-accent (#ff3b1f, #ff5a3c in dark): the token, the open barrier, passed boxes and waiting rows use it, and the Approve button and accent text use a darker mix of it so white and accent text stay readable. Override those with --checkpoint-button and --checkpoint-text.