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 app — samples/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)
| Group | Keys |
|---|---|
font | family, size, weight, lineHeight |
color | primary, placeholder, error, icon |
spacing | padding (CSS shorthand) |
border | color, width, radius |
background | color |
❌ Rejected at validation time (throws frame_style_invalid)
- Inline CSS strings (
style: "color: red") - Pseudo-selectors (
:hover,:focus— useclassesinstead) - Animations, transitions, transforms
position,display,z-index— VORA Mirror owns layoutbox-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:
| Category | Meaning |
|---|---|
native | Rendered inside the processor's own iframe (e.g. PAN field on a card-iframe processor) |
proxy | Pure VORA-rendered DOM — your style controls every pixel |
monolith | The 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_available | The active processor doesn't support this element type at all |
Typical per-element shapes (varies by your account's active processor):
| Element | Typical category |
|---|---|
card | native (rendered inside the processor's PAN iframe or all-in-one monolith) |
address | native or proxy depending on processor |
email / cardholder / save-for-future-use | proxy everywhere (VORA-rendered DOM) |
payment-method-picker | proxy today; will flip to native on supporting processors when wrap-element strategy ships |
wallet-button.applePay / googlePay | native on every processor that supports wallets |
wallet-button.link / cashapp / klarna / affirm | native 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 +
confirmPaymentIntentflow - Errors —
FrameErrorcodes + recovery samples/frame-react— clone-and-run reference integration