Easily add Dark/Light mode toggle in WebViewer.
This demo allows you to:
To add Dark/Light UI themes in WebViewer:
Step 1: Get started with WebViewer 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 collects some data regarding your usage of the SDK for product improvement.
The data that Apryse collects include:
For clarity, no other data is collected by the SDK and Apryse has no access to the contents of your documents.
If you wish to continue without data collection, contact us and we will email you a no-tracking trial key for you to get started.
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 the 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