Styling elements

Style Tokeflow Bridge SDK secure fields with the ElementStyle API and reflect live element state onto your own container with CSS.

Tokeflow's secure fields run inside isolated, cross-origin iframes; card data is encrypted in the browser and never touches your servers. Because the inputs live in another origin, you can't reach inside them with your page's CSS — so the Bridge SDK gives you a structured ElementStyle API to style the text inside the iframe, and a change event you use to style the container around the iframe.

Together, these two layers let secure fields blend seamlessly into your brand while keeping cardholder data fully isolated.

Styling is purely presentational. It never changes how data is collected, encrypted, or tokenized — see Tokenization for the data flow.

The two styling layers

There are two distinct surfaces, and they're styled in two different ways.

LayerWhat it stylesHow you style it
Inside the iframeThe input text, placeholder, and caret rendered by the secure fieldElementStyle object passed as style
Around the iframeBorders, padding, background, focus ring, validation statesYour own CSS, driven by the change event

The ElementStyle API

Pass a style object when you create an element. It maps style rules to the input rendered inside the iframe.

const style = {
  base: {
    color: '#333333',
    fontSize: '16px',
    fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
    fontWeight: '400',
    lineHeight: '24px',
    '::placeholder': { color: '#aaaaaa', fontStyle: 'italic' },
    ':focus': { color: '#000000' },
  },
  complete: { color: '#28a745' }, // input is complete and valid
  invalid: { color: '#dc3545' },  // input is invalid
  empty: { color: '#6c757d' },    // input is empty
};

const cardElement = tokeflow.createCardElement({ style });

Base styles

The base object holds the default appearance, applied in every state unless overridden by a state-specific block. These are the core typographic properties supported.

PropertyTypeDescription
colorstringText color of the input value (any CSS color).
fontSizestringFont size of the input text, e.g. "16px".
fontFamilystringFont stack for the input text. See Loading custom fonts.
fontWeightstringFont weight, e.g. "400" or "600".
lineHeightstringLine height of the input text, e.g. "24px".

Match fontSize, fontFamily, and lineHeight to your surrounding form inputs so the secure field is visually indistinguishable from your native fields.

Pseudo-selectors

Inside base, two pseudo-selectors let you target sub-states of the input.

SelectorTargets
::placeholderThe placeholder text shown while the field is empty.
:focusThe input while it has focus.

Each accepts the same typographic properties as base:

const style = {
  base: {
    color: '#1a1a1a',
    fontSize: '16px',
    '::placeholder': {
      color: '#9aa0a6',
      fontStyle: 'italic',
    },
    ':focus': {
      color: '#000000',
    },
  },
};

State styles

Beyond base, you can supply top-level state blocks that override the base color (and other properties) as the cardholder's input changes. The SDK applies them automatically based on the element's live state.

StateApplied whenTypical use
emptyThe field has no value yetMute the caret/text color before input
completeThe value is complete and validConfirm success with a green tone
invalidThe current value fails validationWarn with a red tone
const style = {
  base: {
    color: '#333333',
    fontSize: '16px',
    lineHeight: '24px',
  },
  empty: { color: '#6c757d' },
  complete: { color: '#28a745' },
  invalid: { color: '#dc3545' },
};

State styles only affect properties you can set on the input text (such as color). To restyle the frame — borders, background, shadows — drive your container with the change event, covered in Container styling.

Loading custom fonts

If your fontFamily references a web font that isn't a system font, declare it via the fonts array so the iframe can load it. Use the same option on elementOptions (applied to every element) or per element.

const tokeflow = new TokeflowBridge({
  publicKey: 'pk_live_mer_xxxxxxxx',
  elementOptions: {
    fonts: ['https://fonts.example.com/inter.css'],
    style: {
      base: {
        fontFamily: '"Inter", sans-serif',
        fontSize: '16px',
      },
    },
  },
});

Public keys (pk_live_…) are safe to ship in browser code. Never put a secret key (sk_live_…) in client-side code — see Authentication.

Container styling

The iframe sits inside a container element you control. Style that container with ordinary CSS to handle everything the iframe can't: borders, padding, background, focus rings, and validation states.

Two techniques work together:

  1. :focus-within — a native CSS pseudo-class that matches your container whenever the focus is inside it (including inside the iframe). Use it for the focus ring with no JavaScript required.
  2. State classes from the change event — toggle your own classes (e.g. invalid, complete) on the container in response to the element's live state.

CSS

.card-element-container {
  border: 1px solid #dddddd;
  border-radius: 8px;
  padding: 12px;
  background: #fafafa;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

/* Focus ring — pure CSS, fires when focus is inside the iframe */
.card-element-container:focus-within {
  border-color: #0066ff;
  box-shadow: 0 0 0 3px rgba(0, 102, 255, 0.15);
}

/* Validation states — toggled from the change event */
.card-element-container.invalid {
  border-color: #dc3545;
}
.card-element-container.complete {
  border-color: #28a745;
}

JavaScript

Mount the element into the container, then reflect its state onto the container from the change event.

const cardElement = tokeflow.createCardElement({ style });
cardElement.mount('.card-element-container');

const container = document.querySelector('.card-element-container');

cardElement.on('change', (event) => {
  // event.error is singular; event.empty / event.complete are booleans
  container.classList.toggle('invalid', !!event.error && !event.empty);
  container.classList.toggle('complete', event.complete);
});

The change event payload exposes only non-sensitive state and metadata — never the raw card data:

interface ElementChangeEvent {
  elementType: ElementType;
  empty: boolean;
  complete: boolean;
  error?: ElementError;   // singular — there is no `errors` array
  value?: {               // non-sensitive metadata only
    type?: string;
    brand?: string;       // e.g. 'visa', 'mastercard'
    bin?: string;
    last4?: string;
  };
}

You can also read the same state synchronously at any time with element.getState(), which returns { empty, focused, valid, invalid, complete, error? }. The change event is best for live UI; getState() is best for one-off checks (for example, before tokenizing).

Putting it together

A minimal, fully styled card field:

<div class="card-element-container"></div>
const tokeflow = new TokeflowBridge({ publicKey: 'pk_live_mer_xxxxxxxx' });
await tokeflow.init();

const cardElement = tokeflow.createCardElement({
  style: {
    base: {
      color: '#1a1a1a',
      fontSize: '16px',
      fontFamily: '"Inter", sans-serif',
      lineHeight: '24px',
      '::placeholder': { color: '#9aa0a6' },
    },
    complete: { color: '#28a745' },
    invalid: { color: '#dc3545' },
  },
  placeholder: {
    cardNumber: '1234 5678 9012 3456',
    cardExpirationDate: 'MM/YY',
    cardSecurityCode: 'CVC',
  },
});

cardElement.mount('.card-element-container');

const container = document.querySelector('.card-element-container');
cardElement.on('change', (event) => {
  container.classList.toggle('invalid', !!event.error && !event.empty);
  container.classList.toggle('complete', event.complete);
});

Advanced theme object

ElementStyle covers the common cases. For finer-grained control, you can pass a lower-level theme object instead, with a styles map keyed by internal selectors. This is an escape hatch for advanced layouts and is not needed by most integrations.

const cardElement = tokeflow.createCardElement({
  theme: {
    styles: {
      '.field input': { fontSize: 16, color: '#333333' },
    },
  },
});

The theme selectors target internal element structure and are less stable across SDK versions than ElementStyle. Prefer ElementStyle for portability, and reach for theme only when you need a rule ElementStyle doesn't expose.

Next steps

On this page