Theming & Styling
The Incode Web SDK uses CSS custom properties (variables) for theming, making it easy to match your brand.
This guide is specific to Web SDK 2.0. If you are still using 1.x, you can find documentation here. Contact your Incode Representative for upgrade information and check if you are a candidate for this upgrade.
Full rollout to all clients still TBD.
The Incode Web SDK uses CSS custom properties (variables) for theming, making it easy to match your brand.
To replace a built-in branded illustration or animation (tutorial graphics, success/fail screens) instead of recoloring it, see Asset Overrides.
Quick Start
Import the base tokens and override the variables you want to customize:
/* Import the SDK theme (defines all CSS variables you can override) */
@import '@incodetech/web/themes/light.css';
/* Override with your brand color — easiest path */
:root {
--primitive-color-brand-500: #0066cc;
--primitive-color-brand-400: #1f7fdf; /* slightly lighter, used on hover */
--primitive-color-brand-600: #0052a3; /* slightly darker, used on press */
}Note: The SDK ships two themes —
@incodetech/web/themes/light.cssand@incodetech/web/themes/dark.css. Pick one as your base, then override variables to match your brand. Component layout/structure CSS lives separately in@incodetech/web/base.cssand per-module CSS such as@incodetech/web/flow/styles.css. The full token catalog is documented below.
Token Hierarchy
The SDK uses a layered token system:
Primitives → Semantic → Component-specific
Each layer references the layer above. Most overrides target the primitive or component layer; touching the semantic layer cascades broadly so use it intentionally.
1. Primitive Tokens
Concrete values that nothing else customizes:
:root {
/* Color scales (50, 100, 200, ..., 900, 1000) for: gray, brand, brand-secondary, positive, warning, negative */
--primitive-color-gray-0: #ffffff;
--primitive-color-gray-500: #6b7280;
--primitive-color-gray-900: #1f2937;
--primitive-color-brand-500: #0066cc;
/* Spacing scale (--primitive-scale-*) and the friendly --spacing-* aliases */
--primitive-scale-4: 4px;
--spacing-4: var(--primitive-scale-4);
--spacing-16: var(--primitive-scale-16);
/* Typography */
--primitive-typography-family-rethink-sans: 'Rethink Sans';
--primitive-typography-size-16: 16px;
--primitive-typography-weight-regular: 400;
--primitive-typography-weight-medium: 500;
/* Border radius scale (--primitive-border-radius-0/4/8/16/24/40/64/9999) and aliases */
--border-radius-medium: var(--primitive-border-radius-16);
--border-radius-full: var(--primitive-border-radius-9999);
}2. Semantic Tokens
Purpose-based names that reference primitives. Different values resolve in @incodetech/web/themes/light.css vs. dark.css, so overriding a semantic token affects whichever theme you're on:
:root {
/* Surfaces — backgrounds */
--surface-neutral-0: var(--primitive-color-gray-0); /* base background */
--surface-neutral-50: var(
--primitive-color-gray-50
); /* subtle elevated surface */
--surface-neutral-900: var(--primitive-color-gray-900); /* highest contrast */
--surface-brand-500: var(--primitive-color-brand-500); /* brand fill */
--surface-status-negative-500: var(--primitive-color-negative-500);
--surface-status-warning-500: var(--primitive-color-warning-500);
--surface-status-positive-500: var(--primitive-color-positive-500);
/* Text — foreground colors */
--text-body-800-primary: var(
--primitive-color-gray-800
); /* primary body text */
--text-body-500-secondary: var(
--primitive-color-gray-500
); /* secondary / muted */
--text-body-400: var(--primitive-color-gray-400); /* placeholders, disabled */
--text-link-default: var(--primitive-color-brand-500);
--text-link-hover: var(--primitive-color-brand-300);
--text-accent-brand: var(--primitive-color-brand-500);
/* Borders */
--border-neutral-100: var(--primitive-color-gray-100);
--border-neutral-400: var(--primitive-color-gray-400);
--border-status-negative: var(--primitive-color-negative-500);
--border-status-focus: var(--primitive-color-brand-500);
}Many tokens have a -static variant that stays fixed across light/dark themes (used for elements that should look identical regardless of theme, like a brand-coloured button). For example --surface-brand-500-static does not switch in dark mode the way --surface-brand-500 might.
3. Component Tokens
Specific to individual components. These reference semantic tokens, so overriding a component token gives you targeted control without affecting other components:
:root {
/* Primary button */
--button-primary-surface-default: var(--surface-brand-500-static);
--button-primary-surface-hover: var(
--surface-brand-400-static
); /* lighter on hover */
--button-primary-surface-pressed: var(
--surface-brand-600-static
); /* darker on press */
--button-primary-surface-disabled: var(--surface-neutral-100);
--button-primary-text-default: var(--text-body-0-static);
--button-primary-border-radius: var(--border-radius-medium);
/* Inputs */
--input-surface-default: var(--surface-neutral-50);
--input-surface-disabled: var(--surface-neutral-100);
--input-border-default: var(--border-neutral-400);
--input-border-focused: var(--border-status-focus);
--input-border-negative: var(--border-status-negative);
--input-text-field-default: var(--text-body-800-primary);
--input-text-field-placeholder: var(--text-body-400);
/* Spinner */
--spinner-surface-primary: var(--surface-brand-500-static);
--spinner-surface-secondary: var(--surface-brand-50);
--spinner-text-title: var(--text-body-800-primary);
--spinner-text-subtitle: var(--text-body-500-secondary);
}button-* tokens come in primary, secondary, and tertiary variants, each with surface, text, and border properties across default / hover / pressed / disabled states.
Common Customizations
Brand colors
The simplest path is to override the primitive brand scale. Everything semantic (--surface-brand-*, --text-accent-brand, button colors, focus rings, etc.) inherits automatically:
:root {
--primitive-color-brand-50: #e6f0ff;
--primitive-color-brand-100: #cce0ff;
--primitive-color-brand-300: #4d9aff;
--primitive-color-brand-400: #1f7fdf; /* button hover */
--primitive-color-brand-500: #0066cc; /* main brand */
--primitive-color-brand-600: #0052a3; /* button pressed */
}If you need finer control, override the component tokens directly:
:root {
--button-primary-surface-default: #0066cc;
--button-primary-surface-hover: #1f7fdf;
--button-primary-surface-pressed: #0052a3;
}Dark mode
The SDK already ships a dark theme — import it instead of rolling your own:
@import '@incodetech/web/themes/dark.css';If you want to switch themes per user preference, gate the import or apply it conditionally:
/* Default to light, switch to dark when the user prefers dark */
@import '@incodetech/web/themes/light.css';
@media (prefers-color-scheme: dark) {
@import '@incodetech/web/themes/dark.css';
}Typography
The SDK uses Rethink Sans by default. Override the primitive family if you want a different font everywhere:
:root {
--primitive-typography-family-rethink-sans:
'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
}Sizes (--primitive-typography-size-12 through --primitive-typography-size-80) and weights (--primitive-typography-weight-regular, --primitive-typography-weight-medium, --primitive-typography-weight-semibold, --primitive-typography-weight-bold) are also override-able primitive tokens.
Border radius
The radius scale is --border-radius-{none,x-small,small,medium,large,x-large,xx-large,full}. Components reference these via component tokens:
:root {
/* Round all primary buttons more aggressively */
--button-primary-border-radius: var(--border-radius-large);
/* Or change the underlying scale */
--primitive-border-radius-16: 12px; /* shrinks --border-radius-medium */
}Container Styling
The SDK elements fill their container. Style the parent for layout control:
<!-- Full viewport height -->
<div style="height: 100vh;">
<incode-flow></incode-flow>
</div>
<!-- Fixed height modal -->
<div style="height: 600px; width: 400px;">
<incode-flow></incode-flow>
</div>Direct CSS Overrides
CSS variables cover color, typography, spacing, and border radius. Some properties — text alignment, for example — have no dedicated token, because most integrations never need to change them. For those, override the component's CSS class directly.
The SDK's web components render without Shadow DOM: every custom element registers with shadow: false, so your page's CSS cascades into components exactly as it cascades into any other element on the page. There's no encapsulation boundary to work around.
Every component also renders plain, Incode-prefixed CSS classes on its root and key child elements — for example .IncodeButton, .IncodeInput, .IncodeFlowCompletedTitle. These class names are stable across releases, so your stylesheet can target them directly.
Matching selector specificity
Some classes are flat and override with a single selector:
.IncodeFlowStartSubtitle {
text-align: right;
}Others are nested under an ancestor, or qualified with a tag name, for extra specificity. Match or exceed that specificity in your override, or fall back to !important:
/* Overrides .IncodeComponent .IncodeFlowCompleted h1.IncodeFlowCompletedTitle */
.IncodeComponent .IncodeFlowCompleted h1.IncodeFlowCompletedTitle,
.IncodeComponent .IncodeFlowCompleted p.IncodeFlowCompletedSubtitle {
text-align: right;
}Note: Some styles switch at a breakpoint.
IncodeFlowCompletedTitleleft-aligns by default and centers above 992px width, for example. Override the base rule and any matching@mediarule together, so your alignment holds at every screen size.
Common classes to target
This is a starting point, not a full inventory — inspect the rendered element in your browser's devtools for anything not listed here.
| Component | Root class | Notable child classes |
|---|---|---|
| Button | .IncodeButton | .IncodeButtonPrimary / Secondary / Link, .IncodeButtonL / M / S, .IncodeButtonLoading |
| Input | .IncodeInput | State reflected via [aria-invalid], :disabled, [type='date'] |
| Typography | .IncodeTypographyH1–H5, .IncodeTypographyP | Nested under .IncodeComponent |
| Flow start screen | .IncodeFlowStart | .IncodeFlowStartSubtitle, .IncodeFlowStartLogo |
| Flow completed screen | .IncodeFlowCompleted | .IncodeFlowCompletedTitle, .IncodeFlowCompletedSubtitle |
| Spinner | .IncodeSpinner | See Spinner Customization below |
Global UI Config
CSS variables and class overrides change how components look. setUiConfig changes what they show, for branding elements that aren't worth a CSS override — the logo, the subtitle text, and whether the default header or footer renders at all.
Call it directly, or pass the same shape to setup({ uiConfig }):
import { setUiConfig } from '@incodetech/web';
setUiConfig({
logoSrc: 'https://your-cdn.com/logo.svg',
logoHeight: '32px',
subtitle: 'Verify your identity to continue',
hideHeader: false,
hideFooterBranding: true,
});| Option | Type | Description |
|---|---|---|
logoSrc | string | Replaces the default Incode logo. |
logoHeight | string | Sets the rendered height of logoSrc (a CSS length, e.g. '32px'). |
subtitle | string | Replaces the default subtitle text under the logo. |
hideHeader | boolean | Hides the default header entirely. |
hideFooterBranding | boolean | Hides the default "Powered by Incode" footer branding. |
All options are optional — pass only the ones you want to change.
Using
<incode-flow>or<incode-workflow>? By default, the orchestrator fetches your Incode dashboard's theme once it initializes and applies it with this same call, which replaces the entire config rather than merging into it. That fetch overwriteslogoSrc,subtitle, andhideFooterBrandingwith the dashboard's values, and clearshideHeader/logoHeightback to unset, even if you set them first. SetdisableDashboardTheme: truein the flow or workflow config (see IncodeFlow Component) to keep your ownsetUiConfigvalues, or set the logo and subtitle on the dashboard instead.
Spinner Customization
This follows the same override pattern described in Direct CSS Overrides above. The loading spinner displayed during flow initialization, module loading, and transitions exposes four tokens:
:root {
/* Spinner icon colors */
--spinner-surface-primary: #0066cc; /* foreground arc */
--spinner-surface-secondary: #e6f0ff; /* track */
/* Spinner text */
--spinner-text-title: #1a1a1a;
--spinner-text-subtitle: #666666;
}The full-screen overlay background uses --surface-neutral-0 from the active theme — override that primitive (or the corresponding --primitive-color-gray-0) if you want a different overlay color, rather than reaching for a dedicated token (there isn't one).
Spinner Sizes
The spinner component supports three sizes that you can configure via the spinnerConfig prop:
| Size | Icon Dimension |
|---|---|
small | 24px |
medium | 48px (default) |
large | 64px |
CSS Class Targeting
You can also target spinner classes directly:
/* Main spinner container */
.IncodeSpinner {
/* your styles */
}
/* Full-screen spinner overlay */
.IncodeSpinnerFullScreen {
/* your styles */
}
/* Spinner content wrapper */
.IncodeSpinnerContent {
/* your styles */
}
/* Wrapper around title + subtitle */
.IncodeSpinnerText {
/* your styles */
}
/* Title and subtitle text */
.IncodeSpinnerTitle {
/* your styles */
}
.IncodeSpinnerSubtitle {
/* your styles */
}Key CSS Variables
A high-level cheat sheet of the categories you'll most often override. The full inventory ships in @incodetech/web/themes/light.css and @incodetech/web/themes/dark.css — inspect them in your browser's devtools or your bundler's source view to see every variable.
| Category | Examples |
|---|---|
| Brand | --primitive-color-brand-{50..600} (override these to re-skin everything) |
| Surfaces | --surface-neutral-{0..1000}, --surface-brand-{50,100,500,500-static,600-static}, --surface-status-{negative,warning,positive}-{50,500} |
| Text | --text-body-{0,400,500-secondary,800-primary}, --text-link-{default,hover,visited,disabled}, --text-accent-brand, --text-status-{negative,warning,positive} |
| Borders | --border-neutral-{100,400}, --border-status-{negative,warning,positive,focus} |
| Buttons | --button-{primary,secondary,tertiary}-{surface,text,border}-{default,hover,pressed,disabled}, --button-*-border-radius |
| Inputs | --input-{surface,border,icon,text-field,text-helper,text-label}-{default,focused,disabled,negative} |
| Spinner | --spinner-surface-{primary,secondary}, --spinner-text-{title,subtitle} |
| Layout | --spacing-{2..96}, --border-radius-{none,x-small,small,medium,large,x-large,xx-large,full} |
| Type | --primitive-typography-family-*, --primitive-typography-size-{12..80}, --primitive-typography-weight-{regular,medium,semibold,bold} |
Updated 1 day ago
