Skip to main content
Elements fields render inside iframes, so you can’t target them with your page’s CSS. Instead, you pass a style configuration object that the SDK applies inside each iframe.

Style Config Shape

type ElementStyleConfig = {
  base?:        ElementStyle; // default state
  focus?:       ElementStyle; // when the field has focus
  invalid?:     ElementStyle; // value is present but invalid (on blur)
  complete?:    ElementStyle; // value is complete and valid (on blur)
  placeholder?: ElementStyle; // styles the placeholder text
};
Each key maps to an ElementStyle object where the keys are camelCase CSS properties.

Passing Styles

Per-element — pass style in the options when creating an element:
const cardNumber = vault.createElement('cardNumber', {
  style: {
    base:        { color: '#1a1a2e', fontSize: '15px', padding: '12px 14px' },
    focus:       { caretColor: '#6366f1', backgroundColor: '#fff' },
    invalid:     { color: '#ef4444' },
    complete:    { color: '#22c55e' },
    placeholder: { color: '#94a3b8' },
  },
});
Global default — pass appearance when calling OzVault.create(). It accepts an Appearance object with an optional theme preset and/or variables overrides. All elements inherit this; per-element style merges on top.
const vault = await OzVault.create({
  pubKey:      'YOUR_PUB_KEY',
  fetchWaxKey: createFetchWaxKey('/api/mint-wax'),
  appearance: {
    theme: 'flat',
    variables: {
      colorPrimary:     '#6366f1',
      colorText:        '#1a1a2e',
      fontFamily:       '"Inter", sans-serif',
      fontSize:         '15px',
      padding:          '12px 14px',
      colorPlaceholder: '#94a3b8',
    },
  },
});

Themes

The theme preset provides a complete set of defaults. Available themes:
ThemeDescription
'default'Dark text on transparent background; neutral placeholder; indigo caret
'night'Light text on transparent background; muted placeholder; indigo caret (dark-mode optimized)
'flat'Dark text; no border radius; bottom-border-only underline style that changes color on focus/invalid/complete
You can combine theme + variables — the theme applies first, then variables override on top.
Omitting theme is not the same as no theme. Passing appearance: {} or appearance: { variables: { ... } } without a theme key applies the 'default' preset as a base. To render elements with no preset styling at all, omit the appearance prop entirely and use per-element style overrides.
appearance valueResult
(omitted entirely)No preset styles — element uses its minimal built-in defaults
{}Equivalent to { theme: 'default' } — full default theme applied
{ theme: 'night' }Night theme applied
{ variables: { colorText: '#333' } }Default theme + variable overrides

AppearanceVariables

VariableMaps to
colorTextbase.color
colorBackgroundbase.backgroundColor
colorPrimarybase.caretColor and focus.caretColor
colorDangerinvalid.color
colorSuccesscomplete.color
colorPlaceholderplaceholder.color
fontFamilybase.fontFamily
fontSizebase.fontSize
fontWeightbase.fontWeight
letterSpacingbase.letterSpacing
lineHeightbase.lineHeight
paddingbase.padding

Supported Style Properties

Typography

PropertyCSS equivalent
colorcolor
fontSizefont-size
fontFamilyfont-family
fontWeightfont-weight
fontStylefont-style
fontVariantfont-variant
fontSmoothingfont-smoothing
webkitFontSmoothing-webkit-font-smoothing
mozOsxFontSmoothing-moz-osx-font-smoothing
letterSpacingletter-spacing
lineHeightline-height
textAligntext-align
textDecorationtext-decoration
textShadowtext-shadow
textTransformtext-transform

Spacing & Box

PropertyCSS equivalent
paddingpadding
paddingToppadding-top
paddingRightpadding-right
paddingBottompadding-bottom
paddingLeftpadding-left

Background & Border

PropertyCSS equivalent
backgroundColorbackground-color
opacityopacity
borderRadiusborder-radius
borderTopLeftRadiusborder-top-left-radius
borderTopRightRadiusborder-top-right-radius
borderBottomLeftRadiusborder-bottom-left-radius
borderBottomRightRadiusborder-bottom-right-radius
borderborder
borderColorborder-color
borderWidthborder-width
borderStyleborder-style
borderTop / borderRight / borderBottom / borderLeftindividual side shorthands
borderTopColor / borderRightColor / borderBottomColor / borderLeftColorindividual side colors
borderTopWidth / borderRightWidth / borderBottomWidth / borderLeftWidthindividual side widths
borderTopStyle / borderRightStyle / borderBottomStyle / borderLeftStyleindividual side styles
boxShadowbox-shadow
outlineoutline
outlineColoroutline-color
outlineWidthoutline-width
outlineStyleoutline-style
outlineOffsetoutline-offset

Sizing

PropertyCSS equivalent
heightheight
minHeightmin-height
maxHeightmax-height

