Appearance customization
The default appearance of the SDK is designed so that it can be used without further modifications. However, if you need to adapt the SDK's appearance, especially the card form, to the visual style of your e-shop, the SDK provides several configuration options.
The SDK works with two visual layers, which are modified differently:
- Apple Pay and Google Pay buttons — the buttons are created by libraries from Apple and Google and allow only basic customization. Using the
uiconfiguration object in theapplepayorgooglepayconfiguration parameter, you can only set the color and type of the button (the text contained on the button). Custom CSS cannot be used because the buttons are rendered by the official library and are not available for direct styling. - Card form — the fields for the card number, expiration and security code are placed in an isolated iframe for security reasons. The content of the iframe is not available for styling using CSS from the parent page. The appearance of the card form is therefore set via the
appearanceconfiguration object. All other elements displayed on the page outside the isolated iframe can be styled normally using the page's own CSS.
Apple Pay
Apple Pay buttons cannot be styled with custom CSS. Their appearance can only be influenced through the ui configuration. This restriction comes directly from Apple rules.
applepay.ui
| Parameter | Type | Allowed values | Default | Description |
|---|---|---|---|---|
type | string | plain, add-money, book, buy, check-out, continue, contribute, donate, order, pay, reload, rent, set-up, subscribe, support, tip, top-up | 'pay' | Text/logo displayed on the button. |
color | string | black, white, white-with-outline | 'black' | Button color variant. |
enableResizeListener | boolean | true, false | true | Enables automatic button adjustment when the container dimensions change. |
style | object | – | – | Button dimensions and rounding — see the table below. |
applepay.ui.style
Via the optional style object, you can add dimensions and rounding:
| Parameter | Type | Default | Description |
|---|---|---|---|
width | number | – | Width in px; if not set, the button fills the container width. |
height | number | – | Height in px; if not set, the button fills the container height. |
borderRadius | number | 4 | Corner rounding in px. |
paddingX | number | 0 | Horizontal padding inside the button in px. |
paddingY | number | 2 | Vertical padding inside the button in px. |
boxSizing | 'border-box' | 'content-box' | 'border-box' | Box model. |
applepay.payment
Use this configuration only if you know for sure that you need to change the default provider for Apple Pay (the default is COMGATE). If you use the standard settings, omit this key and let the SDK choose the provider itself.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
provider | string | Yes (if payment is specified) | 'COMGATE' | Apple Pay processing provider. Allowed: 'COMGATE', 'CSOB'. |
providerId | string | No | – | Provider ID (if it differs from the default). |
applepay.actions
| Parameter | Type | Description |
|---|---|---|
onButtonClick | (moduleId: string) => Promise<void> | Called after clicking the Apple Pay button, before starting the payment. Can be used for cart validation. The payment starts only after the returned Promise resolves. |
Configuration example
applepay: {
mountPoint: '#cg-applepay-box',
ui: {
type: 'pay',
color: 'black',
enableResizeListener: true,
style: {
width: 240,
height: 45,
borderRadius: 6,
paddingX: 12,
paddingY: 10,
boxSizing: 'border-box',
},
},
//payment: {
//provider: 'COMGATE',
//providerId: 'M0MIPS0001', // or '123456'
//},
actions: {
onButtonClick: async (moduleId) => {
console.log('Apple Pay button clicked', moduleId);
// payment starts only after the function completes / Promise resolves
// you can, for example, also stop processing
// more in the documentation for onButtonClick below
},
},
}
Resources
- Online simulator — official tool for testing different configurations of the Apple Pay button
- Marketing Guidelines — marketing guidelines on how to correctly use Apple Pay buttons
- Human Interface Guidelines — guidelines for designing the user interface for Apple Pay integration
Google Pay
Google Pay buttons cannot be styled with custom CSS. Their appearance can only be influenced through the ui configuration. This restriction comes directly from Google rules.
googlepay.ui
| Parameter | Type | Allowed values | Default | Description |
|---|---|---|---|---|
type | string | book, buy, checkout, donate, order, pay, plain, subscribe, short | 'plain' | Text/logo displayed on the button. |
color | string | black, white | 'black' | Button color variant. |
enableResizeListener | boolean | true, false | – | Enables automatic button adjustment when the container dimensions change. |
The value short corresponds to a button with the logo only (without text), buy displays the text „Buy with Google Pay".
googlepay.payment
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
googlePayMerchantId | string | No (test) / Yes (prod) | – | Merchant ID from Google Pay Business Console. Not required for testing, mandatory for production. |
provider | string | No | 'COMGATE' | Google Pay processing provider. Allowed: 'COMGATE', 'CSOB'. |
providerId | string | No | – | Provider ID (if it differs from the default). |
googlepay.actions
| Parameter | Type | Description |
|---|---|---|
onButtonClick | (moduleId: string) => Promise<void> | Called after clicking the Google Pay button, before starting the payment. Can be used for cart validation. The payment starts only after the returned Promise resolves. |
Configuration example
googlepay: {
mountPoint: '#cg-googlepay-box',
ui: {
type: 'pay',
color: 'black',
enableResizeListener: true,
},
payment: {
googlePayMerchantId: 'BCR*******ZAV',
//provider: 'COMGATE',
},
actions: {
onButtonClick: async (moduleId) => {
console.log('Google Pay button clicked', moduleId);
// payment starts only after this Promise resolves
},
},
}
Resources
- Online simulator — official tool for testing different configurations of the Google Pay button
- Brand Guidelines — marketing guidelines on how to correctly use Google Pay buttons
- UX best practices — guidelines for designing the user interface for Google Pay integration
Card form
The SDK automatically inserts default styles for the card container when mounting the module. The styles are inserted at the beginning of <head> as a <style> tag — all selectors are scoped to the container ID, thanks to which there are no collisions with other page styles.
- Styles are inserted on calling
mount()and removed ondestroy(). - The SDK does not use
!important— it is enough to define rules with the same or higher specificity. - CSS variables (
--cg-green,--cg-green-hover,--cg-ibox-h,--cg-btn-h) are scoped under the container ID and can be overridden with your own selector.
SDK styles are inserted at the beginning of <head> (before all other <style> and <link>). Your own CSS with the same specificity will therefore always win due to the later position in the cascade (source order) — regardless of where on the page it is located.
Customizing the default styles
You can override the SDK styles simply with your own CSS with the same or higher specificity:
The condition for modifying styles is to maintain full functionality of all form elements. All elements must remain visible, accessible and usable.
Hiding part of the form, for example using the display: none rule, is considered a violation of Comgate's integration conditions and may lead to termination of the Checkout SDK service.
Example — complete override of all SDK styles
Below is an example of CSS covering all the styles that the SDK inserts. You can choose only the rules you want to override.
/* CSS variables and basic container frame */
#cg-card-box {
/* --cg-green: #2abd4f; */
/* --cg-green-hover: #38c65d; */
/* --cg-ibox-h: 140px; */
/* --cg-btn-h: 44px; */
/* border: 1px solid #f0f0f0; */
/* background: #f2f2f2; */
/* border-radius: 5px; */
/* padding: 15px; */
/* max-width: 450px; */
}
/* Card entry form area */
#cg-card-box-ibox {
/* min-height: var(--cg-ibox-h); */
}
#cg-card-box-ibox iframe {
/* display: block; */
/* width: 100%; */
/* height: var(--cg-ibox-h); */
/* border: none; */
}
/* Card network icons */
#cg-card-box .cg-card-icons {
/* display: flex; */
/* align-items: center; */
/* gap: 6px; */
/* padding: 5px 0; */
}
#cg-card-box .cg-card-icons .cg-card-icon {
/* display: inline-flex; */
/* align-items: center; */
/* justify-content: center; */
/* width: 30px; */
/* height: 19px; */
}
#cg-card-box .cg-card-icons .cg-card-icon img {
/* width: 30px; */
/* height: 19px; */
/* object-fit: contain; */
}
/* Payment submit button */
#cg-card-box-button button {
/* transition: background-color 0.1s ease-in-out; */
/* display: inline-flex; */
/* align-items: center; */
/* justify-content: center; */
/* width: 100%; */
/* height: var(--cg-btn-h); */
/* padding: 14px 10px; */
/* margin-top: 10px; */
/* border: none; */
/* border-radius: 5px; */
/* background: var(--cg-green); */
/* color: #fff; */
/* font-size: 14px; */
/* font-weight: 700; */
/* cursor: pointer; */
}
#cg-card-box-button button:hover {
/* background: var(--cg-green-hover); */
}
/* Lock icon inside the button */
#cg-card-box-button button .cg-button-lock {
/* width: 17px; */
/* flex-shrink: 0; */
/* margin-right: 6px; */
}
/* Check icon inside the button (paid state) */
#cg-card-box-button button .cg-button-tick {
/* width: 14px; */
/* height: 14px; */
/* flex-shrink: 0; */
/* margin-right: 6px; */
}
/* Spinner inside the button (during payment processing) */
#cg-card-box-button button .cg-button-spinner {
/* display: inline-block; */
/* width: 16px; */
/* height: 16px; */
/* margin-right: 8px; */
/* border: 2px solid rgba(255,255,255,0.35); */
/* border-top-color: #fff; */
/* border-radius: 50%; */
/* animation: cg-card-spin .6s linear infinite; */
/* vertical-align: middle; */
}
/* Button — disabled state */
#cg-card-box-button button.disabled-btn,
#cg-card-box-button button.disabled-btn:hover,
#cg-card-box-button button.disabled-btn:active,
#cg-card-box-button button.disabled-btn:focus {
/* opacity: 0.65; */
/* cursor: default; */
/* background: var(--cg-green); */
}
/* Loader in the button area */
#cg-card-box-button .cg-card-button-loader {
/* display: flex; */
/* align-items: center; */
/* justify-content: center; */
/* min-height: var(--cg-btn-h); */
/* padding: 12px 0; */
}
#cg-card-box-button .cg-card-button-loader-spinner {
/* display: inline-block; */
/* width: 24px; */
/* height: 24px; */
/* border: 3px solid rgba(0,0,0,0.15); */
/* border-top-color: var(--cg-green); */
/* border-radius: 50%; */
/* animation: cg-card-spin .6s linear infinite; */
}
/* Form error state */
#cg-card-box .cg-card-error {
/* display: flex; */
/* flex-direction: column; */
/* align-items: center; */
/* justify-content: center; */
/* gap: 10px; */
/* min-height: var(--cg-ibox-h); */
/* padding: 20px 16px; */
/* text-align: center; */
/* color: #555; */
/* font-size: 13px; */
/* line-height: 1.55; */
}
/* Error state icon (SVG via background-image) */
#cg-card-box .cg-card-error::before {
/* content: ''; */
/* display: block; */
/* width: 40px; */
/* height: 40px; */
/* flex-shrink: 0; */
/* background: url("...") center / contain no-repeat; */
}
#cg-card-box .cg-card-error-retry {
/* display: inline-block; */
/* margin-top: 2px; */
/* color: var(--cg-green); */
/* font-size: 13px; */
/* font-weight: 600; */
/* text-decoration: none; */
/* cursor: pointer; */
}
#cg-card-box .cg-card-error-retry:hover {
/* text-decoration: underline; */
}
/* Payment method surcharge */
#cg-card-box .cg-surcharge {
/* margin-top: 8px; */
/* font-size: 14px; */
/* line-height: 1.4; */
}
#cg-card-box .cg-surcharge-info {
/* display: flex; */
/* justify-content: space-between; */
/* align-items: center; */
}
#cg-card-box .cg-surcharge-amount {
/* font-weight: 600; */
/* color: #dc291e; */
}
#cg-card-box .cg-surcharge-toggle {
/* font-size: 14px; */
/* margin-top: 0; */
/* display: inline-block; */
/* color: inherit; */
/* text-decoration: underline; */
/* outline: none; */
}
#cg-card-box .cg-surcharge-detail {
/* font-size: 14px; */
/* color: rgba(0, 0, 0, 0.5); */
/* transition: opacity 0.25s ease, max-height 0.25s ease 0.25s, margin-top 0.25s ease 0.25s; */
/* opacity: 0; */
/* overflow: hidden; */
/* max-height: 0; */
/* margin: 0; */
}
#cg-card-box .cg-surcharge-detail.cg-surcharge-detail--expanded {
/* transition: max-height 0.25s ease, margin-top 0.25s ease, opacity 0.25s ease; */
/* margin-top: 10px; */
/* max-height: 200px; */
/* opacity: 1; */
}
#cg-card-box .cg-surcharge-blocked {
/* color: #c0392b; */
}
#cg-card-box .cg-surcharge-blocked p {
/* margin: 0; */
}
/* Utility — hide element */
#cg-card-box .cg-hidden {
/* display: none !important; */
}
Card form loading state
SDK styles are only inserted after the JavaScript has been downloaded and executed. If you want to show the user a loading spinner immediately after the page renders (before the SDK manages to load), add the is-loading class to the main card container — the SDK automatically removes it after mounting.
Loading styles are optional — without them the container will display as empty space until the SDK loads the form. If you don't mind, you can skip this section.
- In the container
- Outside the container
<div id="cg-card-box" class="is-loading">
<div id="cg-card-box-ibox"></div>
<div id="cg-card-box-button">
<button type="button">Pay</button>
</div>
</div>
<div id="cg-card-box" class="is-loading">
<div id="cg-card-box-ibox"></div>
</div>
<!-- button anywhere else on the page -->
<div id="cg-card-box-button" class="is-loading">
<button type="button">Pay</button>
</div>
CSS for the loading spinner
Insert the following rules into your CSS:
/* Loading state — spinner displayed immediately from HTML, before the SDK loads */
#cg-card-box.is-loading #cg-card-box-ibox {
position: relative;
min-height: 140px;
}
#cg-card-box.is-loading #cg-card-box-ibox::before {
content: '';
position: absolute;
inset: 0;
margin: auto;
width: 25px;
height: 25px;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' stroke='%23888888' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 24 24'%3E%3Crect width='18' height='11' x='3' y='11' rx='2' ry='2'/%3E%3Cpath d='M7 11V7a5 5 0 0 1 10 0v4'/%3E%3Cg transform='translate(0 -.38)'%3E%3Ccircle cx='12.1' cy='16.067' r='.472' style='fill:%23888888;stroke-width:1.25646;'/%3E%3Cpath d='M12.079 16.62h.042v1.342h-.042z' style='fill:%23888888;stroke-width:1.65753;'/%3E%3C/g%3E%3C/svg%3E");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
z-index: 1;
}
#cg-card-box.is-loading #cg-card-box-ibox::after {
content: '';
position: absolute;
inset: 0;
margin: auto;
width: 63px;
height: 63px;
border: 3px solid #e4e4e4;
border-top-color: #999;
border-radius: 50%;
animation: cg-card-spin 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
}
#cg-card-box.is-loading #cg-card-box-button {
display: none;
}
/* Variant: button outside the container */
#cg-card-box-button.is-loading {
display: none;
}
@keyframes cg-card-spin { to { transform: rotate(360deg); } }
Input fields
The form for entering card data runs in a secured iframe, and therefore its customization is exclusively possible via the appearance configuration in the card parameter. The appearance object allows setting colors, fonts, rounding, padding — see the overview below.
Order of style application: default CSS → variables → rules (each layer overrides the previous one).
Main appearance object
| Parameter | Type | Required | Description |
|---|---|---|---|
version | number | Yes | Version of the appearance configuration — increase by 1 on each change. |
variables | object | No | Design attributes (CSS custom properties) — see the table below. |
rules | object | No | Per-element CSS overrides — see the table below. |
appearance.variables — design attributes
The attributes set CSS custom properties on :root inside the iframe. They affect all form elements at once.
| Parameter | Description |
|---|---|
colorText | Text color (inputs, labels). |
colorBackground | Input background color. |
colorPrimary | Primary color — frame on focus, active elements. |
colorDanger | Danger color — frame of an invalid field, error message text. |
colorPlaceholder | Placeholder text color. |
borderColor | Default input frame color. |
borderRadius | Input corner rounding. |
fontFamily | Form font (limited to an allowed allowlist). |
fontSize | Input font size. |
spacing | Input padding and spaces between fields. |
appearance.rules — per-element CSS overrides
Each key in the rules object targets a specific visual element of the form:
| Key | What it styles |
|---|---|
container | Root form container ([data-slot="container"]) |
label | Label above the input ([data-slot="label"]) |
input | Input in the default state ([data-slot="input"]) |
inputFocus | Input in the focus state ([data-slot="input"][data-state="focus"]) |
inputInvalid | Input with an invalid value ([data-slot="input"][data-invalid]) |
error | Error message below the input ([data-slot="error"]) |
Each key accepts an object with allowed CSS properties (SafeStyle):
| CSS property | Type | Example |
|---|---|---|
color | string | '#111827' |
backgroundColor | string | '#ffffff' |
borderColor | string | '#e5e7eb' |
borderRadius | string | '6px' |
fontFamily | string | 'Arial, sans-serif' |
fontSize | string | '14px' |
fontWeight | string | '500' |
lineHeight | string | '1.5' |
letterSpacing | string | '0.5px' |
paddingBlock | string | '10px' |
paddingInline | string | '12px' |
boxShadow | string | '0 0 0 2px rgba(0, 102, 204, 0.25)' |
The SDK internally validates styles and rejects unsafe values. An invalid style is ignored and the SDK writes a warning to the console.
You can set fonts that are included in the operating system (e.g. Arial, Helvetica, sans-serif). You cannot load external fonts using @font-face or from a CDN.
Configuration example
card: {
mountPoint: '#cg-card-box',
appearance: {
version: 1, // increase by 1 on every change
variables: {
colorText: '#111827',
colorBackground: '#ffffff',
colorPrimary: '#0891b2',
colorDanger: '#dc2626',
colorPlaceholder: '#9ca3af',
borderColor: '#e5e7eb',
borderRadius: '4px',
fontFamily: 'Arial, sans-serif',
fontSize: '14px',
spacing: '8px',
},
rules: {
container: { backgroundColor: '#f9fafb' },
label: { color: '#374151', fontWeight: 'bold', fontSize: '12px' },
input: { backgroundColor: '#ffffff', borderColor: '#e5e7eb', color: '#111827', paddingBlock: '6px', paddingInline: '8px' },
inputFocus: { borderColor: '#0891b2' },
inputInvalid: { borderColor: '#dc2626' },
error: { color: '#dc2626', fontSize: '11px' },
},
},
}
Dynamic appearance change at runtime
The appearance of the card form can also be changed after initialization — for example when switching between light and dark mode. Use the setAppearance() method on the Card module instance:
const result = await useCheckout({ /* ... */ });
const instanceCard = result.card?.instance;
// Switch to dark mode
instanceCard.setAppearance({
version: 1, // increase by 1 on every change
variables: {
colorText: '#e5e7eb',
colorBackground: '#2c2c2e',
colorPrimary: '#a1a1aa',
colorDanger: '#f87171',
colorPlaceholder: '#52525b',
borderColor: '#3f3f46',
borderRadius: '4px',
},
rules: {
container: {
backgroundColor: '#0f0f0f',
},
label: {
color: '#71717a',
fontWeight: '500',
},
input: {
backgroundColor: '#2c2c2e',
color: '#e5e7eb',
borderColor: '#3f3f46',
},
inputFocus: {
borderColor: '#a1a1aa',
boxShadow: '0 0 0 2px rgba(161, 161, 170, 0.15)',
},
inputInvalid: {
borderColor: '#f87171',
boxShadow: '0 0 0 2px rgba(248, 113, 113, 0.15)',
},
error: {
color: '#f87171',
},
},
});
The method accepts the same format as appearance in the card configuration. The change takes effect immediately.
Language change
The language of the entire SDK — including button texts and error messages — can be changed at runtime by calling changeLocale() on the Core module instance:
const result = await useCheckout({ /* ... */ });
const instanceCore = result.core?.instance;
// Switch language to English
instanceCore.changeLocale('en');