I like that we now have a few different themes for the backoffice so now I can actually use darkmod! The question is how can I set this as the defult for all users and is it possible to set it for the login/logout/recovery screen as well?
Hi @Eaglef90
I don’t know the answer to this, but here is AI’s take on it:
There is no appsettings switch for this, but you can do it with a small app entry point, and yes it will cover the login, logout and password recovery screens too.
First, how it works in v17. Themes are registered as theme extensions in the core, with these aliases:
umb-light-theme(the base, no CSS of its own)umb-dark-theme(“Dark (Experimental)”)umb-high-contrast-theme(“High contrast (Experimental)”)
UmbThemeContext is registered as a global context (Umb.GlobalContext.Theme) and stores the chosen alias in localStorage under the key umb-theme-alias, falling back to light when nothing is stored. Two things follow from that:
- The preference is per browser and per device, not stored against the user in the database. The same user on another machine gets the default again.
- There is nothing to push out from the Users section.
The reason the login screens come along for the ride is that the theme context is initialised by the core entry point on the umb-app shell, which runs before authentication, and the theme CSS gets appended to document.head. Login, installer, upgrader and backoffice all live inside that same shell.
To set the default, use an appEntryPoint, which is the one entry point type that runs for all apps including Login. The important bit is allowPublicAccess, see below.
/App_Plugins/DemoDefaultTheme/umbraco-package.json:
{
"name": "Demo.DefaultTheme",
"version": "1.0.0",
"allowPublicAccess": true,
"extensions": [
{
"type": "appEntryPoint",
"alias": "Demo.AppEntryPoint.DefaultTheme",
"name": "Default Backoffice Theme",
"js": "/App_Plugins/DemoDefaultTheme/default-theme.js"
}
]
}
/App_Plugins/DemoDefaultTheme/default-theme.js:
import { UMB_THEME_CONTEXT } from '@umbraco-cms/backoffice/themes';
import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api';
const STORAGE_KEY = 'umb-theme-alias';
const DEFAULT_THEME = 'umb-dark-theme';
export const onInit = (host) => {
if (localStorage.getItem(STORAGE_KEY)) return;
new UmbContextConsumerController(host, UMB_THEME_CONTEXT, (context) => {
context?.setThemeByAlias(DEFAULT_THEME);
});
};
No build step needed, the import map resolves those specifiers in the browser.
A few notes on why it is written that way:
allowPublicAccess is not optional here. It defaults to false, and Umbraco splits manifests into public and private sets. The client calls registerPublicExtensions() during app setup, but registerPrivateExtensions() only runs from the backoffice element after you have authenticated. Leave the flag out and your entry point will not load until after login, so the login screen stays light. There is no double registration risk, a manifest is in one set or the other.
Consume the context rather than just writing to localStorage. UmbThemeContext is constructed during the core onInit, which happens before the app entry point modules have finished loading, so it has already read localStorage by the time your code runs. Writing the key on its own would only take effect on the next page load. setThemeByAlias applies it immediately and writes the key itself.
The early return respects user choice. Once someone picks a theme in the user modal the key exists, so this only ever seeds a fresh browser.
Two things worth weighing up before rolling it out. Both dark and high contrast are still labelled Experimental in the core manifests, and third party packages that hardcode colours instead of using the UUI custom properties can look rough, so I would check it with the editors first. Also, if you are testing on an instance with RuntimeMode set to Production, package manifests are cached for 30 days, so restart the app pool or you will wonder why nothing is happening.
One last thing, do not copy UMB_THEME_DARK_ALIAS style imports from newer sample code. Those constants do not exist in 17.x, use the string literals as above.
Justin
uSync.Complete lets you set colours for everyone on a server ![]()
it does it with an entry point as @justin-nevitech states, but it changes the css variables, to just change some of the bits (not the full theme).
Sweet. I just got my paycheck and am going to be buying a new licenses today so I will be back to using complete and will take advantage of this.