Caret

PropertyCSS equivalent
caretColorcaret-color

Transitions & Cursors

PropertyCSS equivalent
transitiontransition
cursorcursor
Properties like position, display, and width that could affect iframe layout are not supported. Use height / minHeight to control the field’s vertical size.
CSS custom properties (var(--token)) are not accepted in style values. The style sanitizer blocks all CSS function syntax, including var(), to prevent CSS injection through the iframe boundary. Pass literal values only:
- color: 'var(--brand-color)'
+ color: '#0057FF'
When a value is blocked, the SDK strips it silently — no error is thrown and no console warning is emitted. The browser falls back to the element’s default (unstyled) appearance for that property. If your design tokens resolve to CSS variables, resolve them to their literal values before passing them in.

Custom Fonts

To use a custom web font inside the iframes, pass a fonts array to OzVault.create(). You can reference a Google Fonts URL:
const vault = await OzVault.create({
  pubKey:      'YOUR_PUB_KEY',
  fetchWaxKey: createFetchWaxKey('/api/mint-wax'),
  fonts: [
    {
      cssSrc: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500&display=swap',
    },
  ],
  appearance: {
    variables: {
      fontFamily: "'Inter', sans-serif",
    },
  },
});
Or a self-hosted font:
fonts: [
  {
    family: 'MyFont',
    src:    'url(https://yourcdn.com/fonts/myfont.woff2) format("woff2")',
    weight: '400',
    style:  'normal',
  },
],

Complete Example

import { OzVault, createFetchWaxKey } from '@ozura/elements';

const vault = await OzVault.create({
  pubKey:      'YOUR_PUB_KEY',
  fetchWaxKey: createFetchWaxKey('/api/mint-wax'),
  fonts: [
    { cssSrc: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500&display=swap' },
  ],
  appearance: {
    theme: 'default',
    variables: {
      fontFamily:       "'Inter', sans-serif",
      fontSize:         '15px',
      colorText:        '#111827',
      padding:          '11px 13px',
      colorBackground:  '#f9fafb',
      colorPrimary:     '#6366f1',
      colorDanger:      '#ef4444',
      colorSuccess:     '#16a34a',
      colorPlaceholder: '#9ca3af',
    },
  },
});

const cardNumber = vault.createElement('cardNumber');
const expiry     = vault.createElement('expirationDate');
const cvv        = vault.createElement('cvv', {
  style: {
    // override just the CVV field
    base: { letterSpacing: '0.15em' },
  },
});

Reflecting State with CSS Classes

Combine the change event with CSS classes on your wrapper element to style the border and background based on field state:
function watchField(el, wrapperId) {
  el.on('change', ({ complete, valid, error }) => {
    const wrap = document.getElementById(wrapperId);
    wrap.classList.toggle('oz-complete', complete && valid);
    wrap.classList.toggle('oz-invalid',  !valid && error != null);
  });
  el.on('focus', () => document.getElementById(wrapperId).classList.add('oz-focused'));
  el.on('blur',  () => document.getElementById(wrapperId).classList.remove('oz-focused'));
}

watchField(cardNumber, 'card-number-wrap');
watchField(expiry,     'expiry-wrap');
watchField(cvv,        'cvv-wrap');
.oz-field-wrap {
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  transition: border-color 0.15s;
}

.oz-field-wrap.oz-focused  { border-color: #6366f1; }
.oz-field-wrap.oz-complete { border-color: #22c55e; }
.oz-field-wrap.oz-invalid  { border-color: #ef4444; }

Updating Styles at Runtime

Call element.update({ style: { ... } }) to restyle a field without re-mounting it:
// Change only the base color
cardNumber.update({ style: { base: { color: '#333' } } });

// Remove a previously set style — pass an empty bucket to clear it
cardNumber.update({ style: { base: {} } });
Style update semantics:
  • Properties you include are merged into the current style — they replace their previous values.
  • Properties previously set but absent from the new style object retain their current values (no keys are cleared by omission).
  • To reset a specific property, pass it explicitly with an empty string: { base: { color: '' } }.
  • To fully reset all styles, destroy and recreate the element.
  • Global vault appearance theme styles are always preserved underneath.
In React, use the style prop on individual field components — it is applied on every render.

React

In React, pass appearance to <OzElements> and style to individual field components:
<OzElements
  pubKey="YOUR_PUB_KEY"
  fetchWaxKey={createFetchWaxKey('/api/mint-wax')}
  appearance={{
    theme: 'flat',
    variables: { colorPrimary: '#6366f1', fontFamily: '"Inter", sans-serif' },
  }}
>
  <OzCard
    style={{
      base:    { fontSize: '15px' },
      invalid: { color: '#ef4444' },
    }}
  />
</OzElements>

Next Steps

Card Elements

All card field options and events.

Bank Elements

All bank field options and events.