BC Theme Manager
Themes the Business Central web client inside a Docker container: navigation bar colour, text and
icon colour, accent colour, logo and on-click link. The same settings the Microsoft 365 admin
centre "Default theme" page applies to a tenant, applied instead to a container on your machine.
Useful when you run several containers and want to tell them apart at a glance, or when you want a
demo container to look like the customer's branded tenant.

Using it
- BC Theme: Open Theme Manager from the command palette.
- Pick a container. Each one shows the theme it is currently wearing.
- Pick a theme, or edit the colours and logo by hand.
- Press Apply to <container>, then reload the Business Central tab with Ctrl+F5.
Out of the box a container that has never been themed opens on the 5-nodes branding, so a
fresh install is one press of Apply away from being branded. Change that with
bcThemeManager.defaultPreset.
BC Theme: Revert a Container to the Stock Theme puts a container back without opening the
panel. BC Theme: Show Log opens the output channel with the patcher's own output.
Themes
The Theme picker holds two kinds of entry.
- Built-in: 5-nodes and Business Central (the stock colours, no logo). These ship with the
extension and cannot be deleted.
- Saved: your own. Save as… names the current form and keeps it; saving over a name
replaces it. Delete removes a saved theme.
Saved themes live as one JSON file each in the extension's global storage, so a theme can be
copied to a colleague on its own rather than pasted out of settings.
Editing any field switches the picker to Custom, because the form is then no longer the theme
that was chosen. The theme's name travels into the container, which is how the container list can
say bc27 (5-nodes).
Different containers can wear different themes: pick the container, pick the theme, apply.
Colour roles
| Setting |
What it paints |
| Navigation bar color |
the bar background, the product-name button, the icon button borders |
| Text and icon color |
the product name, the environment name, the icon glyphs |
| Accent color |
the hover state of the icon buttons and the product-name button |
The indicators beside each field use the WCAG contrast formula against a 4.5:1 minimum, so they
agree with the Microsoft 365 page. The 5-nodes bar passes at 9.51:1; its red accent is 3.56:1 and
is flagged, exactly as the Microsoft 365 theme page flags it.
Logos
A logo is drawn at the left of the bar, before the product name, and links to the on-click URL if
one is set. Fully transparent edges are trimmed when the file is loaded, so the height applies to
the mark rather than to whatever padding it was exported with. The 5-nodes mark, for instance,
fills 46% of its square canvas, so without trimming a 24px height would draw a 11px mark. SVG is
left alone, since rasterising it would throw away the one thing it is good at.
Settings
| Setting |
Default |
bcThemeManager.extensionsPath |
C:\ProgramData\BcContainerHelper\Extensions |
bcThemeManager.dockerPath |
docker |
bcThemeManager.defaultPreset |
5-nodes |
What Apply actually does
- Writes
bc-theme.json into <extensionsPath>\<container>\my, which the container sees as
C:\run\my.
- Copies
Set-BcTheme.ps1 and AdditionalSetup.ps1 into the same folder.
- Runs
docker exec <container> powershell -File C:\run\my\Set-BcTheme.ps1.
AdditionalSetup.ps1 is dot-sourced by bccontainerhelper's navstart.ps1 on every container
start, so the theme survives a restart or a web client reinstall. The stock
C:\run\AdditionalSetup.ps1 it overrides ships intentionally empty, and the extension refuses to
overwrite an AdditionalSetup.ps1 that does not carry its own marker.
Revert restores the original assets from the .orig backups the patcher makes, removes the startup
hook and deletes bc-theme.json.
Why a container tool and not a page inside Business Central
BC on-premises has no supported setting for any of this, and the Microsoft 365 organisation
branding that themes the bar in BC online never reaches a container. The colours are baked into the
web client's own assets under C:\inetpub\wwwroot\BC\wwwroot, so applying a theme means writing
files inside the container. Only something running on the host can do that.
Four traps in the web client
Each one silently defeats a patch that does not account for it, and each is a comment in
resources/Set-BcTheme.ps1.
- The obvious bundle is the wrong one. The header CSS is a webpack CSS module and webpack
copies it into every chunk that imports it.
client.js is not the copy that wins: the bar is
built by designer.productmenubarcontrolfactory.chunk.js, whose <style> is injected later and
beats client.js at equal specificity.
- The stylesheet the page links is not the one it uses. The page's
<link> names
desktop.css, but the running client loads designer.css and projectmadeira.css.
- The top strip is set inline. The client writes
background-color: black straight onto
#shell-container at runtime, and an inline declaration outranks any normal rule, so that one
is written !important.
- The client runs in two frames.
designer.js boots the outer shell frame that owns
#shell-container and the header; client.js boots an inner frame for the page. Script
appended to client.js runs against a window with no header in it, does nothing, and reports no
error at all. The logo injection goes into designer.js.
Developing
npm install
npm run compile # tsc -p ./
npm test # compile, then node --test
F5 runs the extension in a second VS Code window.
The layers are deliberately separated so most of it can be tested without starting VS Code:
| Layer |
Imports vscode |
Tested by |
src/bc/* |
no |
node --test |
src/webviewHtml.ts |
no |
node --test |
media/theme.{css,js} |
no |
a headless browser, serving the real document under its real CSP |
src/extension.ts, src/panel.ts |
yes |
the packaging tests, plus F5 |
Set-BcTheme.ps1 is idempotent: it patches from a pristine .orig copy every time, and decides
which copy is pristine by looking for the stock colours rather than by trusting that a backup
exists. That is what makes a BC upgrade safe, because a reinstalled asset looks stock and becomes
the new baseline instead of being overwritten by a backup of the previous version.
It reads and writes through [IO.File]::ReadAllText/WriteAllText with an explicit UTF-8 encoding.
PowerShell 5.1's Get-Content/Set-Content would re-encode as ANSI and mangle 2077 non-ASCII
characters in client.js alone. To check a patch by hand, swap the colours back and confirm the
text is byte-identical to the .orig.
Requirements
Windows, Docker, and containers created with
bccontainerhelper, which is what provides the
my folder mounted as C:\run\my.