Barcode Scanner
Camera-based barcode/QR scanner with viewfinder frame, animated scan line, controls, manual input fallback, and loading/error states.
Basic Scanner
Preview
Camera Feed
Point camera at barcode
<div class="barcode-scanner" style="max-width: 480px;">
<!-- Camera feed -->
<div class="barcode-scanner-video" style="position: absolute; inset: 0; background: #1a1a2e; display: flex; align-items: center; justify-content: center;">
<span style="color: rgba(255,255,255,0.2);">Camera Feed</span>
</div>
<!-- Viewfinder overlay -->
<div class="barcode-scanner-overlay">
<div class="barcode-scanner-frame">
<div class="barcode-scanner-corners"></div>
<div class="barcode-scanner-scan-line"></div>
<p class="barcode-scanner-instructions">Point camera at barcode</p>
</div>
</div>
</div> Requires: ux.min.css
<div class="relative overflow-hidden bg-neutral rounded-box" style="max-width: 480px; aspect-ratio: 4/3;">
<div class="absolute inset-0 flex items-center justify-center" style="background: #1a1a2e;">
<span class="text-white/20">Camera Feed</span>
</div>
<div class="absolute inset-0 flex flex-col items-center justify-center pointer-events-none">
<div class="relative" style="width: 70%; max-width: 300px; aspect-ratio: 1.5; border: 2px solid rgba(255,255,255,0.5); border-radius: 0.5rem; box-shadow: 0 0 0 9999px rgba(0,0,0,0.5);">
<!-- Corner accents via inline borders -->
<div class="absolute -top-0.5 -left-0.5 w-5 h-5 border-t-3 border-l-3 border-primary rounded-tl-lg"></div>
<div class="absolute -top-0.5 -right-0.5 w-5 h-5 border-t-3 border-r-3 border-primary rounded-tr-lg"></div>
<div class="absolute -bottom-0.5 -left-0.5 w-5 h-5 border-b-3 border-l-3 border-primary rounded-bl-lg"></div>
<div class="absolute -bottom-0.5 -right-0.5 w-5 h-5 border-b-3 border-r-3 border-primary rounded-br-lg"></div>
<p class="absolute -bottom-10 left-1/2 -translate-x-1/2 text-sm text-white/80 whitespace-nowrap">Point camera at barcode</p>
</div>
</div>
</div> Requires: tw.min.css
// Use a barcode detection library (e.g. QuaggaJS, ZXing):
// const video = document.querySelector('video');
// navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' } })
// .then(stream => { video.srcObject = stream; });
// Quagga.init({ inputStream: { type: 'LiveStream', target: video } });
// Quagga.onDetected(result => {
// console.log('Barcode:', result.codeResult.code);
// }); With Controls
Preview
Camera Feed
<div class="barcode-scanner" style="max-width: 480px;">
<div class="barcode-scanner-video" style="position: absolute; inset: 0; background: #1a1a2e; display: flex; align-items: center; justify-content: center;">
<span style="color: rgba(255,255,255,0.2);">Camera Feed</span>
</div>
<div class="barcode-scanner-overlay">
<div class="barcode-scanner-frame">
<div class="barcode-scanner-corners"></div>
<div class="barcode-scanner-scan-line"></div>
</div>
</div>
<!-- Controls -->
<div class="barcode-scanner-controls">
<button class="barcode-scanner-btn" aria-label="Toggle flash">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>
</button>
<button class="barcode-scanner-btn" aria-label="Switch camera">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M11 19H4a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h5"/><path d="M13 5h7a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-5"/><polyline points="16 3 19 1 22 3"/><polyline points="8 21 5 23 2 21"/></svg>
</button>
</div>
</div> Requires: ux.min.css
<div class="relative overflow-hidden bg-neutral rounded-box" style="max-width: 480px; aspect-ratio: 4/3;">
<div class="absolute inset-0 flex items-center justify-center" style="background: #1a1a2e;">
<span class="text-white/20">Camera Feed</span>
</div>
<div class="absolute inset-0 flex flex-col items-center justify-center pointer-events-none">
<div class="relative" style="width: 70%; max-width: 300px; aspect-ratio: 1.5; border: 2px solid rgba(255,255,255,0.5); border-radius: 0.5rem; box-shadow: 0 0 0 9999px rgba(0,0,0,0.5);">
<div class="absolute -top-0.5 -left-0.5 w-5 h-5 border-t-3 border-l-3 border-primary rounded-tl-lg"></div>
<div class="absolute -top-0.5 -right-0.5 w-5 h-5 border-t-3 border-r-3 border-primary rounded-tr-lg"></div>
<div class="absolute -bottom-0.5 -left-0.5 w-5 h-5 border-b-3 border-l-3 border-primary rounded-bl-lg"></div>
<div class="absolute -bottom-0.5 -right-0.5 w-5 h-5 border-b-3 border-r-3 border-primary rounded-br-lg"></div>
</div>
</div>
<div class="absolute top-4 right-4 flex gap-2 pointer-events-auto">
<button class="flex items-center justify-center w-10 h-10 border-none rounded-full text-white cursor-pointer" style="background: rgba(0,0,0,0.5);" aria-label="Toggle flash">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>
</button>
<button class="flex items-center justify-center w-10 h-10 border-none rounded-full text-white cursor-pointer" style="background: rgba(0,0,0,0.5);" aria-label="Switch camera">
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M11 19H4a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h5"/><path d="M13 5h7a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-5"/><polyline points="16 3 19 1 22 3"/><polyline points="8 21 5 23 2 21"/></svg>
</button>
</div>
</div> Requires: tw.min.css
// Toggle flash
// flashBtn.onclick = () => {
// const track = stream.getVideoTracks()[0];
// const caps = track.getCapabilities();
// if (caps.torch) track.applyConstraints({ advanced: [{ torch: !torchOn }] });
// }; Scan Result
Preview
Camera Feed
EAN-13
5901234123457
<div class="barcode-scanner" style="max-width: 480px;">
<div class="barcode-scanner-video" style="position: absolute; inset: 0; background: #1a1a2e; display: flex; align-items: center; justify-content: center;">
<span style="color: rgba(255,255,255,0.2);">Camera Feed</span>
</div>
<div class="barcode-scanner-overlay">
<div class="barcode-scanner-frame" style="border-color: rgba(74,222,128,0.6);">
<div class="barcode-scanner-corners"></div>
</div>
</div>
<div class="barcode-scanner-result" style="animation: none;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>
<span class="barcode-scanner-format">EAN-13</span>
5901234123457
</div>
</div> Requires: ux.min.css
<div class="relative overflow-hidden bg-neutral rounded-box" style="max-width: 480px; aspect-ratio: 4/3;">
<div class="absolute inset-0 flex items-center justify-center" style="background: #1a1a2e;">
<span class="text-white/20">Camera Feed</span>
</div>
<div class="absolute inset-0 flex flex-col items-center justify-center pointer-events-none">
<div class="relative" style="width: 70%; max-width: 300px; aspect-ratio: 1.5; border: 2px solid rgba(74,222,128,0.6); border-radius: 0.5rem; box-shadow: 0 0 0 9999px rgba(0,0,0,0.5);">
<div class="absolute -top-0.5 -left-0.5 w-5 h-5 border-t-3 border-l-3 border-primary rounded-tl-lg"></div>
<div class="absolute -top-0.5 -right-0.5 w-5 h-5 border-t-3 border-r-3 border-primary rounded-tr-lg"></div>
<div class="absolute -bottom-0.5 -left-0.5 w-5 h-5 border-b-3 border-l-3 border-primary rounded-bl-lg"></div>
<div class="absolute -bottom-0.5 -right-0.5 w-5 h-5 border-b-3 border-r-3 border-primary rounded-br-lg"></div>
</div>
</div>
<div class="absolute bottom-6 left-1/2 -translate-x-1/2 flex items-center gap-1 text-sm font-medium bg-success text-white rounded-full px-4 py-2">
<svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>
<span class="inline-block text-xs font-medium px-1.5 py-0.5 rounded bg-white/20 mr-1">EAN-13</span>
5901234123457
</div>
</div> Requires: tw.min.css
// Handle detected result
// Quagga.onDetected(result => {
// const code = result.codeResult.code;
// const format = result.codeResult.format;
// resultEl.textContent = code;
// formatEl.textContent = format;
// resultContainer.style.display = 'flex';
// }); Manual Input Fallback
Preview
Camera Feed
<div class="barcode-scanner" style="max-width: 480px;">
<div class="barcode-scanner-video" style="position: absolute; inset: 0; background: #1a1a2e; display: flex; align-items: center; justify-content: center;">
<span style="color: rgba(255,255,255,0.2);">Camera Feed</span>
</div>
<div class="barcode-scanner-overlay">
<div class="barcode-scanner-frame">
<div class="barcode-scanner-corners"></div>
<div class="barcode-scanner-scan-line"></div>
</div>
</div>
<div class="barcode-scanner-manual">
<input class="barcode-scanner-manual-input" type="text" placeholder="Enter code manually...">
<button class="barcode-scanner-manual-submit">Submit</button>
</div>
</div> Requires: ux.min.css
<div class="relative overflow-hidden bg-neutral rounded-box" style="max-width: 480px; aspect-ratio: 4/3;">
<div class="absolute inset-0 flex items-center justify-center" style="background: #1a1a2e;">
<span class="text-white/20">Camera Feed</span>
</div>
<div class="absolute inset-0 flex flex-col items-center justify-center pointer-events-none">
<div class="relative" style="width: 70%; max-width: 300px; aspect-ratio: 1.5; border: 2px solid rgba(255,255,255,0.5); border-radius: 0.5rem; box-shadow: 0 0 0 9999px rgba(0,0,0,0.5);">
<div class="absolute -top-0.5 -left-0.5 w-5 h-5 border-t-3 border-l-3 border-primary rounded-tl-lg"></div>
<div class="absolute -top-0.5 -right-0.5 w-5 h-5 border-t-3 border-r-3 border-primary rounded-tr-lg"></div>
<div class="absolute -bottom-0.5 -left-0.5 w-5 h-5 border-b-3 border-l-3 border-primary rounded-bl-lg"></div>
<div class="absolute -bottom-0.5 -right-0.5 w-5 h-5 border-b-3 border-r-3 border-primary rounded-br-lg"></div>
</div>
</div>
<div class="absolute bottom-6 left-1/2 -translate-x-1/2 flex gap-2 pointer-events-auto">
<input class="h-10 px-4 border-none rounded-full text-base" style="width: 200px; background: rgba(255,255,255,0.9); color: #111;" type="text" placeholder="Enter code manually...">
<button class="h-10 px-4 border-none rounded-full bg-primary text-white font-medium cursor-pointer hover:opacity-90">Submit</button>
</div>
</div> Requires: tw.min.css
// Manual input fallback
// submitBtn.onclick = () => {
// const code = manualInput.value.trim();
// if (code) onBarcodeDetected({ code, format: 'manual' });
// }; Loading State
Preview
Requesting camera access...
<div class="barcode-scanner" style="max-width: 480px;">
<div class="barcode-scanner-loading">
<div class="barcode-scanner-loading-spinner"></div>
<p class="barcode-scanner-loading-text">Requesting camera access...</p>
</div>
</div> Requires: ux.min.css
<div class="relative overflow-hidden bg-neutral rounded-box" style="max-width: 480px; aspect-ratio: 4/3;">
<div class="absolute inset-0 flex flex-col items-center justify-center gap-4" style="background: #111;">
<div class="w-10 h-10 rounded-full" style="border: 3px solid rgba(255,255,255,0.2); border-top-color: var(--color-primary); animation: spin 0.8s linear infinite;"></div>
<p class="text-sm text-white/70">Requesting camera access...</p>
</div>
</div> Requires: tw.min.css
// Show loading while camera initializes
// loadingEl.style.display = 'flex';
// navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' } })
// .then(stream => { video.srcObject = stream; loadingEl.style.display = 'none'; })
// .catch(err => showError(err.message)); Error State
Preview
Camera access denied
Please allow camera permissions in your browser settings
<div class="barcode-scanner" style="max-width: 480px;">
<div class="barcode-scanner-error">
<svg class="barcode-scanner-error-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
<p class="barcode-scanner-error-text">Camera access denied</p>
<p class="barcode-scanner-error-hint">Please allow camera permissions in your browser settings</p>
</div>
</div> Requires: ux.min.css
<div class="relative overflow-hidden bg-neutral rounded-box" style="max-width: 480px; aspect-ratio: 4/3;">
<div class="absolute inset-0 flex flex-col items-center justify-center gap-4 text-center p-8" style="background: #111;">
<svg class="w-12 h-12 text-error" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
<p class="text-base text-white">Camera access denied</p>
<p class="text-sm text-white/60">Please allow camera permissions in your browser settings</p>
</div>
</div> Requires: tw.min.css
// Handle camera errors
// .catch(err => {
// errorText.textContent = err.name === 'NotAllowedError'
// ? 'Camera access denied'
// : 'Camera not available';
// errorEl.style.display = 'flex';
// }); Scan History
Preview
Camera Feed
QR https://example.com
EAN 5901234123457
CODE128 ABC-12345
<div class="barcode-scanner" style="max-width: 480px;">
<div class="barcode-scanner-video" style="position: absolute; inset: 0; background: #1a1a2e; display: flex; align-items: center; justify-content: center;">
<span style="color: rgba(255,255,255,0.2);">Camera Feed</span>
</div>
<div class="barcode-scanner-overlay">
<div class="barcode-scanner-frame">
<div class="barcode-scanner-corners"></div>
<div class="barcode-scanner-scan-line"></div>
</div>
</div>
<div class="barcode-scanner-history">
<div class="barcode-scanner-history-item"><span class="barcode-scanner-format">QR</span> https://example.com</div>
<div class="barcode-scanner-history-item"><span class="barcode-scanner-format">EAN</span> 5901234123457</div>
<div class="barcode-scanner-history-item"><span class="barcode-scanner-format">CODE128</span> ABC-12345</div>
</div>
</div> Requires: ux.min.css
<div class="relative overflow-hidden bg-neutral rounded-box" style="max-width: 480px; aspect-ratio: 4/3;">
<div class="absolute inset-0 flex items-center justify-center" style="background: #1a1a2e;">
<span class="text-white/20">Camera Feed</span>
</div>
<div class="absolute inset-0 flex flex-col items-center justify-center pointer-events-none">
<div class="relative" style="width: 70%; max-width: 300px; aspect-ratio: 1.5; border: 2px solid rgba(255,255,255,0.5); border-radius: 0.5rem; box-shadow: 0 0 0 9999px rgba(0,0,0,0.5);">
<div class="absolute -top-0.5 -left-0.5 w-5 h-5 border-t-3 border-l-3 border-primary rounded-tl-lg"></div>
<div class="absolute -top-0.5 -right-0.5 w-5 h-5 border-t-3 border-r-3 border-primary rounded-tr-lg"></div>
<div class="absolute -bottom-0.5 -left-0.5 w-5 h-5 border-b-3 border-l-3 border-primary rounded-bl-lg"></div>
<div class="absolute -bottom-0.5 -right-0.5 w-5 h-5 border-b-3 border-r-3 border-primary rounded-br-lg"></div>
</div>
</div>
<div class="absolute bottom-4 left-4 pointer-events-auto" style="max-width: 200px;">
<div class="text-xs text-white/80 truncate rounded-lg mb-1 px-2 py-1" style="background: rgba(0,0,0,0.5);"><span class="inline-block text-xs font-medium px-1 rounded bg-primary/20 text-primary mr-1">QR</span> https://example.com</div>
<div class="text-xs text-white/80 truncate rounded-lg mb-1 px-2 py-1" style="background: rgba(0,0,0,0.5);"><span class="inline-block text-xs font-medium px-1 rounded bg-primary/20 text-primary mr-1">EAN</span> 5901234123457</div>
<div class="text-xs text-white/80 truncate rounded-lg px-2 py-1" style="background: rgba(0,0,0,0.5);"><span class="inline-block text-xs font-medium px-1 rounded bg-primary/20 text-primary mr-1">CODE128</span> ABC-12345</div>
</div>
</div> Requires: tw.min.css
// Track scan history
// const history = [];
// function onDetected(result) {
// history.unshift({ code: result.code, format: result.format, time: Date.now() });
// if (history.length > 5) history.pop();
// renderHistory(history);
// }