Make a code
Type anything, put your logo in the middle, and watch it tell you when the logo is too big to survive.
<div class="demo demo--wide">
<div class="demo-split">
<div class="demo-controls">
<label class="demo-label" for="value">What it carries</label>
<input class="demo-input" id="value" value="https://devix.pk/resources/qr-code">
<div class="demo-row">
<label class="demo-label" for="level">Correction</label>
<div class="demo-seg" id="level" role="group" aria-label="Error correction">
<button type="button" data-level="L">L</button>
<button type="button" data-level="M" aria-pressed="true">M</button>
<button type="button" data-level="Q">Q</button>
<button type="button" data-level="H">H</button>
</div>
</div>
<div class="demo-row">
<label class="demo-label" for="logo">Logo</label>
<input id="logo" type="range" min="0" max="0.3" step="0.01" value="0">
<output id="logoOut">off</output>
</div>
<div class="demo-row">
<div class="demo-seg" id="shape" role="group" aria-label="Module shape">
<button type="button" data-shape="square" aria-pressed="true">Square</button>
<button type="button" data-shape="rounded">Rounded</button>
<button type="button" data-shape="dot">Dots</button>
</div>
<button class="demo-btn" id="png">Download PNG</button>
</div>
<dl class="demo-kv" id="facts"></dl>
</div>
<div id="code"></div>
</div>
</div>
import { createQrCode } from 'https://devix.pk/cdn/oss/qr-code@1.0.0/index.js';
// Drawn here so the demo needs nothing from the network.
const LOGO = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<rect width="100" height="100" rx="22" fill="#0ea5e9"/>
<path d="M28 70V30h18a20 20 0 0 1 0 40H28Zm14-10h4a10 10 0 0 0 0-20h-4v20Z" fill="#fff"/>
</svg>`;
const value = document.getElementById('value');
const logo = document.getElementById('logo');
const logoOut = document.getElementById('logoOut');
const facts = document.getElementById('facts');
let level = 'M';
let shape = 'square';
const qr = createQrCode(document.getElementById('code'), {
value: value.value,
level,
onRender: (code) => {
facts.innerHTML = `
<div><dt>version</dt><dd>${code.version} · ${code.size} × ${code.size}</dd></div>
<div><dt>mode</dt><dd>${code.mode}</dd></div>
<div><dt>correction</dt><dd>${code.level}${code.level !== level ? ' (raised for the logo)' : ''}</dd></div>
<div><dt>mask</dt><dd>${code.mask}</dd></div>`;
},
});
const apply = () => {
const size = Number(logo.value);
logoOut.textContent = size ? `${Math.round(size * 100)}% of the width` : 'off';
qr.setOptions({
value: value.value || ' ',
level,
shape,
logo: size ? { src: LOGO, size } : undefined,
});
};
value.addEventListener('input', apply);
logo.addEventListener('input', apply);
for (const [id, set] of [['level', (v) => (level = v)], ['shape', (v) => (shape = v)]]) {
document.getElementById(id).addEventListener('click', (event) => {
const button = event.target.closest('button');
if (!button) return;
for (const other of event.currentTarget.querySelectorAll('button')) other.setAttribute('aria-pressed', String(other === button));
set(button.dataset.level ?? button.dataset.shape);
apply();
});
}
document.getElementById('png').addEventListener('click', () => qr.download('devix-qr.png'));
apply();
@import url('https://devix.pk/cdn/oss/qr-code@1.0.0/qr-code.min.css');
.demo-split { display: flex; flex-wrap: wrap; gap: 24px; align-items: flex-start; }
.demo-controls { flex: 1 1 260px; display: grid; gap: 12px; align-content: start; }
.demo-label { display: block; margin-bottom: 6px; }
.demo-row { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.demo-row input[type='range'] { flex: 1; min-width: 100px; }
#logoOut { font: 11.5px var(--demo-mono); color: var(--demo-muted); }
.dxq { flex: 0 0 auto; --dxq-max-width: 220px; }