Customization
This guide covers theming, CSS variable overrides, toolbar configuration, feature flags, and worker setup.
Theme Modes
VerifyKit supports three theme modes: 'light', 'dark', and 'system'.
React
Set the theme via the VerifyKitProvider config:
<VerifyKitProvider config={{
workerUrl: 'https://unpkg.com/pdfjs-dist@5.5.207/legacy/build/pdf.worker.min.mjs',
theme: { mode: 'system' },
}}>
<App />
</VerifyKitProvider>Toggle the theme at runtime using the defaultLayoutPlugin API:
const [layout] = useState(() => defaultLayoutPlugin())
layout.theme.toggleTheme() // Toggle between light and dark
layout.theme.setTheme('dark') // Set explicitlyVanilla JS
Set the theme at creation time or change it later:
const viewer = VerifyKit.create(el, {
workerUrl: 'https://unpkg.com/pdfjs-dist@5.5.207/legacy/build/pdf.worker.min.mjs',
theme: { mode: 'system' },
})
// Change at runtime
viewer.setTheme('dark')Listen for theme changes:
viewer.on('themeChange', (mode) => {
console.log('Theme changed to:', mode)
})CSS Variables
VerifyKit's appearance is controlled by CSS custom properties defined on :root. You can override any variable to customize the look and feel.
Overriding via Provider Config
Pass CSS variable overrides through the theme configuration:
<VerifyKitProvider config={{
workerUrl: 'https://unpkg.com/pdfjs-dist@5.5.207/legacy/build/pdf.worker.min.mjs',
theme: {
mode: 'dark',
overrides: {
'--primary': '#6366f1',
'--primary-hover': '#4f46e5',
},
},
}}>Overriding via CSS
You can also override variables in your own stylesheet:
:root {
--primary: #6366f1;
--primary-hover: #4f46e5;
--toolbar-bg: #1a1a2e;
}Key Variable Categories
Layout
| Variable | Default | Description |
|---|---|---|
--btn-base | 26px | Base button size, before --ui-scale |
--toolbar-h-base | 36px | Base toolbar height, before --ui-scale |
--btn-size | calc(var(--btn-base) * var(--ui-scale, 1)) | Standard button size |
--toolbar-height | calc(var(--toolbar-h-base) * var(--ui-scale, 1)) | Height of the toolbar |
--left-nav-width | 36px | Width of the left navigation bar |
--btn-size and --toolbar-height are derived — override --btn-base and
--toolbar-h-base instead, and both the button and the toolbar stay in
proportion under --ui-scale. The two bases respond to the environment:
| Context | --btn-base | --toolbar-h-base |
|---|---|---|
| Default | 26px | 36px |
(pointer: coarse) — touch | 40px | 48px |
(max-height: 500px) — short viewport | 24px | 32px |
| Short and coarse | 36px | 42px |
Colors — Surfaces
| Variable | Light | Dark | Description |
|---|---|---|---|
--surface-1 | #ffffff | #1e1e1e | Primary surface background |
--surface-2 | #f5f5f5 | #252525 | Secondary surface background |
--surface-3 | #ececec | #2d2d2d | Tertiary surface background |
--doc-area-bg | #e8e8e8 | #383838 | Document area background |
Colors — Typography
| Variable | Light | Dark | Description |
|---|---|---|---|
--fg | #1a1a1a | #e0e0e0 | Primary text color |
--fg-secondary | #333333 | #cccccc | Secondary text color |
--fg-muted | #595959 | #999999 | Muted text color |
--fg-subtle | #767676 | #666666 | Subtle text color |
Accent / Interactive
| Variable | Light | Dark | Description |
|---|---|---|---|
--primary | #0072c6 | #0072c6 | Primary accent color |
--primary-hover | #005ea3 | #005ea3 | Accent hover state |
--primary-light | #e5f1fc | rgba(0,114,198,0.20) | Light accent tint |
--selected-bg | #ddeafa | rgba(0,114,198,0.25) | Selected item background |
--hover-bg | #eff5fe | rgba(255,255,255,0.07) | Hover background |
--focus-ring | rgba(0,114,198,0.45) | -- | Focus ring color |
Borders
| Variable | Light | Dark | Description |
|---|---|---|---|
--border | #d4d4d4 | #3c3c3c | Standard border |
--border-strong | #b5b5b5 | #555555 | Stronger border |
Toolbar
| Variable | Light | Dark | Description |
|---|---|---|---|
--toolbar-bg | #ffffff | #1d1d1d | Toolbar background |
--toolbar-border | #e0e0e0 | #0d0d0d | Toolbar border |
--toolbar-text | #555555 | rgba(255,255,255,0.55) | Toolbar text |
--toolbar-text-active | #0072c6 | rgba(255,255,255,0.95) | Active toolbar text |
--toolbar-hover | #f0f0f0 | rgba(255,255,255,0.08) | Toolbar hover |
Shadows
| Variable | Default (Light) | Description |
|---|---|---|
--shadow-xs | 0 1px 2px rgba(0,0,0,0.05) | Extra small shadow |
--shadow-sm | 0 1px 3px rgba(0,0,0,0.08), 0 1px 2px rgba(0,0,0,0.04) | Small shadow |
--shadow-md | 0 4px 12px rgba(0,0,0,0.10), 0 2px 4px rgba(0,0,0,0.06) | Medium shadow |
--shadow-lg | 0 8px 28px rgba(0,0,0,0.14), 0 4px 10px rgba(0,0,0,0.08) | Large shadow |
--shadow-xl | 0 20px 48px rgba(0,0,0,0.18), 0 8px 16px rgba(0,0,0,0.10) | Extra large shadow |
--shadow-page | 0 1px 4px rgba(0,0,0,0.12), 0 2px 8px rgba(0,0,0,0.08) | Page shadow |
Semantic Status Colors
| Variable | Light | Dark | Description |
|---|---|---|---|
--color-valid | #0d6b2c | #4ade80 | Valid/success color |
--color-invalid | #b83016 | #f87171 | Invalid/error color |
--color-warning | #975300 | #fbbf24 | Warning color |
--color-unknown | #5a6370 | #9ca3af | Unknown/neutral color |
Status Surfaces (used by signature panels and banners)
| Variable | Description |
|---|---|
--surface-valid | Background for valid status banners |
--surface-invalid | Background for invalid status banners |
--surface-warning | Background for warning/unknown status banners |
--surface-valid-border | Left stripe for valid signature cards |
--surface-invalid-border | Left stripe for invalid signature cards |
--surface-warning-border | Left stripe for warning/unknown signature cards |
--bar-valid-bg | Document message bar — valid |
--bar-invalid-bg | Document message bar — invalid |
--bar-warn-bg | Document message bar — warning |
--bar-unknown-bg | Document message bar — unknown |
Panels, Tabs and Chrome
| Variable | Light | Dark | Description |
|---|---|---|---|
--panel-bg | #f2f2f2 | #1e1e1e | Sidebar / signature panel background |
--panel-header-bg | #e2e2e2 | #252525 | Panel header strip |
--panel-border | #b8b8b8 | #3c3c3c | Panel border |
--tabbar-bg | #e8e8e8 | #2a2a2a | Multi-document tab strip background |
--tabbar-border | #d0d0d0 | #111111 | Tab strip border |
--tabbar-text | rgba(0,0,0,0.68) | rgba(255,255,255,0.68) | Tab label |
--statusbar-bg | #f3f3f3 | #1d1d1d | Bottom status bar background |
--statusbar-text | rgba(0,0,0,0.60) | rgba(255,255,255,0.38) | Status bar text |
--statusbar-text-dim | rgba(0,0,0,0.45) | rgba(255,255,255,0.28) | Status bar secondary text |
--tooltip-bg | rgba(10,10,10,0.92) | rgba(50,50,50,0.94) | Tooltip background |
--tooltip-text | #fafafa | #f0f0f0 | Tooltip text |
--selected-border | #0072c6 | #0072c6 | Border of a selected item |
Welcome Screen
Shown before a document is open, so these are the first colors a new user sees. Most default to other tokens — override those and the welcome screen follows.
| Variable | Default | Description |
|---|---|---|
--welcome-bg | var(--surface-1) | Welcome screen background |
--welcome-border | var(--border) | Drop-zone border |
--welcome-title | var(--fg) | Headline text |
--welcome-subtitle | var(--fg-muted) | Supporting text |
--welcome-icon | var(--fg-subtle) | Drop-zone icon |
--welcome-drop-bg | rgba(0,114,198,0.04) (dark 0.08) | Drop-zone fill while dragging |
--welcome-drop-border | rgba(0,114,198,0.30) (dark 0.40) | Drop-zone border while dragging |
Toolbar Configuration
React — defaultLayoutPlugin
Disable specific toolbar features using the disable option:
const [layout] = useState(() =>
defaultLayoutPlugin({
disable: {
search: true,
print: true,
download: true,
rotation: true,
fullscreen: true,
theme: true,
selection: true,
openFile: true,
contextMenu: true,
properties: true,
shortcuts: true,
},
})
)React — Individual Plugins
For maximum control, compose only the plugins you want:
import { zoomPlugin, pageNavigationPlugin, toolbarPlugin } from '@trexolab/verifykit-react'
const [zoom] = useState(() => zoomPlugin())
const [nav] = useState(() => pageNavigationPlugin())
const [toolbar] = useState(() => toolbarPlugin())
<Viewer fileBuffer={buffer} plugins={[zoom, nav, toolbar]} />This gives you a minimal viewer with only zoom and page navigation -- no search, no print, no sidebar.
Zoom range
By default the viewer zooms between 25 % and 1000 % in 15 % increments — matching pdf.js and suitable for inspecting fine detail in signature stamps, seals and small print.
Override the range via the zoom option on defaultLayoutPlugin (batteries-included) or by passing options directly to zoomPlugin():
// Batteries-included — default layout
const layout = defaultLayoutPlugin({
zoom: { minScale: 0.5, maxScale: 4, step: 0.1 },
})
// À la carte — individual plugin
const [zoom] = useState(() => zoomPlugin({ minScale: 0.25, maxScale: 16 }))The zoom dropdown only shows preset levels that fit inside the configured range, so there are never options the user cannot actually select. Ctrl+= / Ctrl+- and Ctrl+scroll respect the same bounds.
Vanilla exposes the same option:
VerifyKit.create(el, {
workerUrl: '…',
zoom: { maxScale: 16 },
})Feature Flags
Vanilla JS
The vanilla API exposes features as a flat configuration object. Each feature can be individually toggled:
const viewer = VerifyKit.create(el, {
workerUrl: 'https://unpkg.com/pdfjs-dist@5.5.207/legacy/build/pdf.worker.min.mjs',
features: {
search: true, // default: true
print: true, // default: true
download: false, // default: false
signatures: true, // default: true
thumbnails: true, // default: true
fullscreen: true, // default: true
themeToggle: true, // default: true
openFile: false, // default: false
rotation: true, // default: true
contextMenu: true, // default: true
properties: true, // default: true
shortcuts: true, // default: true
highlights: true, // default: true
},
})Internally, feature flags are mapped to defaultLayoutPlugin({ disable: {...} }).
Accessibility
UI Font Scale
VerifyKit includes a built-in UI font scale control that lets users adjust the size of all viewer text, toolbar buttons, and panel elements. This is an accessibility feature similar to browser zoom, but scoped to the viewer only.
A toolbar dropdown (Aa icon) is included by default. Users can choose from four presets:
| Scale | Multiplier | Base Font | Toolbar Height | Button Size |
|---|---|---|---|---|
| Compact | 0.88x | 11.4px | 31.7px | 22.9px |
| Default | 1.00x | 13.0px | 36.0px | 26.0px |
| Large | 1.15x | 15.0px | 41.4px | 29.9px |
| Extra Large | 1.30x | 16.9px | 46.8px | 33.8px |
The user's preference is automatically saved to localStorage and restored on subsequent visits.
React
// Set initial scale via defaultLayoutPlugin
const [layout] = useState(() =>
defaultLayoutPlugin({
accessibility: { initialScale: 'large' },
})
)
// Programmatic control
layout.accessibility.setFontScale('extra-large')
layout.accessibility.getFontScale() // 'extra-large'
// Disable the feature entirely
defaultLayoutPlugin({
disable: { accessibility: true },
})Vanilla JS
const viewer = VerifyKit.create(el, {
workerUrl: '...',
features: { accessibility: true }, // default: true
})
viewer.setFontScale('large')
viewer.getFontScale() // 'large'
// Listen for changes
viewer.on('fontScaleChange', (scale) => {
console.log('Font scale changed to:', scale)
})Standalone Plugin
import { accessibilityPlugin } from '@trexolab/verifykit-react'
const a11y = accessibilityPlugin({
initialScale: 'large', // 'compact' | 'default' | 'large' | 'extra-large'
persist: true, // save to localStorage (default: true)
})
<Viewer plugins={[a11y, ...otherPlugins]} />
// API
a11y.api.setFontScale('extra-large')
a11y.api.getFontScale()CSS Variables
The font scale works by setting a data-font-scale attribute on .verifykit-root and overriding the CSS design token variables. You can customize the scale factors with CSS:
/* Custom scale overrides */
.verifykit-root[data-font-scale="large"] {
--ui-scale: 1.20; /* adjust from default 1.15 */
--font-base: calc(13px * 1.20);
}--btn-size and --toolbar-height are calc(base * var(--ui-scale)), so they
follow --ui-scale on their own — you only need to restate the font sizes, which
do not derive from it.
Worker Configuration
VerifyKit uses a Web Worker to render PDF pages off the main thread. The workerUrl option is required -- it specifies the URL to the PDF.js worker script.
CDN (Recommended)
Use the legacy worker from unpkg:
React:
<VerifyKitProvider config={{
workerUrl: 'https://unpkg.com/pdfjs-dist@5.5.207/legacy/build/pdf.worker.min.mjs',
}}>Vanilla JS:
const viewer = VerifyKit.create(el, {
workerUrl: 'https://unpkg.com/pdfjs-dist@5.5.207/legacy/build/pdf.worker.min.mjs',
})Self-Hosted Worker
Copy the worker file to your public directory and pass a local path:
npm pack pdfjs-dist@5.5.207 --pack-destination /tmp \
&& tar -xzOf /tmp/pdfjs-dist-5.5.207.tgz package/legacy/build/pdf.worker.min.mjs \
> public/pdf.worker.min.mjsReact:
<VerifyKitProvider config={{ workerUrl: '/pdf.worker.min.mjs' }}>Vanilla JS:
const viewer = VerifyKit.create(el, {
workerUrl: '/pdf.worker.min.mjs',
})Important: The worker version must match the pdfjs-dist version used by VerifyKit (5.5.207). Using a mismatched version will cause rendering errors.
CMap and Standard Font URLs
For non-Latin text support (CJK, Arabic, Hebrew, etc.), VerifyKit loads CMap and font files at runtime. Copy them to your public directory:
cp -r node_modules/@trexolab/verifykit-react/cmaps public/cmaps
cp -r node_modules/@trexolab/verifykit-react/standard_fonts public/standard_fontsCustomize the paths if you serve them from a different location:
<VerifyKitProvider config={{
workerUrl: 'https://unpkg.com/pdfjs-dist@5.5.207/legacy/build/pdf.worker.min.mjs',
cMapUrl: '/assets/cmaps/',
standardFontDataUrl: '/assets/standard_fonts/',
}}>The defaults are /cmaps/ and /standard_fonts/.
Content Security Policy
If your application uses a strict CSP, you may need to add these directives:
Content-Security-Policy:
worker-src 'self' https://unpkg.com;
script-src 'self' 'wasm-unsafe-eval';
worker-src 'self' https://unpkg.com-- Required if using the CDN-hosted worker. If self-hosting the worker,worker-src 'self'is sufficient.script-src 'wasm-unsafe-eval'-- Required for WebAssembly compilation.
Signature Appearance Icons
After verification, VerifyKit draws the status icon on the signature field itself, inside the PDF canvas -- a green checkmark for valid, a red cross for invalid, a yellow question mark for unknown -- the same place Acrobat draws it. The icon reflects the verdict the signature actually got, not the placeholder the signing tool left in the file.
This applies to the signature appearance styles produced by Acrobat and the common signing tools. Where a field cannot carry a status icon, VerifyKit leaves it as it is rather than drawing something misleading over it; see Troubleshooting if an icon does not change.
The icon artwork is fixed in the build. If you need a custom icon set for a white-labelled deployment, get in touch.
import { swapSignatureAppearances, hasAcro6Appearances } from '@trexolab/verifykit-react'
// After verification
const result = await verifier.verify(pdfBytes)
const hasAcro6 = await hasAcro6Appearances(pdfBytes)
if (hasAcro6 || result.signatures.some(s => s.isVisible)) {
const modifiedPdf = await swapSignatureAppearances(pdfBytes, result.signatures)
// Use modifiedPdf for display in PDF.js
}Keyboard Shortcuts
VerifyKit includes built-in keyboard shortcuts for navigation, zoom, tools, and document actions.
Built-in Shortcuts
| Category | Shortcut | Action |
|---|---|---|
| Navigation | PageUp / P / K | Previous page |
PageDown / N / J | Next page | |
Home | First page | |
End | Last page | |
| Zoom | Ctrl+= | Zoom in |
Ctrl+- | Zoom out | |
Ctrl+0 | Fit page | |
Ctrl+1 | Actual size (100%) | |
Ctrl+2 | Fit width | |
| Tools | H | Toggle hand tool |
S | Toggle text select | |
| View | R | Rotate clockwise |
Shift+R | Rotate counter-clockwise | |
F5 | Toggle fullscreen | |
| Search | Ctrl+F | Toggle find bar |
F3 | Find next | |
| Actions | Ctrl+S | Download PDF |
Ctrl+P | ||
Ctrl+O | Open file | |
| Help | ? | Show keyboard shortcuts |
Tool shortcuts (H, S, Ctrl+F, F5) are toggles — press once to activate, press again to deactivate.
Registering Custom Shortcuts
Plugins can register additional shortcuts via the ViewerPluginContext:
function myPlugin(): ViewerPlugin {
return {
name: 'my-plugin',
install(ctx) {
ctx.registerShortcut({
id: 'my-action',
key: 'g',
ctrl: true,
description: 'Go to page',
handler(e) {
e.preventDefault()
// your action
},
})
},
}
}