TrueShade is a lightweight, dependency-free JavaScript module that manages UI color schemes (dark / light) for web applications. It persists the user's preference in localStorage, applies it to the <html> element via the data-theme attribute, reacts instantly to system color-scheme changes, and keeps every open tab in sync. It supports three modes: system-default, dark, and light.
TrueShade
localStorage
<html>
data-theme
system-default
dark
light
Heads up: TrueShade is the logic and synchronization layer only — it does not ship dark/light CSS. You write your styles against the <html data-theme="dark|light"> attribute that TrueShade sets for you.
<html data-theme="dark|light">
prefers-color-scheme
AppName
BroadcastChannel
storage
OnChange
Install TrueShade from the OutSystems Forge.
There are two ways to initialize TrueShade:
Initialize
On Ready
Dispose
On Destroy
Pass an optional AppName to Initialize when several modules should share one preference.
Effective theme (dark or light) — client action GetLayoutColorScheme.
GetLayoutColorScheme
Raw stored preference (system-default, dark, or light) — client action GetStoredLayoutColorScheme.
GetStoredLayoutColorScheme
Set and persist a new theme with the client action SetLayoutColorScheme (system-default, dark, or light). Invalid values are rejected and logged as a console error.
SetLayoutColorScheme
The callback receives the effective theme and the raw preference. Pass { immediate: true } to also fire once with the current theme on subscribe:
{ immediate: true }
const unsubscribe = TrueShade.Theme.OnChange((effective, raw) => { console.log('Effective:', effective, '| Stored:', raw); }, { immediate: true }); // Later, to unsubscribe: unsubscribe();
Because the theme is applied during initialization, the page can briefly paint the wrong theme on first load. To avoid this, add a synchronous snippet to your page <head>, before any stylesheet. Replace MyApp with the AppName you pass to Initialize:
<head>
MyApp
<script> (function () { try { var raw = localStorage.getItem('MyApp$layout-theme') || 'system-default'; var dark = raw === 'dark' || (raw === 'system-default' && matchMedia('(prefers-color-scheme: dark)').matches); document.documentElement.dataset.theme = dark ? 'dark' : 'light'; } catch (e) { document.documentElement.dataset.theme = 'light'; } })(); </script>
Use GetStorageKey() to confirm the exact key at runtime.
GetStorageKey()
Dispose of the module to remove event listeners (the stored preference is preserved):
TrueShade.Theme.Dispose();
Initialize(appName?)
appName
<html data-theme>
void
GetLayoutColorScheme()
'dark' | 'light'
GetStoredLayoutColorScheme()
'system-default' | 'dark' | 'light'
SetLayoutColorScheme(theme)
theme
OnChange(callback, options?)
callback
(effective, raw) => void
'dark'
'light'
options
console.error
() => void
string
myapp$layout-theme
$OS_example$layout-theme
Dispose()
<AppName>$layout-theme
$OS_<moduleName>$layout-theme
moduleName
https://example.com/app
$OS_app$layout-theme
window.matchMedia
$OS_<name>$layout-theme
Retrieve the effective theme (dark or light):
Client Action: GetLayoutColorScheme
Retrieve the raw stored preference (dark, light or system-default):
Client Action: GetStoredLayoutColorScheme
Set and persist a new theme (dark, light or system-default):
Client Action: SetLayoutColorScheme
Invalid theme values are rejected with a console error.
Register a callback to handle theme changes (receives dark or light):
const unsubscribe = TrueShade.Theme.OnChange((effectiveTheme) => { console.log('Theme changed to:', effectiveTheme);});// Later, to unsubscribe:unsubscribe();
Dispose of the module to remove event listeners (does not clear stored preference):
TrueShade.Theme.Initialize(appName?)
TrueShade.Theme.GetLayoutColorScheme()
TrueShade.Theme.GetStoredLayoutColorScheme()
TrueShade.Theme.SetLayoutColorScheme(theme)
TrueShade.Theme.Dispose()
TrueShade.Theme.OnChange(callback)
(effective: string) => void
$OS_<name>$
onChange
To set up the theme system, you can choose one of two approaches:
TrueShade.Theme.Initialize()
Lightweight JavaScript utility to manage Dark / Light / System-default theme in OutSystems apps.
It works by toggling the <html data-theme> attribute and persisting the user’s choice (per module) in localStorage.
Modes: system-default | dark | light
Auto-follows OS dark mode when set to system-default
Per-module key: $OS_<ModuleName>$layout-theme
Minimal DOM writes & zero external dependencies
Safe no-op if localStorage is unavailable
Public API exposed under TrueShade.Theme.*
Consistent Dark / Light theme switching across screens
Respect user’s OS preference unless explicitly overridden
Cross-tab synchronization via storage event
Install the asset from the Forge.
Call the Initialize client action in your Layout’s OnReady event.
html[data-theme="dark"] {
color-scheme: dark;
background: #111;
color: #eee;
}
html[data-theme="light"] {
color-scheme: light;
background: #fff;
color: #111;
TrueShade.Theme.SetLayoutColorScheme(mode)
Initialize()
Idempotent. Seeds localStorage with system-default if absent, applies theme, attaches listeners for OS changes and storage events.
Returns the effective mode. If stored value is system-default, it resolves to the current OS mode (dark or light).
Returns the literal stored string or null (before initialization).
SetLayoutColorScheme(mode)
Accepts only system-default | dark | light. Logs console.error on invalid input.
Removes listeners; does not delete stored preference.
Computed key: $OS_<ModuleName>$layout-theme
Where ModuleName is derived as follows:
First non-empty path segment (e.g. /Sales/Home → Sales)
Else first hostname label (e.g. admin.example.com → admin)
If empty, fallback ⇒ layout-theme
<html data-theme="dark|light"> is always kept in sync.
When user selects system-default, the attribute automatically tracks OS changes.
TrueShade Theme Controller
Lightweight JavaScript utility to manage Dark / Light / OS‑default theme in OutSystems apps by toggling the <html data-theme> attribute and persisting the user choice (per module) in localStorage.
Highlights • Modes: os-default | dark | light • Auto-follows OS dark mode when set to os-default • Per‑module key: $OS_<ModuleName>$layout-theme • Minimal DOM writes & zero external dependencies • Safe no-op if localStorage unavailable • Public API exposed under TrueShade.Theme.*
Use Cases • Consistent dark / light theme switching across screens • Respect user’s OS preference unless explicitly overridden • Cross‑tab synchronization (storage event)
Installation
Minimal CSS (place in Theme)
Public APIs
TrueShade.Theme.SetLayoutColorScheme(ThemeId)
Behavior Details
Initialize(): Idempotent. Seeds localStorage with os-default if absent, applies theme, attaches listeners for OS changes + storage.
GetLayoutColorScheme(): Returns the effective mode; if stored is os-default it resolves to current OS (dark or light).
GetStoredLayoutColorScheme(): Returns literal stored string or null (before initialization seed).
SetLayoutColorScheme(mode): Accepts only os-default | dark | light; invalid logs console.error.
Dispose(): Removes listeners; does not delete stored preference.
Storage Key Format
ModuleName is derived:
DOM Contract
<html data-theme="dark|light"> is maintained. When user selects os-default the attribute auto tracks OS changes.