|
|
4 months ago | |
|---|---|---|
| .. | ||
| README.md | 4 months ago | |
| context.ts | 4 months ago | |
| index.ts | 4 months ago | |
| qr-code-download-trigger.svelte | 4 months ago | |
| qr-code.css | 4 months ago | |
| qr-code.svelte | 4 months ago | |
| types.ts | 4 months ago | |
README.md
QrCode
Themeable, accessible QR code — own zero-dependency encoder, framework 4-layer rigor, richer than the references it learns from.
Design proposal (approved 2026-06-12). This README is both the design record and the component doc.
Why a new one (vs the references)
| Capability | ark-ui (zag + uqr) |
qr-code-styling | react-qr-code | QrCode (ours) |
|---|---|---|---|---|
| Parts / anatomy | ✅ Root·Frame·Pattern·Overlay·Download | ❌ imperative | ❌ single comp | ✅ |
| Encoder | uqr dep |
qr-code-generator dep | qr.js dep | own, zero-dep ($libs/qr) |
| Error correction L/M/Q/H | ✅ | ✅ | ✅ | ✅ |
| Themeable colour (tokens) | ❌ currentColor only |
✅ free | fg/bg props | ✅ via tokens + color |
| Cell shapes (square/rounded/dots) | ❌ | ✅ | ❌ | ✅ |
| Logo overlay | ✅ | ✅ | partial | ✅ + auto quiet-zone + auto-boost ECC |
| Download PNG/SVG | ✅ | ✅ | manual | ✅ DownloadTrigger = <Button> |
role=img + label a11y |
partial | ❌ | ❌ | ✅ |
| Framework morfo/recipe rigor | n/a | n/a | n/a | ✅ |
Takeaway: ark has the best structure but is visually flat and pulls an encoder dependency; qr-code-styling is visually rich but imperative, dependency- heavy, no a11y. Ours keeps ark's clean anatomy + API, owns the encoder (the QR algorithm is the open ISO/IEC 18004 standard — no npm dep), and adds themeable colour, cell shapes, a scannable logo overlay and real a11y.
Encoder — $libs/qr (own, zero-dep)
encode(value, { errorCorrection, minVersion?, maxVersion? }) → { matrix: boolean[][], reserved: boolean[][], size, version, errorCorrection }. A
from-scratch QR encoder (segment analysis → Reed-Solomon ECC over GF(256) →
matrix layout → mask selection over all 8 masks by penalty), pure and
zero-dependency, living in $libs alongside the other pure helpers. The
algorithm follows the public QR standard; uqr/nayuki are correctness
references only, never imported. Unit-tested against known vectors + structural
invariants; the demo harness round-trips encode→decode through jsQR
(versions 1–24, all ECC, all modes, all four cell shapes).
reserved[y][x] marks the function modules (finders, timing, alignment,
format/version info) — the eidos layer keeps those square even when
cellShape is rounded/dots, so the scanner's grid-lock pattern stays
intact and every shape remains scannable. Only the data modules take the
decorative shape.
Scannability (verified)
- Function patterns stay square for all cell shapes (
reservedmask) — whatqr-code-stylingdoes, and the reason rounded/dots still decode. - Logo auto-boosts ECC to
Hand clears a central ~24% region so the lost modules fall within the error-correction budget. - Fixed-tone default colours (dark-on-light) — a QR can't invert in dark
mode and stay scannable, so the default fg/bg are theme-independent; theming
is opt-in via
color/background(you own the contrast). - Quiet zone is in modules, not pixels —
marginmeasures the quiet zone in QR modules (spec minimum 4 — ISO/IEC 18004), the canonical unit: it scales with the symbol so the clear ring stays proportional at any rendered size, which is what guarantees scannability. There is deliberately no pixel margin prop. For fixed pixel spacing in a layout, pad the container in CSS using the QR'sbackgroundcolour — the symbol's quiet zone stays canonical in modules. - Export bakes resolved colours — a serialized SVG loses the document's CSS
custom properties, so
DownloadTriggerresolvesgetComputedStylefills into the clone before writing PNG/SVG. - Logo in exports — SVG export is lossless (the logo is XML and travels with
it). PNG export omits the logo: browsers do not rasterize
<foreignObject>content when an SVG is drawn to a canvas via<img>(a Chromium limitation, not a taint — the QR itself rasterizes fine). The cleared centre region stays, but the overlay is dropped. Use SVG to keep the logo. (A future PNG path could composite a raster logo directly onto the canvas; deferred — thelogoslot is an arbitrary snippet, not a URL.)
Anatomy (parts)
<QrCode value="https://…"> ← Frame (svg, role="img", aria-label)
└ Pattern ← the <path> matrix (decorative)
└ Overlay ← centred logo slot (optional)
<QrCode.DownloadTrigger> ← <Button> → PNG / SVG (optional)
morfo scope: ['eidos'] (+ optional sema for a commit-download tap cue).
No soma — a QR is a pure display (like Avatar / Image): value → matrix in
a $derived, rendered to SVG. Download is imperative via $adom.
API (root)
| Prop | Type | Default | |
|---|---|---|---|
value |
string |
'' |
data to encode (bindable) |
size |
ResponsiveProp<number | Size> |
'md' |
rendered box size |
errorCorrection |
'L' | 'M' | 'Q' | 'H' |
'M' |
redundancy (auto-boosted to H when a logo is present) |
color |
AffirmativeColorRole | string |
currentColor/token |
module colour |
background |
string |
token / transparent | quiet-zone + module background |
cellShape |
'square' | 'rounded' | 'dots' |
'square' |
module shape |
margin |
number |
4 |
quiet zone in modules, not pixels (spec min 4 — see Scannability) |
logo |
Snippet |
— | centred overlay; clears the centre + boosts ECC to H |
alt |
string |
value |
accessible name for the Frame |
DownloadTrigger props: format: 'png' \| 'svg' (default 'png'), filename
(default 'qrcode') + all <Button> props (variant, size, color, …).
It renders the framework <Button> and calls the root's colour-baking export
via context.
Compound: <QrCode> + .DownloadTrigger. The logo is the logo snippet prop
(not a subcomponent); the morfo's Overlay/Pattern parts are render internals.
v1 scope (approved)
square/rounded/dots cell shapes · themeable colour · logo Overlay with auto
quiet-zone + auto-ECC-boost · download PNG + SVG · role=img + label.
Deferred to v2: gradient fills, per-corner (finder) styling — the marketing-grade flourishes of qr-code-styling.
Layers
$libs/qr— own encoder + tests.- morfo
qr-code.ts— Frame / Pattern / Overlay / DownloadTrigger contract. - eidos
qr-code/— wrapper + recipe (--qr-code-*tokens) + SVG render from the matrix. - demo
web/routes/uix/components/qr-code/+ nav entry.