PetalComponents.QrCode (petal_components v4.15.3)

Copy Markdown View Source

A QR code rendered as pure server-side SVG - zero JavaScript, crisp at any size, themeable with currentColor, and it prints.

<.qr_code value="https://petal.build" class="size-40 text-gray-900 dark:text-white" />

Every dark module lands in a single <path>, so a dense code is one DOM node rather than eight hundred. Size it with classes (size-40, w-64 h-64) or pass size for explicit pixel width/height attributes.

The encoder is an optional dependency

Encoding a QR code is real algorithmic work - byte-mode segmentation, Reed- Solomon error correction, mask evaluation - so the matrix comes from eqrcode (MIT, pure Elixir, no dependencies of its own). It is declared optional: true, which means it is not installed into your app unless you ask for it:

# mix.exs
{:eqrcode, "~> 0.2"}

Render <.qr_code> without it and you get a loud error with those instructions rather than a blank box. Everything downstream of the matrix - the SVG, the quiet zone, module rounding, the logo knockout, theming - is ours.

Dark mode: the inversion rule

QR scanners expect dark modules on a light background. Two ways to stay scannable on a dark surface, in order of safety:

  • (a) The safe default - keep the code dark-on-light. Give it an explicit light background and let it sit on a light card. Works with every scanner ever made, including the cheap ones and the ones printed on receipts.

    <div class="rounded-xl bg-white p-4">
      <.qr_code value={@url} background="white" class="size-40 text-gray-900" />
    </div>
  • (b) Inverted - light modules on the dark surface. Looks better on a dark panel and most modern phone cameras handle it, but some older and embedded scanners do not. If you take this route the quiet zone must be the dark surface colour too (leave background transparent so the surface shows through), and the contrast must be high - never grey on grey.

    <div class="bg-gray-900 p-6">
      <.qr_code value={@url} class="size-40 text-white" />
    </div>

Because color defaults to currentColor, class="text-gray-900 dark:text-white" gives you (b) automatically in dark mode. That is a deliberate choice - it is the good-looking default - but if your audience might be scanning with something old, pick (a) and test.

Accessibility

The <svg> is role="img" with a default aria-label of "QR code". Override it with label (or your own aria-label) to say what the code actually points at - "QR code linking to your account settings". The value is never used as the label: it is often a secret (a TOTP enrolment URI contains the shared secret) and reading a URL aloud character by character helps nobody.

Pass aria-hidden="true" when the same information sits next to the code as real text - a QR code must never be the only route to the content. Doing so drops role and the label, so screen readers skip straight to the text.

Logo slot

The :logo slot knocks a hole in the middle of the code and renders your content there. Using it forces error_correction to :h, the level that tolerates ~30% loss, so the code still scans with the hole in it. Slot content is laid out in a 100x100 box that is scaled to fit the knockout, so size things in that coordinate space.

<.qr_code value="https://petal.build" background="white" class="size-48">
  <:logo>
    <div style="display:flex;align-items:center;justify-content:center;height:100%">
      <img src="/images/logo.svg" width="70" height="70" />
    </div>
  </:logo>
</.qr_code>

Summary

Functions

Renders a QR code as inline SVG.

Functions

qr_code(assigns)

Renders a QR code as inline SVG.

See PetalComponents.QrCode for the encoder dependency, the dark-mode inversion rule and accessibility guidance.

Attributes

  • value (:string) (required) - the string to encode (URL, otpauth:// URI, WiFi string, arbitrary text).
  • size (:any) - optional pixel size, setting width/height attributes; omit it and drive the size with classes instead, e.g. class="size-48". Defaults to nil.
  • color (:string) - module (dark) colour; currentColor by default so it themes with text-* classes and prints. Defaults to "currentColor".
  • background (:string) - fill behind the modules, quiet zone included; transparent by default. Set it (e.g. "white") when placing the code on a dark or busy surface. Defaults to "transparent".
  • error_correction (:atom) - error correction level, low to high; forced to :h when the logo slot is used. Defaults to :m. Must be one of :l, :m, :q, or :h.
  • rounded (:any) - 0..1 module corner rounding: 0 = square modules, 1 = fully round dots. Defaults to 0.
  • label (:string) - accessible name for the code. Never derived from value, which is often a secret. Defaults to "QR code".
  • class (:any) - size + colour, e.g. "size-48 text-gray-900 dark:text-white". Defaults to nil.
  • Global attributes are accepted.

Slots

  • logo - optional centre logo, rendered in a knocked-out hole in the middle of the code. Forces error_correction to :h. Content is laid out in a 100x100 box scaled to fit the hole.