Mobile Boarding Pass Screen — Free HTML CSS JS Snippet
Mobile Boarding Pass Screen · Mobile · Plain HTML, CSS & JS · Live preview
Copy & paste
Source Code
<div class="bps-phone">
<div class="bps-screen">
<div class="bps-status"><span>9:41</span><span class="bps-batt"><i></i></span></div>
<header class="bps-head">
<button class="bps-back" aria-label="Back">‹</button>
<h1>Boarding Pass</h1>
<button class="bps-share" aria-label="Share">↷</button>
</header>
<div class="bps-scroll">
<div class="bps-card" id="bpsCard">
<div class="bps-face bps-front">
<div class="bps-airline">
<span class="bps-logo">✈</span>
<div><b>Aerolinx</b><small id="bpsClass">Boarding Group B</small></div>
</div>
<div class="bps-route">
<div class="bps-pt"><b>JFK</b><small>New York</small></div>
<div class="bps-plane">
<div class="bps-dots"></div>
<span>✈</span>
</div>
<div class="bps-pt right"><b>LHR</b><small>London</small></div>
</div>
<div class="bps-grid">
<div><small>PASSENGER</small><b>A. Whitfield</b></div>
<div><small>FLIGHT</small><b>AX 204</b></div>
<div><small>DATE</small><b>Sep 14</b></div>
<div><small>GATE</small><b id="bpsGate">C12</b></div>
<div><small>SEAT</small><b>14A</b></div>
<div><small>BOARDING</small><b>10:35</b></div>
</div>
<div class="bps-perf"></div>
<div class="bps-stub">
<div class="bps-stub-row"><span>Departs</span><b>11:05 AM</b></div>
<div class="bps-stub-row"><span>Terminal</span><b>4</b></div>
<button class="bps-flip-btn" id="bpsFlipBtn">Tap to view barcode ↻</button>
</div>
</div>
<div class="bps-face bps-back">
<p class="bps-back-label">Scan at gate</p>
<div class="bps-barcode" aria-hidden="true"></div>
<p class="bps-pnr">PNR <b>Q7X4M2</b></p>
<button class="bps-flip-btn light" id="bpsFlipBack">↻ Back to pass</button>
</div>
</div>
<button class="bps-wallet" id="bpsWallet">
<span></span> Add to Apple Wallet
</button>
<p class="bps-added" id="bpsAdded" hidden>✓ Added to Wallet</p>
</div>
</div>
</div>*{box-sizing:border-box;margin:0;padding:0}
html{scrollbar-width:none;-ms-overflow-style:none}
html::-webkit-scrollbar{display:none}
body{font-family:system-ui,-apple-system,sans-serif;background:#1e293b;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:24px;scrollbar-width:none;-ms-overflow-style:none}
body::-webkit-scrollbar{display:none}
.bps-phone{width:288px;height:600px;background:#0b1220;border-radius:46px;padding:12px;box-shadow:0 30px 60px -20px rgba(0,0,0,.6),inset 0 0 0 2px #1e293b}
.bps-screen{width:100%;height:100%;border-radius:34px;overflow:hidden;background:#f1f5f9;color:#0f172a;display:flex;flex-direction:column}
.bps-status{display:flex;justify-content:space-between;align-items:center;padding:13px 24px 0;font-size:13px;font-weight:700}
.bps-batt{width:22px;height:11px;border:1.4px solid currentColor;border-radius:3px;position:relative;display:inline-block}
.bps-batt::after{content:'';position:absolute;right:-3px;top:3px;width:2px;height:5px;background:currentColor;border-radius:0 1px 1px 0}
.bps-batt i{position:absolute;left:1.4px;top:1.4px;bottom:1.4px;width:82%;background:currentColor;border-radius:1px}
.bps-head{display:flex;align-items:center;justify-content:space-between;padding:8px 14px 12px}
.bps-back,.bps-share{background:rgba(15,23,42,.06);border:none;width:30px;height:30px;border-radius:50%;font-size:16px;color:#0f172a;cursor:pointer}
.bps-back{font-size:20px}
.bps-head h1{font-size:15px;font-weight:800}
.bps-scroll{flex:1;overflow-y:auto;padding:4px 18px 22px;scrollbar-width:none;-ms-overflow-style:none}
.bps-scroll::-webkit-scrollbar{display:none}
.bps-card{position:relative;perspective:1200px;height:378px;margin-bottom:18px}
.bps-face{position:absolute;inset:0;backface-visibility:hidden;transition:transform .5s cubic-bezier(.4,0,.2,1);border-radius:20px;background:#fff;box-shadow:0 14px 30px -14px rgba(0,0,0,.25);overflow:hidden}
.bps-front{transform:rotateY(0)}
.bps-back{transform:rotateY(180deg);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;background:#0f172a;color:#fff;padding:20px}
.bps-card.flipped .bps-front{transform:rotateY(-180deg)}
.bps-card.flipped .bps-back{transform:rotateY(0)}
.bps-airline{display:flex;align-items:center;gap:10px;background:linear-gradient(135deg,#4f46e5,#7c3aed);color:#fff;padding:16px 18px}
.bps-logo{font-size:20px}
.bps-airline b{display:block;font-size:14.5px}
.bps-airline small{font-size:11px;opacity:.85}
.bps-route{display:flex;align-items:center;justify-content:space-between;padding:18px 20px 6px}
.bps-pt b{font-size:22px;letter-spacing:.5px}
.bps-pt small{display:block;font-size:10.5px;color:#94a3b8;margin-top:2px}
.bps-pt.right{text-align:right}
.bps-plane{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;position:relative;color:#6366f1;font-size:15px;padding:0 10px}
.bps-dots{flex:1;border-top:1.6px dashed #cbd5e1}
.bps-grid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:14px 8px;padding:14px 20px}
.bps-grid small{display:block;font-size:9px;color:#94a3b8;letter-spacing:.4px;margin-bottom:3px}
.bps-grid b{font-size:13px}
.bps-perf{height:0;border-top:2px dashed #e2e8f0;margin:2px 0;position:relative}
.bps-perf::before,.bps-perf::after{content:'';position:absolute;top:-9px;width:18px;height:18px;background:#f1f5f9;border-radius:50%}
.bps-perf::before{left:-9px}
.bps-perf::after{right:-9px}
.bps-stub{padding:14px 20px 18px}
.bps-stub-row{display:flex;justify-content:space-between;font-size:12.5px;color:#475569;margin-bottom:8px}
.bps-stub-row b{color:#0f172a}
.bps-flip-btn{width:100%;background:#eef2ff;color:#4f46e5;border:none;border-radius:10px;padding:10px;font-size:12.5px;font-weight:700;cursor:pointer;font-family:inherit;margin-top:6px}
.bps-flip-btn.light{background:rgba(255,255,255,.12);color:#fff}
.bps-back-label{font-size:11px;letter-spacing:.6px;text-transform:uppercase;color:#94a3b8}
.bps-barcode{width:100%;height:64px;background:repeating-linear-gradient(90deg,#fff 0 2px,transparent 2px 4px,#fff 4px 7px,transparent 7px 8px,#fff 8px 11px,transparent 11px 13px)}
.bps-pnr{font-size:13px;color:#cbd5e1}
.bps-pnr b{color:#fff;letter-spacing:2px;margin-left:4px}
.bps-wallet{width:100%;background:#000;color:#fff;border:none;border-radius:12px;padding:13px;font-size:13.5px;font-weight:700;cursor:pointer;font-family:inherit;display:flex;align-items:center;justify-content:center;gap:8px;transition:opacity .15s}
.bps-wallet:hover{opacity:.85}
.bps-added{text-align:center;font-size:12.5px;color:#16a34a;font-weight:700;margin-top:10px}var card = document.getElementById('bpsCard');
document.getElementById('bpsFlipBtn').addEventListener('click', function () {
card.classList.add('flipped');
});
document.getElementById('bpsFlipBack').addEventListener('click', function () {
card.classList.remove('flipped');
});
var walletBtn = document.getElementById('bpsWallet');
var addedMsg = document.getElementById('bpsAdded');
walletBtn.addEventListener('click', function () {
walletBtn.disabled = true;
walletBtn.textContent = 'Adding…';
setTimeout(function () {
walletBtn.style.display = 'none';
addedMsg.hidden = false;
}, 700);
});
// Simulated live gate update, the way a real airline app pushes gate changes.
setTimeout(function () {
var gate = document.getElementById('bpsGate');
gate.textContent = 'C18';
gate.style.color = '#dc2626';
setTimeout(function () { gate.style.color = ''; }, 1500);
}, 4000);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.
What's included
Features
About this UI Snippet
Mobile Boarding Pass Screen — Flip-to-Barcode Ticket with Live Gate Update

A boarding pass is a ticket UI with a very specific job: show route, time, seat, and gate at a glance from across a security line, then flip to a scannable barcode the instant a gate agent asks for it. This snippet builds that full interaction inside a CSS phone frame — a front face with the standard airline-ticket layout (route, perforated stub, flight details), a genuine 3D flip to a barcode back face, an Apple Wallet-style add button, and a simulated live gate-change push notification.
A real 3D CSS flip, not a fade
The card is two absolutely-positioned .bps-face elements inside a perspective: 1200px container, each with backface-visibility: hidden. The front starts at rotateY(0), the back at rotateY(180deg) — so before any interaction, the back face is already physically behind the front, just rotated out of view. Adding .flipped to the card rotates the front to -180deg and the back to 0deg simultaneously, both eased with the same transition: transform .5s, producing a genuine card-flip rather than a cross-fade. This is the correct technique whenever a UI needs to visually represent "the same object, other side" rather than "a different screen."
The perforated-stub ticket motif
Between the flight-detail grid and the departure/terminal stub, .bps-perf draws a dashed horizontal rule with two circular cutouts positioned at each end via ::before/::after, matching the screen's background color to fake a punched perforation. This single CSS trick is what makes the card read instantly as "ticket" rather than "generic card" — a visual convention borrowed directly from paper boarding passes and still expected in their digital equivalents.
Barcode as pure CSS, not an image
The back face's .bps-barcode element is a single div with a repeating-linear-gradient background alternating white bars of different widths against the dark back-face color — no image request, no canvas, no barcode-generation library. It is a visual stand-in; a production build should render an actual scannable PDF417 or Aztec barcode (via a library like bwip-js) encoded with the real ticket data, since gate scanners need a decodable symbol, not a decorative stripe pattern.
Add to Wallet with a realistic delay
Clicking "Add to Apple Wallet" disables the button, shows an "Adding…" label, and after a brief simulated delay replaces the button with a green confirmation — modeling the real (usually sub-second but non-instant) round trip to generate and register a .pkpass file with the OS wallet app, rather than snapping to success instantly in a way that would feel unconvincing.
A gate change that pushes itself
Four seconds after the screen loads, the gate value updates from C12 to C18 and briefly flashes red before settling back to normal text color — a small simulation of the real-time gate-change push notifications airline apps send, and a reminder that a boarding pass screen is not static: it needs a live-update path from day one, not bolted on later. Wire this to a real WebSocket or polling connection to your flight-ops API in production.
Step by step
How to Use
- 1Paste HTML, CSS, and JSA boarding pass card renders inside a phone frame, front face showing.
- 2Tap "view barcode"The card performs a real 3D flip to reveal the scannable barcode back face.
- 3Flip backTap the back-face button to flip the card back to the ticket details.
- 4Add to WalletClick the wallet button to see the disabled/adding/added state sequence.
- 5Watch the gate updateAfter a few seconds the gate value changes and briefly flashes red, simulating a live push.
- 6Swap in real data and a real barcodeReplace the CSS-stripe placeholder with a generated PDF417/Aztec barcode encoding actual ticket data.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
Both faces sit absolutely positioned inside a container with perspective set, each with backface-visibility: hidden. The front starts at rotateY(0) and the back at rotateY(180deg), so the back is already physically behind the front before any interaction. Adding a flipped class to the card rotates the front to -180deg and the back to 0deg at the same time, both eased with the same CSS transition, producing a genuine 3D flip.
No — it is a decorative CSS repeating-linear-gradient standing in for a real barcode. Production use requires generating an actual scannable symbol (commonly PDF417 or Aztec for boarding passes) encoding the real ticket data, typically with a library such as bwip-js, and rendering that as an image or canvas element in place of the CSS placeholder.
The .bps-perf element draws a dashed horizontal line with two circular divs positioned at its ends, colored to match the surrounding background so they read as punched-out holes. This single visual trick is what makes the layout instantly recognizable as a ticket rather than a generic info card.
No — it simulates the interaction sequence (disabled state, "Adding…" label, then a success message) with a setTimeout delay. A real implementation generates a signed .pkpass file server-side and either offers it as a download that iOS/Android wallet apps intercept, or uses a platform-specific wallet SDK to register the pass directly.
A setTimeout fires four seconds after the screen loads, changes the gate text content from C12 to C18, and briefly sets its color to red before reverting to the default color after 1.5 seconds. In production, replace this fixed timeout with a real WebSocket or polling subscription to your flight-operations data source.
Keep a boolean flipped state that toggles a CSS class or conditional class list on the card, and a separate walletState (idle/adding/added) that drives the wallet button\u2019s label and disabled attribute. Drive the gate value from real-time flight data (a subscription or polling hook) rather than a fixed timeout once connected to a real API.