Task Approval Flow Card — Free HTML CSS JS Snippet

Task Approval Flow Card · Cards · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Three distinct outcomes
Approve, reject, and request-changes are handled as separate intents, not one binary.
Conditional comment field
The changes textarea only appears when Request changes is clicked, keeping the default lean.
Single resolve() function
Every outcome shares one code path, so the badge and banner can never disagree.
Color-coded resolved banner
A matching icon and color confirm exactly what happened after the decision.
Cancelable change request
Cancel returns to the original three actions without losing the pending state.
Requester context up top
Avatar, name, and relative time establish who's asking before the decision.
Quoted comment in the resolution
A submitted change request echoes back the reviewer's own comment text.
Locked-in final state
Once resolved, the action row is replaced entirely rather than merely disabled.

About this UI Snippet

Task Approval Flow Card — Approve, Reject & Request Changes With a Resolved State

Screenshot of the Task Approval Flow Card snippet rendered live

Approval requests show up everywhere teams work together — publishing content, merging a change, releasing a budget, signing off a design. The task approval flow card packages the three real outcomes of an approval decision — approve, reject, or ask for revisions — into one self-contained card with a clean resolved state once acted on. Pair it with task approval flow card's sibling patterns like progress wizard for multi-step approval chains, or comment thread for the discussion that often follows.

Three distinct outcomes, not just yes/no

Most approval UIs collapse to a binary approve/reject, but real review almost always has a third path: "close, but not yet." This card treats Request changes as a first-class action rather than a variant of rejection — clicking it doesn't resolve the card immediately, it opens a comment field, because rejecting and asking for a revision are different intents that deserve different downstream handling (a rejected request usually needs to be resubmitted from scratch; a changes-requested one usually gets edited in place).

A comment field that appears only when needed

The textarea for explaining requested changes stays hidden until you actually click "Request changes," then swaps in for the action row entirely — you either commit to explaining what's needed or cancel back to the three original choices. This keeps the card's default state lean (three buttons, no clutter) while making sure a changes-requested action always comes with context, not a bare status flip.

One resolve() function, three visual outcomes

Approve, reject, and the submitted change request all funnel through a single resolve(kind, message) function that swaps the status badge's color and text, hides the action buttons, and shows a resolved banner with a matching icon and color (green check, red X, indigo pencil). Because every outcome shares one code path, the card can never end up in an inconsistent state — like showing "Approved" while the reject button is still clickable.

Clear, permanent resolved state

Once acted on, the card replaces its action row with a resolved banner and disables further clicks — modeling the reality that an approval decision, once made, is a fact about the request rather than something to be casually re-clicked. This is the same principle behind task approval flow card's status badge switching from a pending amber to a definitive final color.

Wiring it to a real workflow

Swap the click handlers' local resolve() calls for real API requests — POST the decision (and comment, if any) to your backend, then call resolve() once the request succeeds so the UI only shows the final state after it's actually persisted. Add a loading state on the clicked button while the request is in flight, and revert to the action row on failure so the user can retry.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't have to design the state machine for this card from scratch. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why Request changes is modeled as a distinct outcome from Reject rather than a variant of it, and how funneling all three outcomes through one resolve() function guarantees the status badge and the resolved banner can never disagree. The same assistant can help you optimize it — ask whether the card should support an "undo" within a short window after resolving, or whether the comment textarea should validate a minimum length before allowing submission. It's also useful for extending the flow: ask it to add a loading spinner while a real API call is in flight, chain multiple approval cards into a sequential multi-approver flow, or add file/screenshot attachments to the request-changes comment. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a "task approval flow card" in plain HTML, CSS, and JavaScript — no frameworks or libraries.

Requirements:
- Show a pending approval request card with the requester's avatar, name, a relative timestamp, a short title describing what's being requested, and a longer description, plus a status badge that starts in a "Pending" (amber) state.
- Provide three action buttons: Approve, Reject, and Request changes — these must represent three genuinely distinct outcomes, not a binary approve/reject with "request changes" treated as a lesser form of rejection.
- Clicking Request changes must NOT immediately resolve the card. Instead it should hide the three action buttons and reveal a comment textarea (auto-focused) with Cancel and Send request buttons; Cancel must return to the original three-button state without losing any prior context, and Send request must resolve the card only once submitted, with the resolution text including or referencing the comment that was typed.
- Clicking Approve or Reject must resolve the card immediately: hide the action buttons, update the status badge to a distinct color and label for that outcome, and show a resolved confirmation banner with a matching icon and color (e.g. green check for approved, red X for rejected, a different color/icon for changes-requested).
- Route all three possible resolutions through one shared function that updates the badge and shows the resolved banner, so it is structurally impossible for the badge state and the banner state to disagree with each other.
- Once a card is resolved (approved, rejected, or changes sent), it should not be possible to trigger any of the original three actions again — the action row must be fully replaced by the resolved banner, not merely disabled-looking.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA pending approval card renders with Approve, Reject, and Request changes buttons.
  2. 2
    Click Approve or RejectThe status badge updates and a resolved banner replaces the action buttons.
  3. 3
    Click Request changesA comment textarea appears in place of the buttons, focused and ready to type.
  4. 4
    Send or cancel the requestSending resolves the card with your comment quoted; cancel returns to the three actions.
  5. 5
    Reload to resetRefresh the sandbox to see the card in its original pending state again.
  6. 6
    Wire to a real APIReplace resolve() calls with actual approve/reject/request-changes endpoint calls.

Real-world uses

Common Use Cases

Content publishing approvals
Gate publishing a page or post behind an editor's sign-off before it goes live.
Design review sign-off
Approve or request revisions on a design handoff, pairing with comment thread for detailed feedback.
Expense and budget requests
Route spend requests through an approve/reject/request-detail flow.
Access and permissions requests
Approve a teammate's request for elevated access with an audit-friendly resolved state.
Multi-step approval chains
Combine with a progress wizard to show where a request sits across several approvers.
Onboarding task sign-off
Pair with an onboarding checklist widget where a manager approves completed steps.

Got questions?

Frequently Asked Questions

Rejecting and asking for a revision carry different intents and usually trigger different downstream flows — a rejected request is typically closed and resubmitted fresh, while a changes-requested one is expected to be edited and resubmitted in place. Giving Request changes its own comment field makes sure the requester always gets specific, actionable feedback instead of a bare rejection.

All three outcomes call the same resolve(kind, message) function, which is the single place that updates the status badge's class and text, hides the action row and comment box, and shows the resolved banner with a matching color and icon. Because there's one function instead of three separate branches of UI-updating code, the badge and the banner can never fall out of sync.

The Cancel button in the comment box clears the textarea and restores the original three-button action row, with the card still in its pending state — nothing is resolved and no comment is sent unless you explicitly click "Send request."

Replace the resolve() calls inside each button's click handler with an actual API request — POST the decision (and the comment text for a changes request) to your backend — and only call resolve() to update the UI after that request succeeds. Add a brief loading state on the clicked button while waiting, and on failure, leave the action row visible so the user can retry.

Model the card's state as a single status value (pending, approved, rejected, or changes-requested) plus a comment string. Render the action row, comment box, or resolved banner based on that status, and call your framework's state setter from each button's handler instead of resolve()'s direct DOM manipulation — the visual states and CSS classes map over directly.