Skip to content

Theming & Customization

The SDK accepts a theme object that controls the visual appearance of the card form inside the iframe. You only need to override the properties you want to change — everything else falls back to the default theme.

import { ZatlasCardCapture } from '@zatlas/card-capture';
const zatlas = new ZatlasCardCapture({
publishableKey: 'pk_sandbox_your_key',
theme: {
colors: {
primary: '#6D28D9',
borderFocus: '#6D28D9',
background: '#FAFAFA',
},
borders: {
radius: '12px',
},
},
});

You only pass the values you want to change. The SDK deep-merges your overrides with the default theme.

The SDK ships three theme helpers:

ExportDescription
defaultThemeLight theme with teal accents (matches Zatlas platform design)
darkThemeDark background with lighter accents
mergeTheme(partial)Deep-merges a partial theme object with defaultTheme
import { darkTheme } from '@zatlas/card-capture';
const zatlas = new ZatlasCardCapture({
publishableKey: 'pk_sandbox_your_key',
theme: darkTheme,
});

To extend the dark theme:

import { darkTheme, mergeTheme } from '@zatlas/card-capture';
const customDark = mergeTheme({
...darkTheme,
colors: {
...darkTheme.colors,
primary: '#10B981',
borderFocus: '#10B981',
},
});
const zatlas = new ZatlasCardCapture({
publishableKey: 'pk_sandbox_your_key',
theme: customDark,
});

You can change the theme after initialization with updateTheme(). This is useful for toggling between light and dark mode:

import { darkTheme } from '@zatlas/card-capture';
// Toggle to dark mode
zatlas.updateTheme(darkTheme);
// Or pass a partial override
zatlas.updateTheme({
colors: {
primary: '#34D399',
background: '#1F2937',
text: '#F9FAFB',
border: '#374151',
borderFocus: '#34D399',
borderError: '#F87171',
textPlaceholder: '#6B7280',
buttonBackground: '#34D399',
},
});
PropertyDefault (light)Default (dark)Description
colors.background#FFFFFF#1F2937Field background
colors.backgroundHover#FAFBFC#374151Background on hover
colors.backgroundDisabled#F5F5F7#111827Background when disabled
colors.text#1A1B1C#F9FAFBInput text color
colors.textPlaceholder#B6B7BF#6B7280Placeholder text
colors.textError#D84B4B#F87171Error/validation message text
colors.textDisabled#B6B7BF#4B5563Disabled text
PropertyDefault (light)Default (dark)Description
colors.border#B6B7BF#374151Default border
colors.borderHover#CBCCD5#4B5563Border on hover
colors.borderFocus#2D926E#34D399Border on focus
colors.borderError#D84B4B#F87171Border on validation error
colors.borderSuccess#2D926E#34D399Border on valid input
PropertyDefault (light)Default (dark)Description
colors.primary#2D926E#34D399Primary accent (focus ring, CTA default)
colors.success#2D926E#34D399Success accent (valid field border)
PropertyDefault (light)Default (dark)Description
colors.icon#6D6E75#9CA3AFCard brand icon
colors.iconError#D84B4B#F87171Icon on error
colors.iconSuccess#2D926E#34D399Icon on success
PropertyDefaultDescription
colors.buttonBackground#2D926EButton background
colors.buttonBackgroundHover#20674EButton background on hover
colors.buttonText#FFFFFFButton text
PropertyDefaultDescription
colors.labelText#6D6E75Label above each field
PropertyDefaultDescription
colors.bannerErrorBackground#FEF2F2Banner background on payment decline
colors.bannerErrorBorder#FECACABanner border
colors.bannerErrorText#781D1DBanner message text
PropertyDefaultDescription
typography.fontFamily'Inter', system-ui, -apple-system, sans-serifFont stack
typography.fontSize14pxBase font size
typography.fontWeight400Font weight
typography.lineHeight1.5Line height
typography.letterSpacingnormalLetter spacing
PropertyDefaultDescription
borders.radius6pxCorner radius
borders.width1pxBorder width
borders.stylesolidBorder style (solid, dashed, dotted)
PropertyDefaultDescription
spacing.paddingX12pxHorizontal padding inside the field
spacing.paddingY10pxVertical padding inside the field
spacing.iconGap10pxGap between icon and input text
PropertyDefault (light)Default (dark)Description
shadows.focus0 0 0 3px #A2E0C50 0 0 3px rgba(52,211,153,0.25)Focus ring shadow
shadows.error0 0 0 3px rgba(216,75,75,0.15)0 0 0 3px rgba(248,113,113,0.25)Error state shadow
PropertyDefaultDescription
transitions.duration150msAnimation duration
transitions.timingease-in-outTiming function

The SDK injects a secure iframe into the element you pass to card.mount(). The iframe auto-resizes its height to fit the card form content. To ensure a smooth experience on all screen sizes, follow these guidelines.

Style your container element like you would a content block — give it a width, and let the iframe handle its own height:

<div id="card-element" style="width: 100%; max-width: 480px;"></div>

The iframe uses width: 100% to fill its container. You control the width; the SDK controls the height.

  1. The card form renders inside the iframe (card number, expiry, CVC, cardholder name, email, and the CTA button).
  2. The iframe measures its content height and notifies the SDK via postMessage.
  3. The SDK sets the iframe’s pixel height to match.
  4. While the content loads, the iframe starts at a minimum height of 420px to prevent layout shift.

The card form works best between 300px and 600px wide. Below 300px, fields become cramped. Above 600px, the form looks sparse — consider centering it with max-width.

Container widthLayout
300 — 480pxIdeal for mobile and embedded flows
480 — 600pxComfortable on desktop

If you place the card element inside a flexbox or grid parent, make sure the container can grow vertically:

/* Flexbox parent — works fine */
.payment-section {
display: flex;
flex-direction: column;
gap: 16px;
}
/* Grid parent — works fine */
.payment-section {
display: grid;
gap: 16px;
}

Avoid align-items: center on a flex parent with a fixed height — it can push the card form out of view on mobile. Use align-items: stretch (the default) or flex-start instead.

The card form is fully responsive. The expiry and CVC fields stay side by side on all screen sizes — they only need a few characters each. No media queries are needed in your CSS.

For mobile-first layouts, make the container full-width with padding:

#card-element {
width: 100%;
padding: 0 16px;
}