Appearance API Theme every element of the payment step

Your checkout, down to the pixel.

The Naturpay checkout is a blank surface you paint with your own design system. Drive type, color, spacing, radius and motion through a single appearance object, then drop to per-element CSS for the last ten percent. Nothing here looks bolted on, because nothing here is.

Tokens and raw CSS Runtime theme switch Localized and currency-aware
appearance appliedrepaint · 9 ms, no reflow
Inherits your tokensfont, color, radius

The Appearance API

One theme object, five token families

Most of the design work lives in a single appearance object you pass at mount. Five families of design tokens — type, color, spacing, radius and motion — cascade across every field, label, button and wallet inside the checkout. Change one value and the whole surface repaints in place, with no remount and no layout jump.

  • Type tokens: font family, weight scale, label and input sizes, letter spacing.
  • Color tokens: accent, surface, text, muted, border, danger and success states.
  • Spacing, radius and motion tokens for rhythm, corners and transition timing.
design tokens
export const appearance = {
  variables: {
    // type
    fontFamily: 'Familjen Grotesk, sans-serif',
    fontSizeBase: '15px',
    labelWeight: 600,
    // color
    colorAccent: '#c2410c',
    colorSurface: '#16120f',
    colorText: '#efe7da',
    colorMuted: '#8f8474',
    colorBorder: '#3a2f26',
    colorDanger: '#d96a4a',
    // spacing · radius · motion
    spacingUnit: '6px',
    radius: '4px',
    motion: '160ms ease',
  },
};

The last ten percent

When tokens stop, raw CSS starts

Tokens carry the broad strokes. For everything else there is a rules map keyed by stable class names and state selectors, so you can style a focused field, a card error or a hovered wallet exactly as your design system demands. Switch the panels to see tokens, per-element rules and a full dark theme side by side.

appearance.theme
// fast path — set the broad strokes once
const theme = {
  variables: {
    fontFamily: 'Familjen Grotesk, sans-serif',
    colorAccent: '#c2410c',
    colorSurface: '#16120f',
    radius: '4px',
    spacingUnit: '6px',
  },
};
// last 10% — per-element rules and state selectors
const theme = {
  rules: {
    '.Label':          { textTransform: 'uppercase', letterSpacing: '.04em' },
    '.Field':          { border: '1px solid #3a2f26', padding: '12px' },
    '.Field:focus':    { border: '1px solid #c2410c', shadow: '0 0 0 3px rgba(194,65,12,.18)' },
    '.Field--invalid': { border: '1px solid #d96a4a' },
    '.WalletButton:hover': { transform: 'translateY(-1px)' },
    '.PayButton':      { borderRadius: '3px', fontWeight: 600 },
  },
};
// a full dark theme is just another variable set
const dark = {
  variables: {
    colorSurface: '#16120f',
    colorText:    '#efe7da',
    colorMuted:   '#8f8474',
    colorBorder:  '#3a2f26',
    colorAccent:  '#e2570f',
  },
};

const isDark = matchMedia('(prefers-color-scheme: dark)').matches;
checkout.update({ appearance: isDark ? dark : light });

One continuous product

It reads like your design system

Because the checkout draws from the same tokens as the rest of your app, the payment step feels like the page it lives on, not a window punched into it. Buyers never cross a visible seam, and you never ship a screen that breaks your own guidelines.

  • Pull tokens straight from your CSS custom properties or design library.
  • Match focus rings, error styling and button shape to your existing forms.
  • Fields stay isolated and tokenized, so styling never touches PCI scope.

Runtime theming

Switch light, dark or brand on the fly

Theming is not frozen at mount. Call update with a new appearance object whenever your app changes mode, and the live checkout repaints in place — same DOM, same input state, same focused field. Follow the system color scheme, honor a user toggle, or swap a sub-brand palette per route.

  • React to prefers-color-scheme and to a manual light or dark toggle.
  • Carry a different palette per brand or per storefront from one integration.
  • No remount, no flash, no lost keystrokes when the theme changes mid-flow.
checkout.update
// follow a user toggle
toggle.addEventListener('change', () => {
  checkout.update({ appearance: themes.light });
});
// react to the system color scheme
matchMedia('(prefers-color-scheme: dark)')
  .addEventListener('change', (e) => {
    checkout.update({
      appearance: e.matches ? themes.dark : themes.light,
    });
  });
// swap a sub-brand palette per route
router.on('enter', (route) => {
  checkout.update({
    appearance: themes[route.brand] ?? themes.base,
  });
});
display
const checkout = wv.checkout({
  intent: 'pi_3Q…',
  appearance,
  // localize copy and number formats
  locale: 'fr-FR',
  // show the buyer's currency, settle in yours
  display: {
    currency: 'EUR',
    presentment: 'auto',
  },
});

// override a single string if you must
checkout.setLabels({ pay: 'Régler la commande' });

Localization and currency

Speak the buyer's language and money

Set a locale and the checkout translates its labels, errors and field hints, and formats numbers, dates and amounts to local convention. Display prices in the shopper's currency while you settle in your own, and override any single string when your brand voice needs a specific word.

  • Built-in translations for the checkout UI across major locales.
  • Locale-aware amount, date and decimal formatting out of the box.
  • Per-label overrides so any string can match your exact wording.

Layout options

Arrange the flow your way

Beyond color and type, the structure of the payment step is yours to set. Pick a layout that fits the surface — a calm single column, a stepped accordion, or fields laid inline inside a form you already own.

One column

A single, stacked flow that suits most pages: wallet on top, card fields below, one clear call to pay. The simplest layout to read and the fastest to scan on mobile.

layout: 'stacked'

Accordion

Group the steps into expandable sections — contact, address, payment — so a longer flow stays calm. One panel opens at a time, with progress kept clear the whole way down.

layout: 'accordion'

Inline fields

Mount the card, expiry and CVC fields individually inside a form you already built. Naturpay owns only the sensitive inputs; the grid, the labels and the order are entirely yours.

layout: 'inline'

Make the checkout unmistakably yours

Grab a test key, pass your tokens, and watch the payment step take on your brand in a single update call. When the design is dialed in, talk to an engineer about going live.