Match your brand with tokens
Set the accent once through config for the common case, follow the OS or switch themes live from your own toggle, or override the semantic tokens when your design system wants control of every surface.
Appearancetheme.accentColortheme.mode: 'auto'--ks-* tokensdata-ks-theme--ks-toolbar-height
Everything the SDK paints is driven by CSS custom properties. You restyle it by setting those properties, never by overriding its class names, which are not a stable contract and change between releases. Work at the highest layer that solves your problem: overriding
--ks-surface once is better than overriding eight component backgrounds, because it also catches the components added in the next release.1. Just our brand color
Hover, subtle and border shades are derived from it. Nothing else to set. Any CSS color: a hex, a name or one color function; anything else is ignored with a console warning, and the indigo stays.
viewer.tsTypeScript
new PDFViewer(container, doc, { theme: { accentColor: '#e91e63' }, });
2. Follow the OS
'auto' follows the OS through prefers-color-scheme; 'light' (the default) and 'dark' beat the OS in both directions. The mode is applied as a data-ks-theme attribute on the viewer's root element, so two viewers on one page can differ. The document itself never darkens.viewer.tsTypeScript
new PDFViewer(container, doc, { theme: { mode: 'auto' } });
3. Switching theme live
Keep
theme.mode: 'auto' and set the attribute on <html> from your own theme toggle. The viewer and its dialogs switch immediately, with no rebuild. Remove the attribute to follow the OS again.theme-toggle.tsTypeScript
document.documentElement.dataset.ksTheme = isDark ? 'dark' : 'light';
4. Our whole design system
Override the semantic tokens once; toolbar, ribbon, panels, dialogs, popovers and whatever is added next release follow. A value set on
:root reaches the viewer and the surfaces that render into document.body (the signature dialog, the security modal, every popover); a value on your own container reaches the viewer inside it. Your stylesheet loads after the SDK's, so an override at equal specificity wins without !important.theme.cssCSS
:root { --ks-accent: #e91e63; --ks-accent-hover: #c2185b; --ks-accent-subtle: #fce4ec; --ks-accent-text: #ffffff; --ks-surface: #ffffff; --ks-surface-raised: #fafafa; --ks-surface-sunken: #f1f1f4; --ks-text: #1a1a2e; --ks-text-muted: #6b6b80; --ks-border: #e4e4ec; --ks-font: 'Inter', system-ui, sans-serif; --ks-radius: 4px; --ks-radius-sm: 3px; } /* One viewer instance, if you host several. */ #invoice-viewer { --ks-accent: #e91e63; }
With the default
mode: 'light' these values apply wherever the page around the viewer is light; with mode: 'auto' they apply in both themes.5. A dark theme of your own
Leave
theme.mode at 'auto' and redefine the tokens under both selectors: the first for a page that sets data-ks-theme="dark" on <html> itself, the second for a dark OS. An explicit theme.mode: 'dark' uses the SDK's own dark palette; your accentColor still applies.theme.cssCSS
:root[data-ks-theme='dark'] { --ks-surface: #11131a; --ks-surface-raised: #171a22; --ks-surface-sunken: #0c0e13; --ks-text: #e8e8f0; --ks-text-muted: #9a9ab0; --ks-border: #252833; --ks-accent: #f48fb1; /* lighter in dark — contrast, not hue */ --ks-accent-text: #11131a; --ks-canvas-bg: #07080b; } @media (prefers-color-scheme: dark) { :root:not([data-ks-theme='light']) { /* same block */ } }
6. A denser toolbar
Component tokens each fall back to a semantic token, so one part can be retuned without redefining the system. In a compact or narrow viewer the toolbar uses its own smaller heights.
theme.cssCSS
:root { --ks-toolbar-height: 56px; --ks-space-2: 6px; }
Gotchas
- Primitives (the raw color ramp) are not part of the public contract and may change in any release. The semantic tokens are the contract.
- Three things in the markup are kept across releases: the tokens (
--ks-*), thedata-ks-*attributes on the viewer element, and the structural classes (.ks-viewer,.ks-toolbar,.ks-nav-bar,.ks-body,.ks-sidebar,.ks-page-container,.ks-page,.ks-portal,.ks-dialog-backdrop,.ks-dialog,.ks-toast,.ks-loading-overlay,.ks-busy-overlay), for placing, sizing and hiding a region, not for repainting it. Every other class is internal. - Dialogs, sheets and popovers live in one portal layer shared by the whole page. With several viewers in different themes or accents, they take the theme of the viewer created last. To style them directly, target
.ks-portal. - The
ksprefix is fixed and not configurable.