Customizing the theme of our JavaScript PDF viewer
The [dark theme][] guide mentions the different theme presets available in Nutrient Web SDK. Starting with Nutrient Web SDK 1.5, you can build your theme and use it in the Web Viewer. In addition to the presets we already provide, you can now create your own theme.
Minimal theme starter
If you’re just getting started with theming, here’s a minimal example that changes only the primary colors. You’ll still need to provide the complete theme object, but this shows the essential parts to customize:
import NutrientViewer from "@nutrient-sdk/viewer";
// Start with the default light theme and modify specific values.const minimalCustomTheme = { // ... full theme object required (see complete example below) // Key sections to customize for quick branding: // - color.background.primary: Main background colors // - color.background.interactive: Button/link colors // - color.text.primary: Main text color // - color.border: Border colors};
const container = document.getElementById("nutrient-container");
NutrientViewer.load({ container, document: "document.pdf", theme: minimalCustomTheme,}).catch((error) => { console.error("Failed to load viewer:", error);});You cannot pass a partial theme object. You must provide the complete theme object, including properties you don’t want to change. Copy the full example below and modify only what you need.
Applying a theme to the entire viewer
To apply a theme to the entire viewer, you need to set the [NutrientViewer.Configuration#theme][theme property] property to your theme object (try the CSS override approach in the Playground(opens in a new tab)):
import NutrientViewer from "@nutrient-sdk/viewer";
const customTheme = { // ═══════════════════════════════════════════════════════════════════════════ // Elevation — Box shadow values for depth/layering effects // Used for dropdowns, modals, tooltips, and floating elements // ═══════════════════════════════════════════════════════════════════════════ elevation: { low: "0 0 4px 0 rgba(0, 0, 0, 0.4)", // Subtle shadow for slight elevation medium: "0 4px 16px 0 rgba(0, 0, 0, 0.15)", // Prominent shadow for modals/dropdowns },
// ═══════════════════════════════════════════════════════════════════════════ // Opacity — Transparency values used throughout the UI // Applied to disabled states, overlays, and hover effects // ═══════════════════════════════════════════════════════════════════════════ opacity: { none: "0", // Fully transparent low: "0.1", // Barely visible (subtle overlays) medium: "0.5", // Semi-transparent (disabled states) high: "0.9", // Nearly opaque (hover overlays) },
// ═══════════════════════════════════════════════════════════════════════════ // Rounded — Border radius values for rounded corners // Applied to buttons, inputs, cards, and other UI elements // ═══════════════════════════════════════════════════════════════════════════ rounded: { xs: "2px", // Minimal rounding (table cells) sm: "4px", // Small rounding (buttons, inputs) md: "8px", // Medium rounding (cards, panels) lg: "12px", // Large rounding (modals) xl: "16px", // Extra large rounding "2xl": "24px", // Very rounded corners full: "9999px", // Fully rounded (pills, avatars) },
// ═══════════════════════════════════════════════════════════════════════════ // Spacing — Margin and padding values // Used for consistent spacing throughout the interface // ═══════════════════════════════════════════════════════════════════════════ spacing: { xs: "2px", // Tiny spacing (icon gaps) sm: "4px", // Small spacing (compact elements) md: "8px", // Medium spacing (standard padding) lg: "12px", // Large spacing (section padding) xl: "16px", // Extra large spacing "2xl": "24px", // Double extra large "3xl": "32px", // Section margins "4xl": "40px", // Large section gaps "5xl": "48px", // Major section spacing "6xl": "64px", // Page-level spacing "7xl": "80px", // Large page margins "8xl": "96px", // Extra large margins "9xl": "160px", // Maximum spacing },
// ═══════════════════════════════════════════════════════════════════════════ // Color — All color definitions for the theme // This is the most important section for visual customization // ═══════════════════════════════════════════════════════════════════════════ color: { // ───────────────────────────────────────────────────────────────────────── // Support colors — Semantic colors for status messages and alerts // Used in validation, notifications, and system feedback // ───────────────────────────────────────────────────────────────────────── support: { error: { subtler: "#711b00", // Darkest error (backgrounds) subtle: "#b53007", // Dark error (borders) medium: "#fe7a68", // Standard error (text, icons) strong: "#ffd4ce", // Light error (highlighted backgrounds) }, success: { subtler: "#223a03", // Darkest success subtle: "#3C6612", // Dark success medium: "#80CC34", // Standard success strong: "#ecfeda", // Light success }, warning: { subtler: "#562800", // Darkest warning subtle: "#bd5a00", // Dark warning medium: "#eb7f00", // Standard warning strong: "#ffd4a1", // Light warning }, info: { subtler: "#190d94", // Darkest info subtle: "#4537de", // Dark info medium: "#777cf0", // Standard info strong: "#d3dcff", // Light info }, },
// ───────────────────────────────────────────────────────────────────────── // Focused — Colors for focus rings and accessibility indicators // Critical for keyboard navigation and accessibility // ───────────────────────────────────────────────────────────────────────── focused: { default: "#FB6F3B", // Main focus ring color inset: "#5B1C0F", // Inset focus indicator },
// ───────────────────────────────────────────────────────────────────────── // Background — All background colors // Controls the visual appearance of surfaces and containers // ───────────────────────────────────────────────────────────────────────── background: { // Primary backgrounds — Main viewer surfaces primary: { subtle: "#5B1C0F", // Lightest primary background medium: "#431207", // Standard primary background strong: "#180501", // Darkest primary background (main canvas) }, // Interactive backgrounds — Buttons, links, clickable elements interactive: { enabled: "#FB6F3B", // Default button background hovered: "#FEB609", // Hovered button background active: "#FFC938", // Active/pressed button background visited: "#FFC938", // Visited link background disabled: "rgb(84.7% 28.6% 16.1% / 0.5)", // Disabled state }, // Inverse backgrounds — For contrast areas inverse: { subtle: "#E95635", medium: "#FFC938", strong: "#FFE7A9", }, // Secondary backgrounds — Sidebars, panels, cards secondary: { subtle: "#762B1A", medium: "#A93920", strong: "#D84929", }, // Overlay backgrounds — Modals, dropdowns, tooltips overlay: { subtle: "rgb(100% 90.6% 66.3% / 0.25)", // Light overlay medium: "rgb(9.41% 1.96% 0.39% / 0.5)", // Medium overlay interactive: "rgb(98.4% 43.5% 23.1% / 0.25)", // Interactive overlay }, // Positive backgrounds — Selection, highlights positive: { subtle: "#FFC938", medium: "#FEDE89", strong: "#FFE7A9", interactive: { enabled: "#5B1C0F", }, }, },
// ───────────────────────────────────────────────────────────────────────── // Text — All text colors // Controls readability and visual hierarchy of content // ───────────────────────────────────────────────────────────────────────── text: { primary: "#FFE7A9", // Main body text secondary: "#FEB609", // Secondary/muted text helper: "#FB6F3B", // Helper text, captions placeholder: "#FB6F3B", // Input placeholder text inverse: "#5B1C0F", // Text on light backgrounds oninteractive: "#5B1C0F", // Text on interactive elements (buttons) interactive: { enabled: "#FB6F3B", // Link text hovered: "#FEB609", // Hovered link text active: "#FFC938", // Active link text visited: "#FFC938", // Visited link text disabled: "rgb(100% 90.6% 66.3% / 0.5)", // Disabled text }, },
// ───────────────────────────────────────────────────────────────────────── // Icon — All icon colors // Usually matches text colors but can be customized separately // ───────────────────────────────────────────────────────────────────────── icon: { primary: "#FFE7A9", // Main icon color secondary: "#FB6F3B", // Secondary icon color inverse: "#5B1C0F", // Icons on light backgrounds oninteractive: "#5B1C0F", // Icons on buttons interactive: { enabled: "#FB6F3B", // Interactive icon hovered: "#FEB609", // Hovered icon active: "#FFC938", // Active icon visited: "#FFC938", // Visited state disabled: "rgb(100% 90.6% 66.3% / 0.5)", // Disabled icon }, },
// ───────────────────────────────────────────────────────────────────────── // Border — All border colors // Defines boundaries between UI elements // ───────────────────────────────────────────────────────────────────────── border: { subtle: "#762B1A", // Subtle borders (dividers) medium: "#A93920", // Standard borders strong: "#D84929", // Prominent borders inverse: "#FFE7A9", // Borders on dark backgrounds interactive: { enabled: "#FB6F3B", // Interactive element borders hovered: "#FEB609", // Hovered borders active: "#FFC938", // Active borders visited: "#FFC938", // Visited state disabled: "#D84929", // Disabled borders }, positive: { interactive: { enabled: "#5B1C0F", }, subtle: "#FFC938", medium: "#FEB609", strong: "#FB6F3B", }, }, },
// ═══════════════════════════════════════════════════════════════════════════ // Typography — Font definitions for all text styles // Controls font family, size, weight, and line height // Format: 'weight size/line-height font-family' // ═══════════════════════════════════════════════════════════════════════════ typography: { // ───────────────────────────────────────────────────────────────────────── // Headings — Title text styles (h1–h6) // ───────────────────────────────────────────────────────────────────────── heading: { h6: { regular: { font: '400 0.875rem/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0.1px", }, medium: { font: '500 0.875rem/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0.1px", }, semibold: { font: '600 0.875rem/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0.1px", }, }, h5: { regular: { font: '400 1rem/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0.1px", }, medium: { font: '500 1rem/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0.1px", }, semibold: { font: '600 1rem/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0.1px", }, }, h4: { regular: { font: '400 1.375rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0", }, medium: { font: '500 1.375rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0", }, semibold: { font: '600 1.375rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0", }, }, h3: { regular: { font: '400 1.5rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0", }, medium: { font: '500 1.5rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0", }, semibold: { font: '600 1.5rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0", }, }, h2: { regular: { font: '400 1.75rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0", }, medium: { font: '500 1.75rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0", }, semibold: { font: '600 1.75rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0", }, }, h1: { regular: { font: '400 2rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0", }, medium: { font: '500 2rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0", }, semibold: { font: '600 2rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0", }, }, }, // ───────────────────────────────────────────────────────────────────────── // Labels — UI labels, form labels, button text // ───────────────────────────────────────────────────────────────────────── label: { sm: { regular: { font: '400 0.688rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0.05px", }, medium: { font: '500 0.688rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0.05px", }, semibold: { font: '600 0.688rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0.05px", }, }, md: { regular: { font: '400 0.75rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0.1px", }, medium: { font: '500 0.75rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0.1px", }, semibold: { font: '600 0.75rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0.1px", }, }, lg: { regular: { font: '400 0.875rem/1.375 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0.1px", }, medium: { font: '500 0.875rem/1.375 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0.1px", }, semibold: { font: '600 0.875rem/1.375 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0.1px", }, }, }, // ───────────────────────────────────────────────────────────────────────── // Body — Body text, paragraphs, descriptions // ───────────────────────────────────────────────────────────────────────── body: { sm: { regular: { font: '400 0.75rem/1.375 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0.05px", }, medium: { font: '500 0.75rem/1.375 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0.05px", }, semibold: { font: '600 0.75rem/1.375 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0.05px", }, }, md: { regular: { font: '400 0.875rem/1.375 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0.1px", }, medium: { font: '500 0.875rem/1.375 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0.1px", }, semibold: { font: '600 0.875rem/1.375 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0.1px", }, }, lg: { regular: { font: '400 1rem/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0.1px", }, medium: { font: '500 1rem/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0.1px", }, semibold: { font: '600 1rem/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif', letterSpacing: "0.1px", }, }, }, },};
// Get the container element where the viewer will be mounted.const container = document.getElementById("nutrient-container");
NutrientViewer.load({ container, document: "document.pdf", theme: customTheme,}).catch((error) => { console.error("Failed to load viewer with custom theme:", error);});You can find the complete signature of the theme object in the API documentation. One thing to keep in mind is that you cannot pass a partial theme object. You need to pass the whole theme object, including the properties you don’t want to override. You can find an example of a theme object here.
Applying theme to a specific section
The JSON object above is applied to the whole viewer in form of CSS variables. This enables you to override the theme for a specific section of the viewer by just overriding the CSS variables for that section. These variables start with the prefix --bui-. Every theme object property is mapped to a CSS variable. For example, the color.background.primary.strong property is mapped to the --bui-color-background-primary-strong CSS variable.
For example, if you want to override the error support color for the toolbar, you can do the following:
.PSPDFKit-Toolbar { --bui-color-support-error-subtler: #000; --bui-color-support-error-subtle: #000; --bui-color-support-error-medium: #000; --bui-color-support-error-strong: #000;}You can find the complete list of CSS variables in the API documentation.