> For the complete documentation index, see [llms.txt](https://docs.apryse.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.apryse.com/web/get-started/samples/showcase-demo-webviewer-dark-mode.md).

# WebViewer Dark Mode Showcase Demo Code Sample

Learn how to add a Dark/Light mode toggle in WebViewer.

{% hint style="info" %}
**Requirements**

*These packages are required to use these features in production. Trial keys have unlimited access to all features*

<a href="/web/get-started/readme.md" class="button primary">Web SDK</a><a href="https://showcase.apryse.com/webviewer-dark-mode" class="button primary">Live demo</a>
{% endhint %}

Easily add Dark/Light mode toggle in WebViewer.

This demo allows you to:

* Instantly switch the UI between dark and light modes.
* Use a single line of code to toggle themes.

### **Implementation steps**

To add Dark/Light UI themes in WebViewer:

Step 1: [Get started with WebViewer](/web/get-started/readme.md) in your preferred web stack. Step 2: Add the ES6 JavaScript sample code provided in this guide.

Once you generate your license key, it will automatically be included in your sample code below.

{% @apryse-license-key/apryse-license-key platform="WEB\_VIEWER" variant="compact" %}

{% tabs %}
{% tab title="index.js" %}

<pre class="language-js" data-line-numbers><code class="lang-js">// ES6 Compliant Syntax
// GitHub Copilot v1, Claude Sonnet 4 (Preview), October 8, 2025
// File: showcase-demos/webviewer-dark-mode/index.js

import WebViewer from '@pdftron/webviewer';

const licenseKey = '<code class="expression">visitor.claims.wvKey || "YOUR_WEBVIEWER_LICENSE_KEY"</code>';

// Global variables to track state
let redactionDemoFile = "https://apryse.s3.amazonaws.com/public/files/samples/WebviewerDemoDoc.pdf";

// Function to initialize and load the Redaction Tool
function initializeWebViewer() {

  const element = document.getElementById('viewer');
  if (!element) {
    console.error('Viewer div not found.');
    return;
  }

  WebViewer({
    path: '/lib',
    initialDoc: redactionDemoFile,
    licenseKey: licenseKey,
  }, element).then(instance => {

    // define documentViewer for use in other functions
    const { documentViewer } = instance.Core;
    documentViewer.addEventListener('documentLoaded', () => {
      // Initialize redaction tools or any other setup
    });

    //Default mode on load
    handleThemeToggle('dark');

    // UI Section 
    createUIElements();
  });
}

// Function to handle theme toggling
// Toggle WebViewer UI: Dark or Light modes
function handleThemeToggle(theme) {
  console.log(`Theme toggle called with: ${theme}`);

  if (theme === 'dark') {
    window.WebViewer.getInstance().UI.setTheme('dark');
    console.log('Switching to dark theme');
  }
  else if (theme === 'light') {
    window.WebViewer.getInstance().UI.setTheme('light');
    console.log('Switching to light theme');
  }
}

// Make handleThemeToggle globally available
window.handleThemeToggle = handleThemeToggle;

// UI Elements
// ui-elements.js
// Function to create and initialize UI elements
function createUIElements() {
  // Create a container for all controls (label, dropdown, and buttons)
  // Dynamically load ui-elements.js if not already loaded
  if (!window.SidePanel) {
    const script = document.createElement('script');
    script.src = '/showcase-demos/webviewer-dark-mode/ui-elements.js';
    script.onload = () => {
      UIElements.init('viewer');

    };
    document.head.appendChild(script);
  }
}

// Initialize the WebViewer
initializeWebViewer();

</code></pre>

{% endtab %}

{% tab title="ui-elements.js" %}
{% code title="ui-elements.js" lineNumbers="true" %}

```js
// ES6 Compliant Syntax
// GitHub Copilot v1, Claude Sonnet 4 (Preview), October 8, 2025
// File: showcase-demos/webviewer-dark-mode/ui-elements.js

class UIElements {


  static init(viewerId) {
    this.createSidePanel(viewerId);
  }

  // Function to create a side panel that sits on the left side of the viewer
  static createSidePanel(viewerId) {
    const viewerElement = document.getElementById(viewerId);
    if (!viewerElement) {
      console.error(`Viewer element with id '${viewerId}' not found.`);
      return;
    }

    // Create the side panel container
    const sidePanel = document.createElement('div');
    sidePanel.id = 'side-panel';
    sidePanel.className = 'side-panel';

    // Create side panel content
    const content = document.createElement('div');
    content.className = 'side-panel-content';

    // Add some sample content
    const sampleContent = document.createElement('div');
    sampleContent.innerHTML = `
      <div class="panel-section">
        <h4>Switch Modes</h4>
        <div class="theme-switch-container">
          <div class="theme-switch">
            <input type="radio" id="dark-mode" name="theme" value="dark" checked onclick="handleThemeToggle(this.value)">
            <label for="dark-mode" class="switch-option left">
              <span class="switch-icon">🌙</span>
              <span class="switch-label">Dark</span>
            </label>

            <input type="radio" id="light-mode" name="theme" value="light" onclick="handleThemeToggle(this.value)">
            <label for="light-mode" class="switch-option right">
              <span class="switch-icon">☀️</span>
              <span class="switch-label">Light</span>
            </label>
            
            <div class="switch-slider"></div>
          </div>
        </div>
      </div>
    `;

    content.appendChild(sampleContent);

    sidePanel.appendChild(content);

    // Create a wrapper to contain both the side panel and viewer
    const wrapper = document.createElement('div');
    wrapper.id = 'viewer-wrapper';
    wrapper.className = 'viewer-wrapper';

    // Insert the wrapper before the viewer element
    viewerElement.parentNode.insertBefore(wrapper, viewerElement);

    // Move the viewer element into the wrapper and add the side panel
    wrapper.appendChild(sidePanel);
    wrapper.appendChild(viewerElement);

    // Add the viewer-with-panel class to the viewer element
    viewerElement.classList.add('viewer-with-panel');

    console.log('Side panel created successfully');
  }

  // Function to toggle side panel visibility
  toggleSidePanel() {
    const sidePanel = document.getElementById('side-panel');
    if (sidePanel) {
      sidePanel.classList.toggle('collapsed');
    }
  }

  // Function to add content to the side panel
  addPanelContent(content) {
    const panelContent = document.querySelector('.side-panel-content');
    if (panelContent) {
      const contentDiv = document.createElement('div');
      contentDiv.className = 'panel-section';
      contentDiv.innerHTML = content;
      panelContent.appendChild(contentDiv);
    }
  }
}


```

{% endcode %}
{% endtab %}

{% tab title="index.css" %}
{% code title="index.css" lineNumbers="true" %}

```css
/* Main layout - side by side containers within #viewer */
#viewer {
  display: flex;
  height: 100%;
  width: 100%;
}

/* Side Panel Styles */
.viewer-wrapper {
  display: flex;
  height: 100vh;
  width: 100%;
}

.side-panel {
  width: 300px;
  min-width: 250px;
  max-width: 400px;
  background-color: #f5f5f5;
  border-right: 1px solid #ddd;
  box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
  transition: transform 0.3s ease;
  z-index: 1000;
  display: flex;
  flex-direction: column;
}

.side-panel.collapsed {
  transform: translateX(-100%);
}

.side-panel-header {
  background-color: #e9ecef;
  padding: 15px 20px;
  border-bottom: 1px solid #ddd;
  flex-shrink: 0;
}

.side-panel-header h3 {
  margin: 0;
  font-size: 18px;
  font-weight: 600;
  color: #333;
}

.side-panel-content {
  flex: 1;
  padding: 20px;
  overflow-y: auto;
}

.panel-section {
  margin-bottom: 25px;
}

.panel-section h4 {
  margin: 0 0 12px 0;
  font-size: 14px;
  font-weight: 600;
  color: #555;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.panel-button {
  display: block;
  width: 100%;
  padding: 10px 15px;
  margin-bottom: 8px;
  background-color: #fff;
  border: 1px solid #ddd;
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.2s ease;
  font-size: 14px;
}

.panel-button:hover {
  background-color: #007bff;
  color: white;
  border-color: #007bff;
}

.panel-button:active {
  transform: translateY(1px);
}

.setting-item {
  margin-bottom: 15px;
}

.setting-item label {
  display: flex;
  align-items: center;
  font-size: 14px;
  color: #555;
  cursor: pointer;
}

.setting-item input[type="checkbox"] {
  margin-right: 10px;
}

.setting-item input[type="range"] {
  margin-left: 10px;
  flex: 1;
}

.viewer-with-panel {
  flex: 1;
  height: 100vh;
}

/* Dark mode styles */
@media (prefers-color-scheme: dark) {
  .side-panel {
    background-color: #2d3748;
    border-right-color: #4a5568;
  }
  
  .side-panel-header {
    background-color: #1a202c;
    border-bottom-color: #4a5568;
  }
  
  .side-panel-header h3 {
    color: #e2e8f0;
  }
  
  .panel-section h4 {
    color: #a0aec0;
  }
  
  .panel-button {
    background-color: #4a5568;
    border-color: #718096;
    color: #e2e8f0;
  }
  
  .panel-button:hover {
    background-color: #007bff;
    border-color: #007bff;
  }
  
  .setting-item label {
    color: #a0aec0;
  }
}

/* Responsive design */
@media (max-width: 768px) {
  .side-panel {
    position: absolute;
    left: 0;
    top: 0;
    height: 100%;
    z-index: 1001;
  }
  
  .viewer-wrapper {
    position: relative;
  }
  
  .side-panel.collapsed {
    transform: translateX(-100%);
  }
}

/* Toggle button for mobile */
.side-panel-toggle {
  position: fixed;
  top: 20px;
  left: 20px;
  z-index: 1002;
  background-color: #007bff;
  color: white;
  border: none;
  border-radius: 4px;
  padding: 10px;
  cursor: pointer;
  display: none;
}

@media (max-width: 768px) {
  .side-panel-toggle {
    display: block;
  }
}

/* Theme Switch Styles */
.theme-switch-container {
  display: flex;
  justify-content: center;
  margin-top: 15px;
}

.theme-switch {
  position: relative;
  display: flex;
  background-color: #e9ecef;
  border-radius: 25px;
  padding: 4px;
  border: 2px solid #dee2e6;
  width: 200px;
  height: 50px;
  overflow: hidden;
}

.theme-switch input[type="radio"] {
  display: none;
}

.switch-option {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  position: relative;
  z-index: 2;
  transition: color 0.3s ease;
  padding: 5px;
}

.switch-option.left {
  border-radius: 20px 0 0 20px;
}

.switch-option.right {
  border-radius: 0 20px 20px 0;
}

.switch-icon {
  font-size: 16px;
  margin-bottom: 2px;
}

.switch-label {
  font-size: 12px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.switch-slider {
  position: absolute;
  top: 4px;
  left: 4px;
  width: calc(50% - 4px);
  height: calc(100% - 8px);
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-radius: 20px;
  transition: transform 0.3s ease, background 0.3s ease;
  z-index: 1;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}

/* Dark mode selected */
#dark-mode:checked ~ .switch-slider {
  transform: translateX(0);
  background: linear-gradient(135deg, #2c3e50 0%, #5a5c5e 100%);
}

/* Light mode selected */
#light-mode:checked ~ .switch-slider {
  transform: translateX(100%);
  background: linear-gradient(135deg, #e4ce85 0%, #e9ca1d 100%);
}

/* Text color changes */
#dark-mode:checked ~ .switch-option.left {
  color: white;
}

#light-mode:checked ~ .switch-option.right {
  color: white;
}

.switch-option {
  color: #6c757d;
}

/* Dark mode theme styles */
@media (prefers-color-scheme: dark) {
  .theme-switch {
    background-color: #4a5568;
    border-color: #718096;
  }
  
  .switch-option {
    color: #a0aec0;
  }
  
  #dark-mode:checked ~ .switch-option.left {
    color: white;
  }
  
  #light-mode:checked ~ .switch-option.right {
    color: white;
  }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.apryse.com/web/get-started/samples/showcase-demo-webviewer-dark-mode.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
