Universal Theme Supports Dark Mode
Starting with APEX 23.1, Universal Theme includes a built in Dark Mode. However, giving users the ability to toggle between light and dark modes based on their preference requires a small amount of configuration. This article walks through creating a user preference driven dark mode toggle that persists across sessions.
The Simplest Approach
Universal Theme’s dark mode is controlled by adding the class u-dark to the document body. You can toggle it with one line of JavaScript:
document.body.classList.toggle('u-dark');
But this does not persist the user’s choice. As soon as they navigate to another page or start a new session, the setting is lost.
Persisting the Preference
Create an application item called G_DARK_MODE with session state protection set to Unrestricted. Then create a small table to store user preferences, or add a column to an existing preferences table:
CREATE TABLE user_display_prefs (
username VARCHAR2(255) PRIMARY KEY,
dark_mode VARCHAR2(1) DEFAULT 'N',
updated_dt DATE DEFAULT SYSDATE
);
On the Global Page, add a Before Header computation that loads the preference into the application item:
BEGIN
SELECT NVL(dark_mode, 'N')
INTO :G_DARK_MODE
FROM user_display_prefs
WHERE username = :APP_USER;
EXCEPTION
WHEN NO_DATA_FOUND THEN
:G_DARK_MODE := 'N';
END;
The Toggle Button
Add a button or navigation bar entry to the Global Page. Create a Dynamic Action on click that executes JavaScript to toggle the class and then makes an AJAX call to save the preference:
// Toggle the class immediately for instant visual feedback
document.body.classList.toggle('u-dark');
var isDark = document.body.classList.contains('u-dark') ? 'Y' : 'N';
// Save the preference via AJAX callback
apex.server.process('SAVE_DARK_MODE', {
x01: isDark
}, {
success: function(data) {
// Preference saved
}
});
Create an AJAX Callback process named SAVE_DARK_MODE on the Global Page:
MERGE INTO user_display_prefs p
USING (SELECT :APP_USER AS username FROM DUAL) s
ON (p.username = s.username)
WHEN MATCHED THEN
UPDATE SET dark_mode = APEX_APPLICATION.G_X01, updated_dt = SYSDATE
WHEN NOT MATCHED THEN
INSERT (username, dark_mode) VALUES (:APP_USER, APEX_APPLICATION.G_X01);
Applying on Page Load
Finally, add a Global Page JavaScript block that checks the application item and applies the class on every page load:
if (apex.item("G_DARK_MODE").getValue() === 'Y') {
document.body.classList.add('u-dark');
}
This gives you a fully persistent, user controlled dark mode toggle with instant visual feedback and no page refresh required.