webdevpuneet.com

Offer Letter Preview Card — Free Compensation Breakdown UI

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

CategoryCards

Copy & paste

Source Code

<div class="olp-card" id="olpCard">
  <div class="olp-head">
    <span class="olp-badge">Offer</span>
    <h2>Senior Product Designer</h2>
    <p class="olp-sub">Northwind Labs &middot; Remote (US)</p>
  </div>

  <dl class="olp-comp">
    <div class="olp-comp-row">
      <dt>Base salary</dt>
      <dd>$148,000<span>/yr</span></dd>
    </div>
    <div class="olp-comp-row">
      <dt>Signing bonus</dt>
      <dd>$10,000<span>one-time</span></dd>
    </div>
    <div class="olp-comp-row">
      <dt>Equity</dt>
      <dd>0.08%<span>4yr vest, 1yr cliff</span></dd>
    </div>
    <div class="olp-comp-row olp-comp-total">
      <dt>Est. year-one total</dt>
      <dd>$158,000</dd>
    </div>
  </dl>

  <div class="olp-meta">
    <div><span class="olp-meta-label">Start date</span><span class="olp-meta-val">September 15, 2026</span></div>
    <div><span class="olp-meta-label">Manager</span><span class="olp-meta-val">Priya Shah</span></div>
    <div><span class="olp-meta-label">Offer expires</span><span class="olp-meta-val">August 29, 2026</span></div>
  </div>

  <div class="olp-actions" id="olpActions">
    <button class="olp-btn olp-btn--decline" type="button" data-action="decline">Decline</button>
    <button class="olp-btn olp-btn--negotiate" type="button" data-action="negotiate">Negotiate</button>
    <button class="olp-btn olp-btn--accept" type="button" data-action="accept">Accept offer</button>
  </div>

  <div class="olp-result" id="olpResult" hidden></div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

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.

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 platform offers
Pair with a candidate pipeline kanban so a candidate reaching the offer stage sees a clear summary.
HR offer management
Show a compensation breakdown of base, bonus and equity as separate rows, with a highlighted estimated first-year total.
Job board offer stages
Extend a job listing card into the offer stage, with Accept, Decline and Negotiate each resolving differently.
Internal mobility offers
Present compensation for a role change, replacing action buttons with a result panel so the response is never a silent no-op.
Contractor and promotion letters
Swap salary rows for hourly or project rates, or reuse the breakdown to summarise a raise on a promotion letter.

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.