Source Code

<div class="qrs-phone">
  <div class="qrs-screen">
    <div class="qrs-status"><span>9:41</span><span class="qrs-batt"><i></i></span></div>

    <header class="qrs-head">
      <button class="qrs-close" id="qrsClose" aria-label="Close">&times;</button>
      <h1>Scan QR Code</h1>
      <button class="qrs-flash" id="qrsFlash" aria-label="Toggle flashlight">
        <svg width="17" height="17" viewBox="0 0 24 24" fill="currentColor"><path d="M11 21h-1l1-7H7.82a1 1 0 01-.99-1.14l1.38-9A1 1 0 019.2 3h6.38a1 1 0 01.99 1.14L15.94 11H19a1 1 0 01.8 1.6l-8 10a1 1 0 01-.8-1.6z"/></svg>
      </button>
    </header>

    <div class="qrs-viewfinder" id="qrsViewfinder">
      <div class="qrs-camera-noise"></div>
      <div class="qrs-frame">
        <span class="qrs-corner qrs-tl"></span>
        <span class="qrs-corner qrs-tr"></span>
        <span class="qrs-corner qrs-bl"></span>
        <span class="qrs-corner qrs-br"></span>
        <div class="qrs-scanline" id="qrsScanline"></div>
      </div>
      <p class="qrs-hint" id="qrsHint">Align QR code within the frame</p>
    </div>

    <div class="qrs-sheet" id="qrsSheet" hidden>
      <div class="qrs-sheet-handle"></div>
      <div class="qrs-sheet-icon">&#128279;</div>
      <p class="qrs-sheet-label">Link detected</p>
      <p class="qrs-sheet-url" id="qrsUrl">https://northline.app/promo/summer24</p>
      <div class="qrs-sheet-actions">
        <button class="qrs-btn-primary" id="qrsOpen">Open Link</button>
        <button class="qrs-btn-secondary" id="qrsCopy">Copy</button>
      </div>
      <button class="qrs-scan-again" id="qrsAgain">Scan another code</button>
    </div>
  </div>
</div>

Mobile QR Code Scanner Screen — Free Snippet

Mobile QR Code Scanner Screen · Mobile · Plain HTML, CSS & JS · Live preview

What's included

Features

Bracket-corner viewfinder frame, the universal scan-target visual convention
Sweeping glow scanline animated with a top keyframe and box-shadow glow
Simulated code detection after a believable delay, with a distinct found state
Bottom sheet reveal with a rise-in animation once a code is detected
Working Open Link and Copy buttons with their own transitional label states
Flashlight toggle independent of the scan/found state machine
Scan-again control reuses the exact same start function as initial load
Phone-frame mockup with status bar, close button, and header title
Zero dependencies, vanilla JavaScript only

About this UI Snippet

Mobile QR Scanner Screen — Sweeping Viewfinder with a Working Result Sheet

Screenshot of the Mobile QR Code Scanner Screen snippet rendered live

A QR scanner screen has one job: make a camera viewfinder feel alive while it searches for a code, then hand off cleanly to a result once it finds one. Since this snippet runs without real camera access, it builds the entire perceived experience in CSS and JavaScript instead — a dark viewfinder with rounded bracket corners, a sweeping green laser line, a simulated detection after a believable delay, and a bottom sheet with real open and copy actions once a code is found.

A viewfinder built from brackets, not a photo

.qrs-frame is a plain square with four .qrs-corner elements, each drawn with only two visible border sides and a rounded outer corner, positioned at the four corners of the frame. This is the universal visual shorthand for "scan target here" used across every native camera-based scanner, and it reads correctly even with the plain gradient-noise background standing in for a live camera feed.

A scanline that actually sweeps

.qrs-scanline is a thin gradient-glow bar animated with a top keyframe from the top of the frame to the bottom and back, on a 2-second ease-in-out loop. Combined with a soft box-shadow glow, this single element does most of the work of making the viewfinder feel like it is actively searching rather than a static rectangle waiting for something to happen.

A simulated detection with a state change, not just a popup

startScanning() sets a timer that, after roughly 2.6 seconds, adds a .found class to the frame — turning the green corners and scanline yellow and pausing the sweep — before the result sheet rises from the bottom. This two-step handoff (found state, then sheet) mirrors how real scanners visually confirm a lock before presenting the decoded result, rather than jumping straight from "searching" to "here is your data" with no transition.

A bottom sheet with working actions, not a static card

Once revealed, #qrsSheet shows the decoded URL and two real buttons: "Open Link," which shows a brief "Opening..." transitional label, and "Copy," which flips to a "Copied" confirmation state for about a second and a half before reverting. Both model the actual two real actions a user takes after a successful scan — following the link or grabbing it for later — rather than presenting the decoded text as inert.

Flashlight toggle and a real reset path

The flashlight button toggles a visual on/off state (.qrs-flash.on) independent of the scanning logic, matching how a real scanner lets the user turn on the torch at any point without interrupting detection. "Scan another code" calls the exact same startScanning() function used on initial load, resetting the frame's found state, hiding the sheet, and restarting the detection timer — so the whole flow can be repeated indefinitely without a page reload.

