From ad27ec9e21898f7a9a057ac89bd9399dd868de02 Mon Sep 17 00:00:00 2001 From: John Dunning Date: Wed, 27 May 2026 17:11:16 -0700 Subject: [PATCH] Improve QR generator UX - Block HTTP URLs: only https:// is now accepted; error message updated accordingly - Hide preview on input change: typing in the URL field immediately clears any existing QR code so users don't mistake a stale code for the current URL - Size buttons re-render immediately: after a code has been generated, clicking a different size regenerates the QR code at that size without requiring a second click of Generate - Enter key support: pressing Enter in the URL field triggers generation when the URL is valid - Keyboard focus ring on size buttons: uses :focus-visible on the label via has() so the blue outline appears during keyboard navigation but not on mouse click --- index.html | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/index.html b/index.html index de0a611..00ed4dc 100644 --- a/index.html +++ b/index.html @@ -84,6 +84,7 @@ body { font-family: 'Roboto Flex', sans-serif; background: #fcfcfc; color: #0b0b0b; } .outline-focus { outline: 3px solid #2A60AF; outline-offset: 4px; } input:focus { outline: 3px solid #2A60AF; outline-offset: 4px; } + label:has(input[type="radio"]:focus-visible) { outline: 3px solid #2A60AF; outline-offset: 4px; } @@ -169,6 +170,8 @@

Preview

label.addEventListener('click', () => { selectedSize = opt.value; updateSizeStyles(); + // Re-render immediately if a code has already been generated + if (currentDataUrl) generateQrCode(); }); sizeContainer.appendChild(label); }); @@ -188,11 +191,11 @@

Preview

}); } - // URL validation + // URL validation — https only function isValidUrl(input) { try { const url = new URL(input); - return url.protocol === 'https:' || url.protocol === 'http:'; + return url.protocol === 'https:'; } catch { return false; } @@ -214,11 +217,24 @@

Preview

document.getElementById('qr-url').removeAttribute('aria-describedby'); } - // Clear error on input - document.getElementById('qr-url').addEventListener('input', clearError); + // Clear error and hide preview on input change + document.getElementById('qr-url').addEventListener('input', () => { + clearError(); + currentDataUrl = null; + document.getElementById('preview-section').classList.add('hidden'); + }); + + // Submit on Enter if URL is valid + document.getElementById('qr-url').addEventListener('keydown', (e) => { + if (e.key === 'Enter' && isValidUrl(e.target.value.trim())) { + generateQrCode(); + } + }); // Generate - document.getElementById('generate-btn').addEventListener('click', async () => { + document.getElementById('generate-btn').addEventListener('click', () => generateQrCode()); + + async function generateQrCode() { const urlInput = document.getElementById('qr-url'); const trimmedUrl = urlInput.value.trim(); @@ -227,7 +243,7 @@

Preview

return; } if (!isValidUrl(trimmedUrl)) { - showError('Enter a valid URL starting with https:// or http://.'); + showError('Enter a valid URL starting with https://.'); return; } @@ -294,7 +310,7 @@

Preview

btn.textContent = 'Generate QR code'; btn.disabled = false; } - }); + } // Download document.getElementById('download-btn').addEventListener('click', () => {