Skip to content
Home Theme Gallery

QR code

The custom element is <hui-qr-code>.

Overview

Encodes its value as a QR code and draws it as one SVG path, dark modules on a white tile whatever the theme. A phone camera reads dark on light far more reliably than the reverse, so the code is deliberately not themed.

Example

A pairing code
Source
<hui-qr-code value="https://ledger.example.com/link/4f2a9c17e5" label="Scan to link your mobile device"></hui-qr-code>

The Go template that renders it:

<hui-qr-code value="{{ .PairingURL }}" label="Scan to link your mobile device"></hui-qr-code>

API

Attributes4
Attributes
NameTypeDefaultDescription
valuestring""What the code encodes: a URL, a pairing token. Reflected. A new value redraws the code, so an HTMX swap sending a fresh token is the whole update.
labelstring"QR code"The accessible name. The value itself is never announced - it is usually a secret or an opaque token - so put the instruction in text beside the code.
level"L" | "M" | "Q" | "H""M"Error correction. An unrecognised value falls back to M rather than failing to draw.
sizenumber160The drawn size of the code in pixels, excluding the tile's 16px padding.
Properties4
Properties
NameTypeDefaultDescription
valuestring''Reflects to the value attribute.
labelstring'QR code'Reflects to the label attribute.
levelQrLevel'M'Reflects to the level attribute, normalised.
sizenumber160Reflects to the size attribute.
::part() hooks2
::part() hooks
NameDescription
tileThe white tile: the border, the xl radius and the 16px padding that is the code's quiet zone. It carries role="img" and the label.
codeThe <svg> itself, aria-hidden, holding one path of unit squares.

States

Error correction and size

Higher correction survives more damage to the printed code at the cost of more modules, so the same value needs a denser grid. size is the drawn width of the code itself, not counting the tile's padding.

Error correction and size
Source
<div style="display:flex;gap:1rem;align-items:flex-start;flex-wrap:wrap">
<hui-qr-code value="https://ledger.example.com/link/4f2a9c17e5" level="L" size="120" label="Level L"></hui-qr-code>
<hui-qr-code value="https://ledger.example.com/link/4f2a9c17e5" level="H" size="120" label="Level H"></hui-qr-code>
</div>

Nothing to encode

An empty value draws the tile and no code, sets data-invalid and warns once - never repeatedly, however often it redraws. A value too long for any version at the chosen level does the same.

Nothing to encode
Source
<hui-qr-code value="" label="No pairing code yet"></hui-qr-code>

Server-side mechanics

The server owns the value. The element encodes what it is handed and draws it; it never fetches, generates or stores a token, and it keeps no copy of the value beyond the attribute. Re-rendering the same markup draws the same code, so an hx-swap that replaces the element with a fresh pairing token is the entire update. A Go host that would rather not ship an encoder to the browser can render the same code server-side as SVG and send that instead - this element is for hosts that would rather send the value.

Accessibility

  • The tile is role="img" named by label; the SVG is aria-hidden, so a screen reader is told a code is present and not read a meaningless path.
  • The label defaults to "QR code" rather than being empty, so an unnamed element is never an unlabelled image.
  • Under forced colours the tile and the modules keep their own colours through forced-color-adjust: none, because an inverted code does not scan.

Gotchas

  • The tile is white and the modules dark in the dark theme and under forced colours alike. This is not an oversight: a camera reads dark on light far more reliably than the reverse, and a themed code is a code that sometimes does not scan.
  • The tile's 16px padding is the quiet zone a scanner needs. Removing it with ::part(tile) will produce a code that looks right and reads unreliably.
  • The value is not in the accessibility tree. A reader who cannot see the code needs the alternative - a link, a copyable code - in the markup beside it, as the example does.