Darkmode flickers on page load #11901

Open
opened 2025-12-29 21:51:20 +01:00 by adam · 4 comments
Owner

Originally created by @UnknownTy on GitHub (Dec 5, 2025).

NetBox Edition

NetBox Community

NetBox Version

v4.4.7

Python Version

3.12

Steps to Reproduce

  1. Open Netbox in Chrome
  2. Enable darkmode
  3. Swap between different pages / Refresh page

https://github.com/user-attachments/assets/dd295383-d506-45a3-81fd-20c4d1e2720f

Expected Behavior

Darkmode remains consistent when changing pages.

Observed Behavior

Darkmode flickers when changing pages, briefly showing default color mode.

Originally created by @UnknownTy on GitHub (Dec 5, 2025). ### NetBox Edition NetBox Community ### NetBox Version v4.4.7 ### Python Version 3.12 ### Steps to Reproduce 1. Open Netbox in Chrome 2. Enable darkmode 3. Swap between different pages / Refresh page https://github.com/user-attachments/assets/dd295383-d506-45a3-81fd-20c4d1e2720f ### Expected Behavior Darkmode remains consistent when changing pages. ### Observed Behavior Darkmode flickers when changing pages, briefly showing default color mode.
adam added the type: bugstatus: needs ownernetboxseverity: low labels 2025-12-29 21:51:20 +01:00
Author
Owner

@jnovinger commented on GitHub (Dec 6, 2025):

Thanks for the report. This is a known limitation, when navigating between pages, the browser briefly renders before setmode.js executes and reapplies your dark mode preference from localStorage. It could maybe be eliminated by inlining the script in the template, but it's purely cosmetic and the core team isn't likely to prioritize it soon. We'd welcome a community contribution if someone wants to tackle it.

Related: #15340, #16003

@jnovinger commented on GitHub (Dec 6, 2025): Thanks for the report. This is a known limitation, when navigating between pages, the browser briefly renders before `setmode.js` executes and reapplies your dark mode preference from `localStorage`. It could maybe be eliminated by inlining the script in the template, but it's purely cosmetic and the core team isn't likely to prioritize it soon. We'd welcome a community contribution if someone wants to tackle it. Related: #15340, #16003
Author
Owner

@UnknownTy commented on GitHub (Dec 6, 2025):

Heck I'll take a look. Might be cosmetic but I'm hating this flashbang every time I swap pages ;)

@jnovinger is there any reason why the CSS styling applies to body instead of html/root for dark mode styling?

My best guess at the issue is the rework to how the dark mode works in the 4.x release to be client-side entirely. (This has been around for a year and no one complained? I might be wrong.)

This section updates the theme:

