You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/src/uix/eidos/components/qr-code
dev e522e04e65
docs(theming): los 21 de la mañana recuperan su README y su pestaña Tokens
2 months ago
..
README.md docs(theming): los 21 de la mañana recuperan su README y su pestaña Tokens 2 months ago
context.ts feat(eidos): add QrCode — themeable QR with own zero-dep encoder 4 months ago
index.ts feat(eidos): add QrCode — themeable QR with own zero-dep encoder 4 months ago
qr-code-download-trigger.svelte feat(eidos): add QrCode — themeable QR with own zero-dep encoder 4 months ago
qr-code.css uix(theming): cola pequeña — seis componentes, y una receta que TAPABA sus claves 2 months ago
qr-code.svelte fix(eidos): QrCode size tokens use literal var names (recipe contract) 4 months ago
types.ts feat(eidos): add QrCode — themeable QR with own zero-dep encoder 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.

Baseline

No Air baseline — new component (2026-06-12). Nothing in the legacy layers rendered QR codes; the encoder itself is also new ($libs/qr, zero-dep).

Talla y tema

10 clave(s) pública(s) en lib/recipes/base.ts (bloque qr-code), con sus pasos por talla y el nombre RESUELTO que consume la receta. Es el contrato vivo: la pestaña Tokens de su demo lista estas mismas claves y las resuelve sobre el escenario.

Token (--qr-code-…) Valor por defecto
fg #18181b
bg #ffffff
size-xs 96px
size-sm 128px
size-md 160px
size-lg 200px
size-xl 256px
radius var(--radius-md)
overlay-bg #ffffff
overlay-radius var(--radius-sm)

Lo que el guard R-5.4 da por silencioso, con su razón medida (scripts/theming-sentinel-exceptions.ts):

  • size-xs — one step per instance: the wrapper writes var(--qr-code-size-{k}) inline from the size prop and the demo runs lg. Forced -> reaches (96px -> 123px)
  • size-sm — same, one step per instance
  • size-md — same, one step per instance
  • size-xl — same, one step per instance
  • fg — painted as the SVG fill of the module path, not as color on the root; read on the path -> reaches (rgb(24,24,27) -> rgb(1,2,3))

Comparativa — 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 (reserved mask) — what qr-code-styling does, and the reason rounded/dots still decode.
  • Logo auto-boosts ECC to H and 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 — margin measures 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's background colour — the symbol's quiet zone stays canonical in modules.
  • Export bakes resolved colours — a serialized SVG loses the document's CSS custom properties, so DownloadTrigger resolves getComputedStyle fills 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 — the logo slot 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.

Decisiones

Each argued in place above; the compact register:

  • Own zero-dep encoder ($libs/qr) — the QR algorithm is the open ISO/IEC 18004 standard; uqr/nayuki are correctness references only.
  • Function modules stay square whatever cellShape (the reserved mask) — scannability over decoration (see Scannability).
  • Fixed-tone default colours — a QR can't invert in dark mode and stay scannable; theming is opt-in via color/background.
  • margin in modules, not pixels (spec min 4) — the quiet zone scales with the symbol; pixel spacing belongs to the container's CSS.
  • Exports bake resolved colours (getComputedStyle into the clone) — a serialized SVG loses the document's custom properties.
  • No soma — pure display (like Avatar/Image): value → matrix in a $derived; download is imperative via $adom.

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.

Gaps

  • Gradient fills + per-corner (finder) styling — the marketing-grade flourishes of qr-code-styling — disposition: diferir (v2).
  • PNG export drops the logo (<foreignObject> isn't rasterized when SVG draws to canvas; see Scannability) — disposition: diferir (a raster-logo composite path); SVG export is lossless.
  • Demo page pending the v2 template migration — disposition: implementar (demo/docs web phase).

Passive justification

Zero events by design: a QR is pure display — value in, SVG out; no interaction surface of its own. The one interactive part, DownloadTrigger, IS a composed <Button> (its contact firma and states live there). The morfo may later add an optional commit-download cue (scope would gain sema), which is the recorded exception path.

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.

Audit exceptions

  • R-1.5 exception: the only interactive part (DownloadTrigger) composes the canonical <Button> — the focus ring lives in button.css; Pattern and Overlay are display indicators.

Powered by TurnKey Linux.