Skip to main content

Widget customization

The widget renders inside a Shadow DOM (mode: 'open'). Your page CSS does not reach its internals, and its CSS does not leak onto your page. That isolation is the reason customization happens through a small, explicit surface rather than through stylesheets.

Accent color

The accent-color attribute sets the primary button background, link color, focus outline, spinner, and progress fill.

<veridia-widget accent-color="#7C3AED"></veridia-widget>

The default is #0f172a — slate-900, very close to black. If you set nothing, you get a near-black button. That is intentional (it is neutral against almost any brand), but it surprises people who expect blue.

The value is injected verbatim as the --accent CSS custom property, so any valid CSS color works — not just hex:

<veridia-widget accent-color="rgb(124 58 237)"></veridia-widget>
<veridia-widget accent-color="oklch(0.55 0.22 264)"></veridia-widget>
<veridia-widget accent-color="var(--brand-primary)"></veridia-widget>

The last form is useful: var() resolves against the host element, so the widget can inherit a color from your own design tokens.

Button text on the accent surface is white. Pick a color with enough contrast against white to pass WCAG AA — pastels will not.

Language

locale accepts exactly three values: en, es, pt.

<veridia-widget locale="es"></veridia-widget>

If you omit it, the widget reads navigator.language and falls back to English for anything it does not recognize. Any other value you pass is ignored and the browser-locale fallback applies — the attribute is validated, not trusted.

Set it explicitly whenever your app already knows the user's language. Browser locale and app locale disagree more often than you would expect, especially on shared or corporate devices.

locale can be changed at runtime and the widget re-renders immediately — but read the warning in Configuration first if you configure the widget programmatically.

On document capture steps, the widget shows an "or / Upload from gallery" link below the camera buttons. The user picks: camera or a file from their device.

Three things worth knowing:

  1. It is document-only. The picker is rendered exclusively on the document branch of the capture view, and the capture function additionally refuses a selfie role at runtime. The selfie and the active-liveness burst can only come from a live camera. That is two independent enforcement points, and it is deliberate: a biometric you accept as a file is a biometric an attacker can supply.
  2. The user chooses; the camera stays the default. There is no attribute to force gallery-only, and none to disable the picker.
  3. Every frame is labelled with its provenance (camera or upload) and that label reaches the backend.

An honest limit on point 3, because your compliance team will ask: the provenance label is self-reported by the browser and trivially forgeable — an attacker simply sends camera. It is telemetry and triage, not a fraud control. Its real value is that honest traffic labels itself truthfully, which keeps gallery uploads from contaminating the calibration of the document-forensics signals that do catch injected documents. Only platform attestation could make provenance non-forgeable, and that is out of reach for a web widget.

What you can tell compliance with confidence: the selfie and liveness capture cannot be file-sourced. What you must not claim: that a document marked camera was photographed with a camera.

If a chosen file is rejected — wrong type, or over 20 MB — the widget shows an inline message and stays on the capture step. The camera is still right there. It does not emit veridia:error for that.

Sizing

The widget's inner card is capped at max-width: 440px and is fluid below that. It sets no minimum height on its container.

That cap is inside the Shadow DOM, so giving the host a wider box does not widen the card — you get a 440px card sitting in an empty gap. Size the host to what you actually want:

veridia-widget {
display: block;
width: 100%;
max-width: 440px;
margin: 0 auto;
}

There are no width-based media queries in the widget. It reads well on phones because the layout is fluid up to 440px, not because a mobile breakpoint switches it. Nothing changes at 480px.

Subclassing the custom element

The widget class is exported, so you can extend it and register your own tag. This is the supported escape hatch when attributes are not enough.

import { VeridiaWidget } from 'https://widget.xxuxe.online/veridia-widget.min.js';

class AcmeKyc extends VeridiaWidget {
connectedCallback() {
// Defaults for every instance in our app.
if (!this.hasAttribute('locale')) this.setAttribute('locale', 'es');
if (!this.hasAttribute('accent-color')) this.setAttribute('accent-color', '#7C3AED');
super.connectedCallback();
}
}

customElements.define('acme-kyc', AcmeKyc);
<acme-kyc publishable-key="qv_pubt_YOUR_KEY" user-ref="customer-12345"></acme-kyc>

Notes that will save you time:

  • Call super in the lifecycle callbacks you override. connectedCallback reads the attributes and renders; skipping super gives you an element that never draws anything.
  • Importing the bundle registers <veridia-widget> as a side effect. The registration is guarded against double-registration, so your subclass tag coexists with the original.
  • The shadow root is mode: 'open', so this.shadowRoot is reachable from a subclass. But render() replaces innerHTML on every state transition — anything you inject into the shadow root is destroyed on the next render. Do not build features on that.
  • Everything except the class and the four public types is internal and can change without a major version. Subclass to set defaults, wrap behavior, or add your own listeners. Do not reach into private fields.

For anything deeper — different typography, different capture overlay, a different flow — the supported route is not a subclass. Contact support.

What is deliberately fixed

You cannot change the internal typography, the spacing, the capture overlay and framing guide, the copy, or the step order. Not through CSS (Shadow DOM), not through attributes.

The capture overlay in particular is calibrated against the quality checks: the framing guide is what makes the document land in the region the sharpness check evaluates. Redesigning it changes acceptance rates.

What's next