function updateElements(targetMode: ColorMode): void {
  const body = document.querySelector('body');
  if (body && targetMode == 'dark') {
    body.setAttribute('data-bs-theme', 'dark');
  } else if (body) {
    body.setAttribute('data-bs-theme', 'light');
  }

This script is being run after the page renders to update the body's data-bs-theme attribute, however if the body exists the user can already see it causing a flicker.

There is setmode.js in static which loads before the DOM, but this can't set the body attribute (AFAIK) since it's run before the body is created.

function setMode(mode) {
    document.documentElement.setAttribute("data-bs-theme", mode);
    localStorage.setItem("netbox-color-mode", mode);
}

A potential way to do this is to have the dark mode styling refer to the <html> element instead of <body>, and have the updateElements function update the <html> attribute.
E.g.:

function updateElements(targetMode: ColorMode): void {
  if (targetMode == 'dark') {
   document.documentElement.setAttribute("data-bs-theme", 'dark');
  } else {
   document.documentElement.setAttribute("data-bs-theme", 'light');
  }

^^ Similar to setMode

html[data-bs-theme=light] .navbar-vertical.navbar-expand-lg {
  // Background Gradient
  background: linear-gradient(180deg, rgba(0, 133, 125, 0.00) 0%, rgba(0, 133, 125, 0.10) 100%), #FFF;
}

^^ Update styling to use root

Some very quick tests (Local file overwrites for netbox.css & rewriting colorMode.ts) seems to work.
Just want some clarity on why CSS is body instead of root and I might make a PR.

@UnknownTy commented on GitHub (Dec 6, 2025): Heck I'll take a look. Might be cosmetic but I'm hating this flashbang every time I swap pages ;) @jnovinger is there any reason why the CSS styling applies to `body` instead of `html`/`root` for dark mode styling? My best guess at the issue is the rework to how the dark mode works in the 4.x release to be client-side entirely. (This has been around for a year and no one complained? I might be wrong.) This section updates the theme: ```ts function updateElements(targetMode: ColorMode): void { const body = document.querySelector('body'); if (body && targetMode == 'dark') { body.setAttribute('data-bs-theme', 'dark'); } else if (body) { body.setAttribute('data-bs-theme', 'light'); } ``` This script is being run after the page renders to update the body's `data-bs-theme` attribute, however if the body exists the user can already see it causing a flicker. There is `setmode.js` in static which loads before the DOM, but this can't set the body attribute (AFAIK) since it's run before the body is created. ```js function setMode(mode) { document.documentElement.setAttribute("data-bs-theme", mode); localStorage.setItem("netbox-color-mode", mode); } ``` A potential way to do this is to have the dark mode styling refer to the `<html>` element instead of `<body>`, and have the `updateElements` function update the `<html>` attribute. E.g.: ```ts function updateElements(targetMode: ColorMode): void { if (targetMode == 'dark') { document.documentElement.setAttribute("data-bs-theme", 'dark'); } else { document.documentElement.setAttribute("data-bs-theme", 'light'); } ``` ^^ Similar to `setMode` ```css html[data-bs-theme=light] .navbar-vertical.navbar-expand-lg { // Background Gradient background: linear-gradient(180deg, rgba(0, 133, 125, 0.00) 0%, rgba(0, 133, 125, 0.10) 100%), #FFF; } ``` ^^ Update styling to use root Some very quick tests (Local file overwrites for netbox.css & rewriting `colorMode.ts`) seems to work. Just want some clarity on why CSS is `body` instead of `root` and I might make a PR.
Author
Owner

@jnovinger commented on GitHub (Dec 6, 2025):

Just want some clarity on why CSS is body instead of root and I might make a PR.

That's before my time, so I'm not sure without more digging (probably in those related issues).

@jnovinger commented on GitHub (Dec 6, 2025): > Just want some clarity on why CSS is body instead of root and I might make a PR. That's before my time, so I'm not sure without more digging (probably in those related issues).
Author
Owner

@UnknownTy commented on GitHub (Dec 7, 2025):

Looks like it's from @jeremystretch when restoring Darkmode functionality after removing it to work with Tabler:
cdba308597

What I think happened is Darkmode was removed temporarily, then restored by assigning the new theme attribute to the body rather than root. From there future .SCSS contributions just referred to it by the body since that's where the attribute was being updated.

Heck another commit from ~2 weeks ago points out Tabler expects data-bs-theme to be using root rather than body as well:
6efb258b9f/netbox/project-static/styles/overrides/_tabler.scss (L166-L179)

@jeremystretch since I see you're a common contributor here could you comment on why it's on body rather than root?
I'm not much of a web dev so there might be some reason I don't know.

Edit: Just read the following in contributing.md. Sorry for pings!

Please avoid pinging members with @ unless they've previously expressed interest or involvement with that particular issue.

@UnknownTy commented on GitHub (Dec 7, 2025): Looks like it's from @jeremystretch when restoring Darkmode functionality after removing it to work with Tabler: https://github.com/netbox-community/netbox/pull/14833/commits/cdba3085975ffc453d213ceb282c25f065d8de33 What I think happened is Darkmode was removed temporarily, then restored by assigning the new theme attribute to the `body` rather than `root`. From there future `.SCSS` contributions just referred to it by the body since that's where the attribute was being updated. Heck another commit from ~2 weeks ago points out Tabler expects `data-bs-theme` to be using root rather than body as well: https://github.com/netbox-community/netbox/blame/6efb258b9fe7281535a88ea729cdf5c64619684f/netbox/project-static/styles/overrides/_tabler.scss#L166-L179 @jeremystretch since I see you're a common contributor here could you comment on why it's on `body` rather than `root`? I'm not much of a web dev so there might be some reason I don't know. Edit: Just read the following in `contributing.md`. Sorry for pings! > Please avoid pinging members with @ unless they've previously expressed interest or involvement with that particular issue.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#11901