Appearance and Localization
Whitelisted styling tokens, brand fonts, the card-brand badge and translated labels for the card frames.
Before You Start
Read the following guides before proceeding:
| Guide | Why |
|---|---|
| PCI Compliant SDK | Isolation model and package map |
| SDK Setup | Registering a font CDN origin |
Overview
The frames accept whitelisted, typed appearance tokens. They do not accept CSS. Arbitrary CSS inside a card
frame is a data-exfiltration channel — an attribute-selector rule with a url() value leaks a PAN one
character at a time — so every value is re-validated inside the frame against a strict grammar and applied
through CSSOM custom properties, never through generated <style> text.
An invalid value is dropped, the frame logs a console warning, and the default renders. A bad theme
degrades the look and never the isolation.
const appearance = {
theme: 'dark',
variables: { colorText: '#FFFFFF', borderRadius: '12px' },
brandBadge: { style: 'logo', tone: 'color' },
};
createCardTopUpFields(sdk, { auth, appearance });theme accepts light and dark; any other value renders the light scheme.
Appearance is read when an element mounts. Changing the object afterwards has no effect. To re-theme,
destroy the element and create a new one.
Variables
| Token | Type | What it styles |
|---|---|---|
colorText | color | Input and value text |
colorLabel | color | Field labels |
colorPlaceholder | color | Placeholder text |
colorError | color | Validation messages and the invalid-field border |
colorFocus | color | Focused-field border and accent |
colorBackground | color | Field background |
colorBorder | color | Field border |
surfaceColor | color | The frame document's own background. Defaults to transparent, so your panel shows through |
fontFamily | font stack | Comma-separated family names |
fontUrl | font file URL | One brand font file — see Brand Fonts |
fontFaces | array | Multi-weight brand font: [{ url, weight?, style? }]. Takes precedence over fontUrl |
fontSizeBase | length | Value font size |
fontSizeLabel | length | Label font size |
lineHeightBase | length | Value line height |
lineHeightLabel | length | Label line height |
fontWeightBase | weight | Value font weight |
fontWeightLabel | weight | Label font weight |
borderRadius | length | Field corner radius |
borderWidth | length | Field border width |
spacingUnit | length | Vertical gap between rows |
controlMinHeight | length | Minimum field height |
Reveal frames accept four more tokens:
| Token | Type | What it styles | Default |
|---|---|---|---|
copyIconSize | length | Square size of the copy-button glyph | 20px |
colorCopyIcon | color | Copy-button glyph | colorLabel |
colorCopyIconHover | color | Copy-button glyph on hover | colorText |
colorCopyIconCopied | color | The transient "copied" checkmark | colorFocus |
Unknown tokens are dropped without a warning to your page.
Value Grammars
Each type is validated against the pattern below. var(), url(), calc(), quotes, semicolons and braces
fail every one of them by construction.
| Type | Validation (regex) | Max length | Accepted forms |
|---|---|---|---|
| color | ^(#[0-9a-fA-F]{3,8}|rgb\(…\)|rgba\(…\)|hsl\(…\)|hsla\(…\)|[a-zA-Z]{3,20})$ | 40 chars | #1A1A1A, rgb(26, 26, 26), rgba(26, 26, 26, 0.5), hsl(0, 0%, 10%), hsla(0, 0%, 10%, 0.5), CSS named colors |
| length | ^\d+(\.\d+)?(px|rem|em)$ | 12 chars | 12px, 1.5rem, 0.75em. Unitless and percentage values are rejected |
| weight | ^(normal|bold|[1-9]00)$ | — | 100–900, normal, bold |
| font stack | Each comma-separated name matches ^\s*(?:'[A-Za-z0-9 _-]+'|"[A-Za-z0-9 _-]+"|[A-Za-z0-9 _-]+)\s*$ | 300 chars | YourBrand, Inter, sans-serif |
| font file URL | ^https:\/\/[^\s"'()\;{}]+\.(woff2|woff|otf|ttf)(\?[^\s"'()\;{}]*)?$ | 500 chars | https://cdn.example.com/fonts/brand-400.woff2 |
Brand Fonts
A brand font loads from a font file URL. A stylesheet URL is rejected — host-supplied CSS is the channel
the token model exists to close.
variables: {
fontFamily: 'YourBrand, Inter, sans-serif',
fontFaces: [
{ url: 'https://cdn.example.com/fonts/brand-400.woff2', weight: '400' },
{ url: 'https://cdn.example.com/fonts/brand-600.woff2', weight: '600' },
],
}| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | https:// URL of a .woff2, .woff, .otf or .ttf file |
weight | string | No | 100–900, normal or bold. Default 400 |
style | string | No | normal or italic. Default normal |
The frame builds the @font-face rules itself and names every face after the first family in fontFamily.
A custom font therefore requires a valid fontFamily; without one, fontUrl and fontFaces are dropped
with a console warning.
Two host-side requirements:
- The CDN origin must be registered against your client id. It enters the frames'
font-srcpolicy.
Include it in onboarding or request it later. - The font host must allow cross-origin loading. Browsers fetch fonts in CORS mode, so the file must
respond with anAccess-Control-Allow-Originheader covering the frame origin.*is acceptable for
public font files.
Without the CORS header the browser blocks the font and reports
net::ERR_FAILEDon a200response.
The frame falls back to the next family infontFamilyand renders normally, so the failure is visible
only in the console.
Brand Badge
The card-number field shows the detected card brand. The artwork is bundled inside the frame; you select
among fixed presentations and cannot inject markup.
| Option | Values | Default | Meaning |
|---|---|---|---|
show | boolean | true | Render the indicator |
style | logo, text | logo | Brand mark or plain brand name |
tone | color, mono | color | Original brand colors, or a single-color mark drawn in colorLabel. Applies to logo only |
background | color | transparent | Plate behind the mark |
borderRadius | length or % | 50% | Plate corners — 50% is a circle, 4px a rounded square |
size | length or % | 24px | Plate size, width equal to height |
offsetX | signed length | 0px | Horizontal nudge; positive moves right |
offsetY | signed length | 0px | Vertical nudge; positive moves down |
The default position is vertically centered, 16 pixels from the field's right edge. borderRadius and
size accept % in addition to the length units; offsetX and offsetY accept a leading - and no %.
appearance: {
brandBadge: {
style: 'logo',
tone: 'color',
background: '#FFFFFF',
borderRadius: '50%',
size: '24px',
},
}Localized Labels
Field labels and validation messages come from your application, already translated. The frames ship
English defaults and render whatever you pass.
createCardTopUpFields(sdk, {
auth,
strings: {
cardNumberLabel: 'Numéro de carte',
expiryLabel: 'MM/AA',
cvvLabel: 'CVV',
nameLabel: 'Titulaire',
errCardLuhn: 'Numéro de carte invalide',
},
});Keys are per element type:
| Element | Key table |
|---|---|
| Top-up collect | Strings Keys |
| Payout collect | Strings Keys |
| Reveal | Strings Keys |
An omitted key renders its English default. Unknown keys are ignored.
Updated 15 days ago

