Easily add Dark/Light mode toggle in WebViewer.
This demo allows you to:
Implementation steps
To add Dark/Light UI themes in WebViewer:
Step 1: Choose your preferred web stack
Step 2: Download required modules listed in the Demo Dependencies section below
Step 3: Add the ES6 JavaScript sample code provided in this guide
Demo Dependencies
This sample uses the following:
1// ES6 Compliant Syntax
2// GitHub Copilot v1, Claude Sonnet 4 (Preview), October 8, 2025
3// File: showcase-demos/webviewer-dark-mode/index.js
4
5import WebViewer from '@pdftron/webviewer';
6
7// Global variables to track state
8let redactionDemoFile = "https://apryse.s3.amazonaws.com/public/files/samples/WebviewerDemoDoc.pdf";
9
10// Function to initialize and load the Redaction Tool
11function initializeWebViewer() {
12
13 const element = document.getElementById('viewer');
14 if (!element) {
15 console.error('Viewer div not found.');
16 return;
17 }
18
19 WebViewer({
20 path: '/lib',
21 initialDoc: redactionDemoFile,
22 licenseKey: 'YOUR_LICENSE_KEY',
23 }, element).then(instance => {
24
25 // define documentViewer for use in other functions
26 const { documentViewer } = instance.Core;
27 documentViewer.addEventListener('documentLoaded', () => {
28 // Initialize redaction tools or any other setup
29 });
30
31 //Default mode on load
32 handleThemeToggle('dark');
33
34 // UI Section
35 createUIElements();
36 });
37}
38
39// Function to handle theme toggling
40// Toggle WebViewer UI: Dark or Light modes
41function handleThemeToggle(theme) {
42 console.log(`Theme toggle called with: ${theme}`);
43
44 if (theme === 'dark') {
45 window.WebViewer.getInstance().UI.setTheme('dark');
46 console.log('Switching to dark theme');
47 }
48 else if (theme === 'light') {
49 window.WebViewer.getInstance().UI.setTheme('light');
50 console.log('Switching to light theme');
51 }
52}
53
54// Make handleThemeToggle globally available
55window.handleThemeToggle = handleThemeToggle;
56
57// UI Elements
58// ui-elements.js
59// Function to create and initialize UI elements
60function createUIElements() {
61 // Create a container for all controls (label, dropdown, and buttons)
62 // Dynamically load ui-elements.js if not already loaded
63 if (!window.SidePanel) {
64 const script = document.createElement('script');
65 script.src = '/showcase-demos/webviewer-dark-mode/ui-elements.js';
66 script.onload = () => {
67 UIElements.init('viewer');
68
69 };
70 document.head.appendChild(script);
71 }
72}
73
74// Initialize the WebViewer
75initializeWebViewer();
76
1// ES6 Compliant Syntax
2// GitHub Copilot v1, Claude Sonnet 4 (Preview), October 8, 2025
3// File: showcase-demos/webviewer-dark-mode/ui-elements.js
4
5class UIElements {
6
7
8 static init(viewerId) {
9 this.createSidePanel(viewerId);
10 }
11
12 // Function to create a side panel that sits on the left side of the viewer
13 static createSidePanel(viewerId) {
14 const viewerElement = document.getElementById(viewerId);
15 if (!viewerElement) {
16 console.error(`Viewer element with id '${viewerId}' not found.`);
17 return;
18 }
19
20 // Create the side panel container
21 const sidePanel = document.createElement('div');
22 sidePanel.id = 'side-panel';
23 sidePanel.className = 'side-panel';
24
25 // Create side panel content
26 const content = document.createElement('div');
27 content.className = 'side-panel-content';
28
29 // Add some sample content
30 const sampleContent = document.createElement('div');
31 sampleContent.innerHTML = `
32 <div class="panel-section">
33 <h4>Switch Modes</h4>
34 <div class="theme-switch-container">
35 <div class="theme-switch">
36 <input type="radio" id="dark-mode" name="theme" value="dark" checked onclick="handleThemeToggle(this.value)">
37 <label for="dark-mode" class="switch-option left">
38 <span class="switch-icon">🌙</span>
39 <span class="switch-label">Dark</span>
40 </label>
41
42 <input type="radio" id="light-mode" name="theme" value="light" onclick="handleThemeToggle(this.value)">
43 <label for="light-mode" class="switch-option right">
44 <span class="switch-icon">☀️</span>
45 <span class="switch-label">Light</span>
46 </label>
47
48 <div class="switch-slider"></div>
49 </div>
50 </div>
51 </div>
52 `;
53
54 content.appendChild(sampleContent);
55
56 sidePanel.appendChild(content);
57
58 // Create a wrapper to contain both the side panel and viewer
59 const wrapper = document.createElement('div');
60 wrapper.id = 'viewer-wrapper';
61 wrapper.className = 'viewer-wrapper';
62
63 // Insert the wrapper before the viewer element
64 viewerElement.parentNode.insertBefore(wrapper, viewerElement);
65
66 // Move the viewer element into the wrapper and add the side panel
67 wrapper.appendChild(sidePanel);
68 wrapper.appendChild(viewerElement);
69
70 // Add the viewer-with-panel class to the viewer element
71 viewerElement.classList.add('viewer-with-panel');
72
73 console.log('Side panel created successfully');
74 }
75
76 // Function to toggle side panel visibility
77 toggleSidePanel() {
78 const sidePanel = document.getElementById('side-panel');
79 if (sidePanel) {
80 sidePanel.classList.toggle('collapsed');
81 }
82 }
83
84 // Function to add content to the side panel
85 addPanelContent(content) {
86 const panelContent = document.querySelector('.side-panel-content');
87 if (panelContent) {
88 const contentDiv = document.createElement('div');
89 contentDiv.className = 'panel-section';
90 contentDiv.innerHTML = content;
91 panelContent.appendChild(contentDiv);
92 }
93 }
94}
95
96
1/* Main layout - side by side containers within #viewer */
2#viewer {
3 display: flex;
4 height: 100%;
5 width: 100%;
6}
7
8/* Side Panel Styles */
9.viewer-wrapper {
10 display: flex;
11 height: 100vh;
12 width: 100%;
13}
14
15.side-panel {
16 width: 300px;
17 min-width: 250px;
18 max-width: 400px;
19 background-color: #f5f5f5;
20 border-right: 1px solid #ddd;
21 box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1);
22 transition: transform 0.3s ease;
23 z-index: 1000;
24 display: flex;
25 flex-direction: column;
26}
27
28.side-panel.collapsed {
29 transform: translateX(-100%);
30}
31
32.side-panel-header {
33 background-color: #e9ecef;
34 padding: 15px 20px;
35 border-bottom: 1px solid #ddd;
36 flex-shrink: 0;
37}
38
39.side-panel-header h3 {
40 margin: 0;
41 font-size: 18px;
42 font-weight: 600;
43 color: #333;
44}
45
46.side-panel-content {
47 flex: 1;
48 padding: 20px;
49 overflow-y: auto;
50}
51
52.panel-section {
53 margin-bottom: 25px;
54}
55
56.panel-section h4 {
57 margin: 0 0 12px 0;
58 font-size: 14px;
59 font-weight: 600;
60 color: #555;
61 text-transform: uppercase;
62 letter-spacing: 0.5px;
63}
64
65.panel-button {
66 display: block;
67 width: 100%;
68 padding: 10px 15px;
69 margin-bottom: 8px;
70 background-color: #fff;
71 border: 1px solid #ddd;
72 border-radius: 4px;
73 cursor: pointer;
74 transition: all 0.2s ease;
75 font-size: 14px;
76}
77
78.panel-button:hover {
79 background-color: #007bff;
80 color: white;
81 border-color: #007bff;
82}
83
84.panel-button:active {
85 transform: translateY(1px);
86}
87
88.setting-item {
89 margin-bottom: 15px;
90}
91
92.setting-item label {
93 display: flex;
94 align-items: center;
95 font-size: 14px;
96 color: #555;
97 cursor: pointer;
98}
99
100.setting-item input[type="checkbox"] {
101 margin-right: 10px;
102}
103
104.setting-item input[type="range"] {
105 margin-left: 10px;
106 flex: 1;
107}
108
109.viewer-with-panel {
110 flex: 1;
111 height: 100vh;
112}
113
114/* Dark mode styles */
115@media (prefers-color-scheme: dark) {
116 .side-panel {
117 background-color: #2d3748;
118 border-right-color: #4a5568;
119 }
120
121 .side-panel-header {
122 background-color: #1a202c;
123 border-bottom-color: #4a5568;
124 }
125
126 .side-panel-header h3 {
127 color: #e2e8f0;
128 }
129
130 .panel-section h4 {
131 color: #a0aec0;
132 }
133
134 .panel-button {
135 background-color: #4a5568;
136 border-color: #718096;
137 color: #e2e8f0;
138 }
139
140 .panel-button:hover {
141 background-color: #007bff;
142 border-color: #007bff;
143 }
144
145 .setting-item label {
146 color: #a0aec0;
147 }
148}
149
150/* Responsive design */
151@media (max-width: 768px) {
152 .side-panel {
153 position: absolute;
154 left: 0;
155 top: 0;
156 height: 100%;
157 z-index: 1001;
158 }
159
160 .viewer-wrapper {
161 position: relative;
162 }
163
164 .side-panel.collapsed {
165 transform: translateX(-100%);
166 }
167}
168
169/* Toggle button for mobile */
170.side-panel-toggle {
171 position: fixed;
172 top: 20px;
173 left: 20px;
174 z-index: 1002;
175 background-color: #007bff;
176 color: white;
177 border: none;
178 border-radius: 4px;
179 padding: 10px;
180 cursor: pointer;
181 display: none;
182}
183
184@media (max-width: 768px) {
185 .side-panel-toggle {
186 display: block;
187 }
188}
189
190/* Theme Switch Styles */
191.theme-switch-container {
192 display: flex;
193 justify-content: center;
194 margin-top: 15px;
195}
196
197.theme-switch {
198 position: relative;
199 display: flex;
200 background-color: #e9ecef;
201 border-radius: 25px;
202 padding: 4px;
203 border: 2px solid #dee2e6;
204 width: 200px;
205 height: 50px;
206 overflow: hidden;
207}
208
209.theme-switch input[type="radio"] {
210 display: none;
211}
212
213.switch-option {
214 flex: 1;
215 display: flex;
216 flex-direction: column;
217 align-items: center;
218 justify-content: center;
219 cursor: pointer;
220 position: relative;
221 z-index: 2;
222 transition: color 0.3s ease;
223 padding: 5px;
224}
225
226.switch-option.left {
227 border-radius: 20px 0 0 20px;
228}
229
230.switch-option.right {
231 border-radius: 0 20px 20px 0;
232}
233
234.switch-icon {
235 font-size: 16px;
236 margin-bottom: 2px;
237}
238
239.switch-label {
240 font-size: 12px;
241 font-weight: 500;
242 text-transform: uppercase;
243 letter-spacing: 0.5px;
244}
245
246.switch-slider {
247 position: absolute;
248 top: 4px;
249 left: 4px;
250 width: calc(50% - 4px);
251 height: calc(100% - 8px);
252 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
253 border-radius: 20px;
254 transition: transform 0.3s ease, background 0.3s ease;
255 z-index: 1;
256 box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
257}
258
259/* Dark mode selected */
260#dark-mode:checked ~ .switch-slider {
261 transform: translateX(0);
262 background: linear-gradient(135deg, #2c3e50 0%, #5a5c5e 100%);
263}
264
265/* Light mode selected */
266#light-mode:checked ~ .switch-slider {
267 transform: translateX(100%);
268 background: linear-gradient(135deg, #e4ce85 0%, #e9ca1d 100%);
269}
270
271/* Text color changes */
272#dark-mode:checked ~ .switch-option.left {
273 color: white;
274}
275
276#light-mode:checked ~ .switch-option.right {
277 color: white;
278}
279
280.switch-option {
281 color: #6c757d;
282}
283
284/* Dark mode theme styles */
285@media (prefers-color-scheme: dark) {
286 .theme-switch {
287 background-color: #4a5568;
288 border-color: #718096;
289 }
290
291 .switch-option {
292 color: #a0aec0;
293 }
294
295 #dark-mode:checked ~ .switch-option.left {
296 color: white;
297 }
298
299 #light-mode:checked ~ .switch-option.right {
300 color: white;
301 }
302}
Did you find this helpful?
Trial setup questions?
Ask experts on DiscordNeed other help?
Contact SupportPricing or product questions?
Contact Sales