fix: eliminate dark-mode flash in MonochromeUI/NewBootstrap
Closes #343
What does this PR aim to accomplish?
The MonochromeUI dark mode flashed a light-coloured page on every load. This happened because Bootstrap 5.3.3 was fetched from CDN at document-idle — the page painted with the old Bootstrap 3 light theme first, then snapped to dark once the CDN response arrived.
How does this PR accomplish the above?
Root cause: Bootstrap CSS was loaded asynchronously from CDN inside the main async IIFE, which runs after the page has already painted with the site's original Bootstrap 3 stylesheet.
Fix — three cooperating layers:
-
@run-at document-start+@resource BootstrapCSS
Bootstrap 5.3.3 CSS is now declared as a@resource(cached by the userscript manager on install) and a@requirefor its JS. An early synchronous IIFE runs before the first paint and:- reads
UserScript-Setting-ThemefromlocalStorageand setsdata-bs-themeon<html>immediately - injects Bootstrap 5.3.3 CSS from the local cache (no network round-trip)
- injects the correct skin CSS (
MonochromeSkinCSSorNewBootstrapSkinCSS, plus AddAnimation/AddColorText if enabled) - hides the page with
html { opacity: 0 !important }to prevent any CLS while the old stylesheets are still in the CSSOM - arms a
MutationObserverto intercept old Bootstrap/theme<link>tags as they are inserted by the HTML parser
- reads
-
IIFE-time cleanup (at
DOMContentLoaded)
The main async IIFE now waits forDOMContentLoadedat startup (needed since the script now runs atdocument-start). Immediately after that wait — beforeawait fetch(loginpage.php)— it:- disconnects the
MutationObserver - removes any old Bootstrap/theme
<link>elements still in the DOM (catches anything the preload scanner fetched before the observer could intercept) - removes the
opacity: 0hide style, revealing the page in its correct final state
- disconnects the
-
Late cleanup fallback
The original link-removal loop insideif (UtilityEnabled("NewBootstrap"))is kept as a belt-and-suspenders pass for edge cases.
First-install / cache-miss graceful degradation:
If GM_getResourceText("BootstrapCSS") returns null (resource not yet cached on first install), the early block exits without hiding the page or blocking stylesheets. The IIFE falls back to adding Bootstrap 5.3.3 CSS via CDN link (same as the old behaviour). After the first page load the resource is cached and all subsequent loads use the embedded path.
Other changes:
MonochromeSkinCSSandNewBootstrapSkinCSSextracted to top-levelconstso they are shared between the early block and the rest of the script (no duplication)- The
Styleelement created at document-idle no longer re-injects the skin CSS (already done early); it is kept for the dynamic CSS additions appended later in the IIFE
Confirmation
- I have read the contributor's guide
- My code includes comments where the logic is non-obvious
- I have tested my changes in a browser — Note: this was developed and iterated in a remote Claude Code session without direct browser access. The PR author confirmed behaviour at each iteration step. Final browser confirmation by a human reviewer is needed before merge.
- I am willing to provide support for this contribution if needed
- My contribution is compatible with GNU General Public License v3.0
- No duplicate PRs exist for this change
- This change provides value to the community (eliminates a jarring UX issue in dark mode)
- I accept that this PR may be rejected
- This is a freely given contribution
- Tested in both new UI and classic UI — only the new UI (MonochromeUI + NewBootstrap) path was changed; the classic UI code path is untouched. When
NewBootstrapis disabled the early block exits immediately and the script behaves identically to before.