Dark mode in Power Pages π
Posted on March 29, 2025 (Last modified on March 31, 2025) • 5 min read • 995 wordsResearch by Android Authority found that nearly 82% of smartphone users use dark mode which isn’t surprising given the numerous benefits it offers. Although I might be a bit biased as a devβ¦
With that in mind, I thought I would share a way to add dark mode to Power Pages as it’s a key feature for improving end-user experience.
There will be a bonus at the end, in that we will store the theme preference in the browser so that when the user returns to the website it remembers their theme preference!
Important
I am using the LUX theme from Bootswatch so feel free to checkout this blog where I go through how you can apply a custom theme to Power Pages.
Also, make sure you deactivate the basicportaltheme.css as we don’t want this to come through to the design.
Copy the below code into your theme.css web file to style the page.
:root {
--background-color: #ffffff;
--text-color: #000000;
}
[data-theme='dark'] {
--background-color: #1a1a1a;
--text-color: #ffffff;
}
[data-theme='light'] {
--background-color: #ffffff;
--text-color: #000000;
}
body {
background-color: var(--background-color);
color: var(--text-color);
transition: background-color 0.5s ease;
}
.nav-link {
color: var(--text-color);
transition: inherit;
}
.navbar-brand {
color: var(--text-color);
transition: inherit;
}
.bi-moon-stars-fill:hover {
color: #f0c420;
transition: color 0.3s ease;
}
.bi-brightness-high-fill:hover {
color: #fc9601;
transition: color 0.3s ease;
}
For the content and logic of the page replace your headerwebtemplate.html code with the below.
Note
For the sake of a demo I have added the javascript code to a web template, it’s best practice for reusable functions to be stored in a js file.
<nav class="navbar navbar-expand-lg">
<div class="container-fluid">
<div class="navbar-brand" href="#">{{ snippets['Site name'] }}</div>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
{% assign nav = weblinks['Default'] %}
{% if nav %}
{% for link in nav.weblinks %}
<li class="nav-item">
<a class="nav-link" aria-current="page">{{ link.name | escape }}</a>
</li>
{% endfor %}
{% endif %}
</ul>
<div class="form-check form-switch ms-auto" id="theme-toggle">
<i class="bi bi-brightness-high-fill fs-3 pe-5" id="theme-icon"></i>
</div>
</div>
</div>
</nav>
<!-- Script to handle toggling theme dynamically -->
<script>
const themeIcon = document.getElementById("theme-icon");
const rootElement = document.documentElement;
const savedTheme = localStorage.getItem("theme") || "light";
rootElement.setAttribute("data-theme", savedTheme);
const setLocalThemeIcon = (localtheme, themeicon) => {
if (localtheme === "dark") {
themeicon.classList.remove("bi-brightness-high-fill");
themeicon.classList.add("bi-moon-stars-fill");
} else if (localtheme === "light") {
themeIcon.classList.remove("bi-moon-stars-fill");
themeIcon.classList.add("bi-brightness-high-fill");
}
}
if (savedTheme) {
setLocalThemeIcon(savedTheme, themeIcon);
}
themeIcon.addEventListener("click", () => {
// switch Boostrap icon
if (themeIcon.classList.contains("bi-moon-stars-fill")) {
themeIcon.classList.remove("bi-moon-stars-fill");
themeIcon.classList.add("bi-brightness-high-fill");
} else {
themeIcon.classList.remove("bi-brightness-high-fill");
themeIcon.classList.add("bi-moon-stars-fill");
}
// set theme
const currentTheme = rootElement.getAttribute("data-theme");
const newTheme = currentTheme === "dark" ? "light" : "dark";
rootElement.setAttribute("data-theme", newTheme);
// set localstorage to remember theme and toggle value
localStorage.setItem("theme", newTheme);
});
</script>
:root {
--background-color: #ffffff;
--text-color: #000000;
}
[data-theme='dark'] {
--background-color: #1a1a1a;
--text-color: #ffffff;
}
[data-theme='light'] {
--background-color: #ffffff;
--text-color: #000000;
}
body {
background-color: var(--background-color);
color: var(--text-color);
transition: background-color 0.5s ease;
}
.nav-link {
color: var(--text-color);
transition: inherit;
}
.navbar-brand {
color: var(--text-color);
transition: inherit;
}
.bi-moon-stars-fill:hover {
color: #f0c420;
transition: color 0.3s ease;
}
.bi-brightness-high-fill:hover {
color: #fc9601;
transition: color 0.3s ease;
}
<nav class="navbar navbar-expand-lg">
<div class="container-fluid">
<div class="navbar-brand" href="#">{{ snippets['Site name'] }}</div>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
{% assign nav = weblinks['Default'] %}
{% if nav %}
{% for link in nav.weblinks %}
<li class="nav-item">
<a class="nav-link" aria-current="page">{{ link.name | escape }}</a>
</li>
{% endfor %}
{% endif %}
</ul>
<div class="form-check form-switch ms-auto" id="theme-toggle">
<i class="bi bi-brightness-high-fill fs-3 pe-5" id="theme-icon"></i>
</div>
</div>
</div>
</nav>
<!-- Script to handle toggling theme dynamically -->
<script>
const themeIcon = document.getElementById("theme-icon");
const rootElement = document.documentElement;
const savedTheme = localStorage.getItem("theme") || "light";
rootElement.setAttribute("data-theme", savedTheme);
const setLocalThemeIcon = (localtheme, themeicon) => {
if (localtheme === "dark") {
themeicon.classList.remove("bi-brightness-high-fill");
themeicon.classList.add("bi-moon-stars-fill");
} else if (localtheme === "light") {
themeIcon.classList.remove("bi-moon-stars-fill");
themeIcon.classList.add("bi-brightness-high-fill");
}
}
if (savedTheme) {
setLocalThemeIcon(savedTheme, themeIcon);
}
themeIcon.addEventListener("click", () => {
// switch Boostrap icon
if (themeIcon.classList.contains("bi-moon-stars-fill")) {
themeIcon.classList.remove("bi-moon-stars-fill");
themeIcon.classList.add("bi-brightness-high-fill");
} else {
themeIcon.classList.remove("bi-brightness-high-fill");
themeIcon.classList.add("bi-moon-stars-fill");
}
// set theme
const currentTheme = rootElement.getAttribute("data-theme");
const newTheme = currentTheme === "dark" ? "light" : "dark";
rootElement.setAttribute("data-theme", newTheme);
// set localstorage to remember theme and toggle value
localStorage.setItem("theme", newTheme);
});
</script>
To finish it off just add a image to a web page and you should be able to see the header come through.
I will have a little rant and say this would be so much easier if Power Pages supported Bootstrap 5.3 as this version includes colour modes (dark/light mode)π©
Hopefully, we’ll see this feature in the future, but for now, it’s still possible to implement dark mode with a bit of effort.
As always, thanks for reading and have a great day!