Source Code

<div class="wrap">
  <div class="locker-card">
    <div class="locker-head">
      <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="3" y1="15" x2="21" y2="15"/><line x1="9" y1="3" x2="9" y2="21"/></svg>
      <div>
        <h3>Package ready for pickup</h3>
        <p>Riverside Station &middot; Locker <span id="lockerNum">C-14</span></p>
      </div>
    </div>

    <div class="locker-code-box">
      <span class="locker-code-label">Pickup code</span>
      <div class="locker-code-row">
        <span class="locker-code" id="lockerCode">4 8 2 9 1 7</span>
        <button class="locker-copy-btn" id="lockerCopyBtn">Copy</button>
      </div>
    </div>

    <div class="locker-expiry">
      <div class="locker-expiry-bar"><div class="locker-expiry-fill" id="expiryFill"></div></div>
      <span class="locker-expiry-text" id="expiryText">Code expires in 47:58:00</span>
    </div>

    <button class="locker-regen-btn" id="regenBtn">Generate new code</button>
    <p class="locker-note" id="lockerNote">Show this code, or scan the QR on the locker screen, within the pickup window.</p>
  </div>
</div>

Parcel Locker Pickup Code Card — Free HTML CSS JS Snippet

Parcel Locker Pickup Code Card · Cards · Plain HTML, CSS & JS · Live preview

What's included

Features

Live countdown timer with progress bar that recolors from indigo to amber-red under a configurable urgency threshold
Six-digit code displayed large, monospaced, and digit-spaced for easy keypad entry
Copy-to-clipboard with async Clipboard API and hidden-textarea execCommand fallback
Regenerate action with a simulated request delay and a post-swap cooldown to prevent accidental double-regeneration
Fade-and-swap transition when the code changes instead of an instant jarring digit-flip
Expired state disables the countdown text and re-enables the regenerate button automatically
Helper note text updates after regeneration to confirm the old code no longer works
Compact card suited to a push notification landing page or delivery-app pickup screen

About this UI Snippet

Parcel Locker Pickup Code Card — Countdown Timer, Copy-to-Clipboard & Code Regeneration

Screenshot of the Parcel Locker Pickup Code Card snippet rendered live

Package lockers hand a customer one job: type or scan a code into a keypad before it stops working. This card is built around that single moment — a large, unmistakably legible pickup code, a countdown that makes the expiry window impossible to miss, and a copy button so the code can be pasted into a locker's touchscreen search field instead of misread digit by digit.

A countdown that recolors itself as urgency rises

remaining starts at just under 48 hours and ticks down every second via setInterval(tick, 1000), which decrements it by 30 to keep the demo timeline fast without changing the display format. updateExpiry() derives a percentage of TOTAL_SECONDS remaining for the progress bar's width, and once remaining drops under four hours it adds an .urgent class that swaps the bar's gradient from indigo to amber-red and turns the countdown text red. The threshold is deliberately not the final minute — someone who still has hours left doesn't need alarm, but someone under four hours benefits from a visual nudge before the deadline sneaks past them.

Digits formatted for a keypad, not for reading as a word

formatCode() inserts a space between every digit of the six-digit code ('482917' becomes '4 8 2 9 1 7') and renders it in a monospace font at a large size. Locker keypads and touchscreens expect digits typed one at a time; a run of six digits with no separation is easy to lose your place in mid-entry, especially glancing between a phone screen and a physical keypad in a lobby or hallway.

Regeneration with a cooldown, not an instant re-roll

Clicking #regenBtn disables the button, shows a "Generating…" label, and only after a 900ms delay swaps in a fresh six-digit code via randomCode() — mimicking a real request to a locker network's backend rather than instantaneous client-side randomness. canRegen guards against a second click firing mid-request, and after the swap the button stays disabled for a further 2.2 seconds with a "New code generated" confirmation label before returning to normal, giving the user time to notice their old code just changed before they could accidentally tap regenerate again and invalidate the one they just got.

Copy button with the same clipboard fallback pattern as any code field

copyCode() tries navigator.clipboard.writeText() first and falls back to a hidden <textarea> plus document.execCommand('copy') when the async Clipboard API isn't available, keeping the button usable across the wide range of devices and browsers a delivery-pickup notification link might be opened in — an in-app webview, an older Android browser, or a non-HTTPS staging link.

The swap transition avoids a jarring instant digit-flip