Wiring it to a real camera

Replace the CSS gradient-noise background with a live <video> element bound to navigator.mediaDevices.getUserMedia, and swap the fixed setTimeout detection delay for a real decoding library (such as jsQR or the browser's native BarcodeDetector API) running against each video frame — the found-state class toggle and the result-sheet reveal logic need no changes to work with a real decoded payload in place of the demo URL.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Instead of tracing the scan-to-result sequencing by hand, paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how the found state pauses the scanline animation and how the timing between the found state and the sheet reveal was chosen to feel like a real detection lock rather than an instant jump. The same assistant can help you optimize it, for instance asking whether the fixed 2.6-second detection delay should instead be driven by a real decoding library running against actual camera frames. It is also useful for extending the screen: ask it to wire navigator.mediaDevices.getUserMedia and a real QR decoding library into the viewfinder, add haptic-style visual feedback on detection, or support scanning different payload types (Wi-Fi credentials, contact cards, plain text) with type-specific result sheets. 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 mobile "QR code scanner" screen in plain HTML, CSS, and JavaScript, framed inside a CSS phone mockup, with a simulated camera viewfinder and a working result sheet, no camera or decoding library required for the demo.

Requirements:
- A dark viewfinder area with a square scan-target frame made of four bracket-style corner marks (two visible border sides each, rounded on the outer corner) and a glowing horizontal scanline that continuously sweeps from the top of the frame to the bottom and back using a CSS keyframe animation.
- After a believable delay (a couple of seconds) following page load, the frame must visibly transition into a "found" state: the corner brackets and scanline change color and the sweep animation pauses, distinct from the ongoing searching state.
- Shortly after entering the found state, a bottom sheet must rise into view from the bottom of the screen showing a detected URL, with a rise-in transform animation rather than an instant appearance.
- The result sheet must include a working "Open Link" button that shows a brief transitional label change on click, a "Copy" button that shows a temporary "Copied" confirmation state before reverting, and a "Scan another code" control that resets the frame back to its searching state and restarts the same detection timer used on initial load.
- A separate flashlight toggle button in the header must switch its own visual on/off state independently of the scanning and found states.

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 phone-framed scanner screen renders with the green scanline sweeping inside the bracket corners.
  2. 2
    Wait a couple of secondsThe frame turns yellow, the scanline pauses, and a result sheet rises from the bottom.
  3. 3
    Tap "Open Link" or "Copy"Each button shows its own brief transitional state before settling.
  4. 4
    Tap "Scan another code"The sheet dismisses and the scanning animation restarts from the beginning.
  5. 5
    Tap the flashlight iconIt toggles a visual on state independently of the scan cycle.
  6. 6
    Wire it to a real cameraSwap the noise background for a live video element and the setTimeout for a real decoding library like jsQR or BarcodeDetector.

Real-world uses

Common Use Cases

Payment and check-in apps
Model the scan-to-result flow used by boarding-pass, ticket, and point-of-sale QR scanning features before wiring in a real decoding library.
Marketing and promo-code campaigns
Prototype a promo QR redemption screen end-to-end, including the open/copy result actions, without needing a live camera during design review.
Inventory and asset-tracking apps
Adapt the same viewfinder and result-sheet pattern for scanning barcodes or asset tags instead of URLs.
Teaching CSS scan-line and state-machine UI
A compact reference for building a believable scanning animation and a found/idle state toggle without any camera or decoding dependency.
Related: Mobile Boarding Pass Screen
See the Mobile Boarding Pass Screen for the scannable-barcode side of a similar scan-based interaction.
Related: Mobile OTP Verification Screen
See the Mobile OTP Verification Screen for a related mobile identity-confirmation pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

No — it is a fully simulated UI. The viewfinder background is a CSS gradient standing in for a camera feed, and detection is a fixed setTimeout delay rather than real image analysis. Wire it to navigator.mediaDevices.getUserMedia for a live video feed and a library like jsQR or the native BarcodeDetector API for real decoding.

The .qrs-scanline element is positioned with a top keyframe animation that moves it from the top of the .qrs-frame square to the bottom and back over a 2-second ease-in-out loop, combined with a soft box-shadow glow for visual weight.

startScanning() sets a timer that adds a found class to the frame after roughly 2.6 seconds, which turns the corner brackets and scanline yellow and pauses the sweep animation via animation-play-state, then reveals the result sheet shortly after.

In this demo, it only shows a visual Copied confirmation state for about 1.4 seconds. Wire it to the real navigator.clipboard.writeText API with the actual decoded value to make it functional.

The "Scan another code" button calls the same startScanning() function used on initial page load, which hides the result sheet, removes the found class from the frame, and restarts the detection timer from zero.

Yes. Track a status value (scanning, found, result-shown) in state, run your real decoding logic inside an effect bound to a video element, and set status to found the moment a real decode succeeds instead of after a fixed timeout.