Skip to main content

VORA Mirror — Preview & customization

What VORA Mirror looks like, three reference themes, and the integration snippet you can copy.

For a hands-on interactive render, try the Interactive playground — pick a theme, edit the style JSON, watch the preview re-render, and copy the generated snippet. Optional live SDK mount against a sandbox demo merchant.

Want a full local sandbox? Clone the sample appsamples/frame-react is the end-to-end working integration with a live Vite + Express dev setup. Five minutes from git clone to rendered card field.


The 30-second integration

<!-- Add to your checkout page -->
<script src="https://js.vonpay.com/v1/vora.js"></script>

<div id="card-element"></div>
<button id="pay-button" disabled>Pay $49.99</button>

<script>
// 1. Create a session on your server (POST /v1/sessions with vp_sk_*)
const { session_id } = await fetch("/api/create-session", { method: "POST" })
.then((r) => r.json());

// 2. Init VORA with your publishable key
const vora = new VORA({ publishableKey: "vp_pk_test_..." });
await vora.sessions.retrieve(session_id);

// 3. Mount the card field
const card = vora.fields.create("card");
card.mount("#card-element");
card.on("change", (e) => {
document.getElementById("pay-button").disabled = !e.complete;
});

// 4. Tokenize on submit + send to your server
document.getElementById("pay-button").addEventListener("click", async () => {
const { token } = await card.tokenize();
await fetch("/api/charge", {
method: "POST",
body: JSON.stringify({ payment_method: token }),
});
});
</script>

Full walkthrough → Quickstart.


Three reference themes

VORA Mirror's style option is an allowlist-validated object. Anything outside the allowlist throws frame_style_invalid before the iframe mounts — your CI catches misuse before merchants see it.

Theme 1 — Default (matches your site's body styles)

const card = vora.fields.create("card", {
// No style override — inherits browser defaults.
// Best for sites with simple, neutral checkout pages.
});

Visual: System font, default input border (#d1d5db on most stacks), 16px text. Looks at home on any page that doesn't fight it.

Theme 2 — Light + branded accent

const card = vora.fields.create("card", {
style: {
font: {
family: "system-ui, -apple-system, sans-serif",
size: "16px",
weight: "400",
},
color: {
primary: "#1a1a1a", // input text
placeholder: "#9ca3af", // placeholder gray
error: "#dc2626", // validation red
icon: "#6b7280", // card-brand icon
},
border: {
color: "#e5e7eb",
width: "1px",
radius: "8px",
},
background: { color: "#ffffff" },
spacing: { padding: "12px 14px" },
},
});

Visual: Pill-shaped input, soft gray border, 8px corners. Sits naturally on a Tailwind/shadcn-style page.

Theme 3 — Dark mode

const card = vora.fields.create("card", {
style: {
font: {
family: '"Inter", system-ui, sans-serif',
size: "16px",
weight: "500",
},
color: {
primary: "#e5e7eb", // light text on dark bg
placeholder: "#6b7280",
error: "#f87171", // softer red for dark
icon: "#9ca3af",
},
border: {
color: "#374151", // mid-gray border
width: "1px",
radius: "12px",
},
background: { color: "#1f2937" }, // slate-800
spacing: { padding: "14px 16px" },
},
});

Visual: Dark surface with mid-gray border. Pairs with a slate-50 / zinc-900 dashboard or marketing-site aesthetic.


What you can and can't style

✅ Allowed (every key in VORAFieldStyle)

GroupKeys
fontfamily, size, weight, lineHeight
colorprimary, placeholder, error, icon
spacingpadding (CSS shorthand)
bordercolor, width, radius
backgroundcolor

❌ Rejected at validation time (throws frame_style_invalid)

  • Inline CSS strings (style: "color: red")
  • Pseudo-selectors (:hover, :focus — use classes instead)
  • Animations, transitions, transforms
  • position, display, z-index — VORA Mirror owns layout
  • box-shadow, outline — reserved for future style schema additions

For pseudo-selectors, use the classes option to attach your own CSS classes:

const card = vora.fields.create("card", {
classes: {
focus: "my-card-focus", // applied while focused
invalid: "my-card-invalid", // applied when validation fails
complete: "my-card-complete", // applied when all fields valid
},
});

Then in your own stylesheet:

.my-card-focus { outline: 2px solid #3b82f6; }
.my-card-invalid { outline: 2px solid #dc2626; }
.my-card-complete { border-color: #10b981; }

Test the themes interactively

The fastest way to see all three themes rendered against a real sandbox processor is the sample app:

git clone https://github.com/Von-Payments/vonpay.git
cd vonpay/samples/frame-react

cp .env.example .env
# Edit .env — fill in your vp_sk_test_* + VITE_VONPAY_PUBLISHABLE_KEY

pnpm install
pnpm dev

Open http://localhost:5173, click Create checkout session, then edit src/App.tsx line ~95 to swap the style prop between the three themes above. Vite hot-reloads each change.

Test card 4242 4242 4242 4242 (any future expiry, any CVC) renders the full successful tokenization path.


Capability hints — what each element looks like in practice

The capability map at /v1/public/binder-load tells the SDK how each element renders for the active processor — one of four categories:

CategoryMeaning
nativeRendered inside the processor's own iframe (e.g. PAN field on a card-iframe processor)
proxyPure VORA-rendered DOM — your style controls every pixel
monolithThe processor's all-in-one embedded checkout renders this surface as part of its bigger picture; VORA Mirror's element acts as a typed handle
not_availableThe active processor doesn't support this element type at all

Typical per-element shapes (varies by your account's active processor):

ElementTypical category
cardnative (rendered inside the processor's PAN iframe or all-in-one monolith)
addressnative or proxy depending on processor
email / cardholder / save-for-future-useproxy everywhere (VORA-rendered DOM)
payment-method-pickerproxy today; will flip to native on supporting processors when wrap-element strategy ships
wallet-button.applePay / googlePaynative on every processor that supports wallets
wallet-button.link / cashapp / klarna / affirmnative where supported, not_available elsewhere

What this means for theming: native elements render inside the active processor's iframe — your style option translates through to that processor's style schema. proxy elements are pure VORA-rendered DOM — your style controls every pixel directly.

Full reference → Elements.


What's next

  • Quickstart — full end-to-end walkthrough
  • Elements — per-element options + collect shapes
  • React<VORAProvider> + useVORA() patterns
  • 3D Secure — modal harmonization + confirmPaymentIntent flow
  • ErrorsFrameError codes + recovery
  • samples/frame-react — clone-and-run reference integration