swapCode() adds a .swap class that fades and lifts the code out over 220ms before the text content actually changes underneath, then removes the class so the new digits fade back in. Instantly replacing six digits with six different digits reads as a glitch; the brief fade communicates "this value just changed" the same way the incident widget's row-removal transition does.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how the countdown's urgency threshold works and why it recolors at four hours specifically rather than at the final few minutes. The same assistant can help optimize it — for instance asking whether the setInterval-based countdown should instead compute remaining time from a fixed expiry timestamp (Date.now() based) to avoid any drift on a tab left open for hours. It's also useful for extending the card: ask it to add a QR code alongside the digit code for locker screens with a camera, support multiple pending packages in one card, or persist the countdown across a page refresh using localStorage. 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 "parcel locker pickup code" card in plain HTML, CSS, and JavaScript — no framework, no library.

Requirements:
- Display a large, digit-spaced six-digit pickup code alongside a station name and locker number, plus a "Copy" button that copies just the raw digits (no spaces) to the clipboard using the async Clipboard API with a fallback to a hidden textarea and execCommand for browsers without it, showing a brief inline "Copied!" confirmation that reverts after roughly a second and a half.
- Implement a live countdown timer (ticking at least once per second) showing hours, minutes, and seconds remaining until the code expires, backed by a horizontal progress bar whose fill width shrinks in proportion to time remaining.
- Once remaining time drops below a configurable urgency threshold (for example four hours), both the progress bar and the countdown text must visually switch to a distinct "urgent" color scheme, and switch back if the countdown is reset above the threshold.
- Add a "Generate new code" button that, on click, disables itself and shows a brief loading label, then after a short simulated delay replaces the currently displayed code with a newly generated six-digit code using a fade-out/fade-in transition (not an instant text swap), resets the countdown to its full duration, and stays disabled for a couple of seconds afterward before becoming clickable again, so the button cannot be clicked twice in a row before the previous request would realistically complete.
- When the countdown reaches zero, switch the countdown text to an "expired" message and make sure the regenerate button is enabled so the user's only remaining action is requesting a new code.

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
    Watch the countdownThe expiry timer ticks down every second and the progress bar and text turn urgent-red under four hours remaining.
  2. 2
    Click CopyCopies the current six-digit code to the clipboard, showing a brief "Copied!" confirmation on the button.
  3. 3
    Click "Generate new code"Simulates requesting a fresh code from the locker network — the old code fades out and a new one fades in, and the timer resets to a full window.
  4. 4
    Replace the locker and code valuesUpdate the station name, locker number in the HTML, and the initial code passed to formatCode() and copyBtn.dataset.value in the JS.
  5. 5
    Wire to a real pickup APIReplace randomCode() with a fetch call to your locker network's code-issuance endpoint, and set remaining from the real expiry timestamp it returns.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for a React component, or "Tailwind" for a Tailwind CSS version.

Real-world uses

Common Use Cases

Delivery and parcel locker apps
The core use case — surface this after a delivery notification so a customer can retrieve a package without digging through email for the original confirmation.
Grocery and click-and-collect pickup
Show a pickup code for a curbside or in-store locker order alongside the Delivery ETA Card for the full order-to-pickup journey.
Dry cleaning and laundry locker services
Any unattended locker-based pickup service — laundry, key exchange, equipment rental — can reuse the same countdown-and-code pattern.
Campus and coworking parcel rooms
University mailrooms and coworking spaces increasingly use smart lockers for resident and member deliveries; this card fits their pickup-notification email or app screen.
Learn urgency-threshold styling
A clean example of a countdown that stays visually calm most of the time and only escalates its color once a real deadline threshold is crossed.
Fallback-safe clipboard copy
Demonstrates the standard pattern of trying the modern async Clipboard API first and degrading gracefully to execCommand for older or restricted browser contexts.

Got questions?

Frequently Asked Questions

updateExpiry() checks whether remaining is under four hours (3600 * 4 seconds) and, if so, adds an .urgent class to both the progress bar fill and the countdown text, which swaps their colors from indigo/gray to an amber-to-red gradient and solid red respectively.

randomCode() uses Math.random() for demo purposes, which is not cryptographically secure. In production, the code should be generated server-side using a secure random source and returned to the client via your locker network's API rather than generated in the browser.

canRegen and the disabled attribute stay set for about 2.2 seconds after the new code appears, giving the user a moment to register that their old code just changed before they could tap regenerate again and invalidate the code they just received.

The countdown text switches to an "expired" message and the regenerate button is explicitly re-enabled (in case it was mid-cooldown), since the only useful action left once a code expires is requesting a fresh one.

formatCode() joins the six digits with spaces to make the code easier to read digit-by-digit while typing it into a physical locker keypad, rather than trying to parse a run of six unspaced characters at a glance.

Replace randomCode() with a fetch() call to your provider's code-issuance endpoint, set remaining from the real expiresAt timestamp the API returns (converted to seconds from now), and update the station and locker number text from the same response.