Offer Letter Preview Card — Free Compensation Breakdown UI

Offer Letter Preview Card · Cards · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Itemized compensation
Base, bonus, and equity as separate labeled rows.
Highlighted total row
Estimated year-one total stands out visually.
Three distinct actions
Accept, Decline, and Negotiate each resolve differently.
Visible state change
Actions swap out for a result panel, not a silent no-op.
Outcome-specific styling
Green, red, and indigo result panels per action.
Key offer metadata
Start date, manager, and expiration surfaced clearly.
Single shared handler
One click listener drives all three actions via data-action.
Zero dependencies
Pure HTML, CSS, and JS.

About this UI Snippet

Offer Letter Preview Card — Compensation Breakdown & Response Actions

Screenshot of the Offer Letter Preview Card snippet rendered live

The offer letter preview card is the moment a candidate reads and responds to a job offer: role, compensation line items, key dates, and three distinct actions. This snippet builds it in plain HTML, CSS, and JavaScript.

Compensation as itemized rows, not one number

A <dl> lists base salary, signing bonus, and equity as separate rows, each with a secondary label (/yr, one-time, vesting terms) under the figure — then a visually distinct total row summarizes estimated year-one value. Breaking compensation into parts is what makes an offer legible instead of a single opaque number.

Three actions, three outcomes

Accept, Decline, and Negotiate are not decorative — each button carries a data-action, and a shared click handler looks up a distinct message per action from a messages object, then swaps the action row for a result panel styled uniquely per outcome (green for accept, red for decline, indigo for negotiate). One handler, three genuinely different end states.

A real, visible state change

Clicking any action hides the button row entirely and reveals #olpResult with outcome-specific text and color — so "acting on" the card isn't just a button press with no visible consequence, the card visibly resolves into whichever path was chosen.

Metadata that matters to a decision

Start date, hiring manager, and offer expiration are surfaced as their own row, separate from compensation — the practical details a candidate needs alongside the numbers to actually decide.

Customizing it

Wire each action to a real API call (e-signature for accept, a negotiation form for negotiate), add a countdown to the expiration date, or support multiple compensation currencies. Pair it with a job listing card for the original posting, or a candidate pipeline kanban for the recruiter's side of the same process.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Rather than guessing how to keep three different button outcomes maintainable, paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain how the shared click handler uses each button's data-action attribute together with a messages lookup object to avoid three near-duplicate event handlers, and why swapping the action row for a distinct result panel communicates a state change more clearly than simply disabling the clicked button. The same assistant is useful for extending the card into a real flow — ask it to wire the Accept button to an e-signature API call with a loading state while the request is in flight, add a live countdown to the offer's expiration date, or restructure the compensation breakdown to support multiple currencies or hourly contractor terms instead of an annual salary.

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 an "offer letter preview card" in plain HTML, CSS, and JavaScript — no frameworks, no dependencies.

Requirements:
- A card showing a job title, company/location subtitle, and an itemized compensation breakdown as a definition list: base salary, signing bonus, and equity, each with its figure and a small secondary note (e.g. "/yr", "one-time", vesting terms) — plus a visually distinct total row summarizing estimated year-one value.
- A metadata section showing start date, hiring manager, and offer expiration date as separate rows.
- Three action buttons: Accept, Decline, and Negotiate, each carrying a data-action attribute, all wired to a single shared click handler (not three separate near-duplicate handlers) that looks up a distinct outcome message per action from a lookup object.
- Clicking any action button must hide the entire action-button row and reveal a separate result panel showing the outcome-specific message, with the result panel's background/text color also differing per action (e.g. green for accept, red for decline, indigo/blue for negotiate) — this must be a real, visible state change, not just a disabled button or a console log.
- Keep it in a dark theme with clear visual hierarchy between the compensation total and its line items, and make sure the JavaScript only references classnames/ids/data attributes that exist in the HTML you write.

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 the HTML, CSS, and JSAn offer card with compensation breakdown and actions render.
  2. 2
    Review the comp rowsBase, bonus, and equity each show a value and a note.
  3. 3
    Click Accept, Decline, or NegotiateThe action row is replaced by a result panel.
  4. 4
    Compare outcomesEach action produces its own message and color.
  5. 5
    Edit the compensation figuresChange the dl rows for a different offer.
  6. 6
    Wire an action to your backendSend the real request inside its click handler.

Real-world uses

Common Use Cases

Recruiting platforms
HR offer-management tools
Show candidates a clear compensation summary.
Job boards
Extend a job listing card into an offer stage.
Internal mobility offers
Present role-change compensation changes.
Contractor rate proposals
Swap salary rows for hourly/project terms.
Promotion letters
Reuse the comp breakdown for raise summaries.

Got questions?

Frequently Asked Questions

Each button carries a data-action attribute ("accept", "decline", or "negotiate"). The shared click handler reads that attribute, looks up the corresponding text in a messages object, and applies a matching modifier class (olp-result--accept, --decline, or --negotiate) to the result panel — so a single function drives three visually and textually distinct end states instead of three near-duplicate handlers.

Hiding the entire action row and revealing a dedicated result panel makes the state change unambiguous — there's no risk of a disabled-but-still-visible button being misread as "still pending." It also frees the space for a message that explains what happens next, which a disabled button can't communicate on its own.

Inside the click handler's accept branch, instead of (or in addition to) swapping in the result panel immediately, fire your API call or open an e-signature modal first, and only show the success result panel once that call resolves. You could show a loading state on the button while the request is in flight, then reveal the result panel on success or an error message on failure.

Compute the difference between the current date and the expiration date shown in the metadata row, format it as "expires in N days," and update that text (or re-render it) on an interval or on page load. You could also swap the metadata row's styling to a warning color once the offer is within, say, 48 hours of expiring.

Model the offer as a data object (role, company, compensation line items, dates) and render the dl rows from it. Hold the response action in component state (null until clicked), conditionally render either the action buttons or the result panel based on that state, and call your real API from the click handler before updating state to reflect success. The compensation and metadata layout is plain CSS and ports unchanged.