From 2590e94ca19c594ebb0b8a421b6b68917ddbcb62 Mon Sep 17 00:00:00 2001 From: Remo Senekowitsch Date: Thu, 26 Dec 2024 13:01:31 +0100 Subject: [PATCH] Avoid theme flicker if system setting is dark Previously, `@media (prefers-color-scheme: dark)` was only used in the `noscript` stylesheet, but that's the best way to immediately set the dark theme without any flickering. That is now integrated into the the main stylesheet. The flickering is still present if the user has a default system setting of light mode but explicitly set it to dark. Actually, this introduces flickering from dark to light if the user has a system setting of dark but an explicit override of light. So it goes both ways. I think this is a good tradeoff, most users will want the website to just follow the system setting. The `theme-switch.js` can also be simplified a little, since it's not necessary anymore to set the `data-theme` attribute if there is no explicit override. --- src/styles/app.scss | 109 +++++++++++++++++++++++++-------- src/styles/noscript.scss | 49 --------------- static/scripts/theme-switch.js | 21 ++----- 3 files changed, 88 insertions(+), 91 deletions(-) diff --git a/src/styles/app.scss b/src/styles/app.scss index 2d53046af..4f41ed6ee 100644 --- a/src/styles/app.scss +++ b/src/styles/app.scss @@ -6,7 +6,7 @@ $header-font: 'Alfa Slab One', serif; // Switching theme will only work if JavaScript is enabled as well // Default light theme -:root, :root:not([data-theme]) { +:root { --gray: #2a3439; --red: #a72145; --yellow: #ffc832; @@ -33,32 +33,89 @@ $header-font: 'Alfa Slab One', serif; --rust-logo-filter: initial; } -// Dark theme +// Dark theme variables +:root { + --dark-theme-gray: #2a3439; + --dark-theme-red: #a72145; + --dark-theme-yellow: #ffc832; + --dark-theme-code-color: white; + --dark-theme-code-bg-color: rgba(213, 203, 198, 0.05); + --dark-theme-code-border-color: rgba(213, 203, 198, 0.25); + --dark-theme-blockquote-color: rgb(195, 205, 210); + --dark-theme-blockquote-bg-color: rgba(213, 203, 198, 0.05); + --dark-theme-blockquote-left-border-color: rgb(195, 205, 210); + --dark-theme-body-background-color: #181a1b; + --dark-theme-body-foreground-color: #e8e6e3; + --dark-theme-body-color: white; + --dark-theme-div-brand-a-color: white; + --dark-theme-white-elem-color: white; + --dark-theme-white-elem-a: #d5cbc6; + --dark-theme-nav-links-a: #d5cbc6; + --dark-theme-publish-date-author: #d5cbc6; + --dark-theme-section-header-h2-color: white; + --dark-theme-theme-icon: #43484d; + --dark-theme-theme-popup-border: #43484d; + --dark-theme-theme-popup-bg: #141617; + --dark-theme-theme-hover: #474c51; + --dark-theme-theme-choice-color: #d5cbc6; + --dark-theme-rust-logo-filter: drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff); +} + +// Obey dark theme system preference unless data-theme is set explicitly +@media (prefers-color-scheme: dark) { + :root:not([data-theme]) { + --gray: var(--dark-theme-gray); + --red: var(--dark-theme-red); + --yellow: var(--dark-theme-yellow); + --code-color: var(--dark-theme-code-color); + --code-bg-color: var(--dark-theme-code-bg-color); + --code-border-color: var(--dark-theme-code-border-color); + --blockquote-color: var(--dark-theme-blockquote-color); + --blockquote-bg-color: var(--dark-theme-blockquote-bg-color); + --blockquote-left-border-color: var(--dark-theme-blockquote-left-border-color); + --body-background-color: var(--dark-theme-body-background-color); + --body-foreground-color: var(--dark-theme-body-foreground-color); + --body-color: var(--dark-theme-body-color); + --div-brand-a-color: var(--dark-theme-div-brand-a-color); + --white-elem-color: var(--dark-theme-white-elem-color); + --white-elem-a: var(--dark-theme-white-elem-a); + --nav-links-a: var(--dark-theme-nav-links-a); + --publish-date-author: var(--dark-theme-publish-date-author); + --section-header-h2-color: var(--dark-theme-section-header-h2-color); + --theme-icon: var(--dark-theme-theme-icon); + --theme-popup-border: var(--dark-theme-theme-popup-border); + --theme-popup-bg: var(--dark-theme-theme-popup-bg); + --theme-hover: var(--dark-theme-theme-hover); + --theme-choice-color: var(--dark-theme-theme-choice-color); + --rust-logo-filter: var(--dark-theme-rust-logo-filter); + } +} +// Set dark theme based on explicit data-theme setting :root[data-theme='dark'] { - --gray: #2a3439; - --red: #a72145; - --yellow: #ffc832; - --code-color: white; - --code-bg-color: rgba(213, 203, 198, 0.05); - --code-border-color: rgba(213, 203, 198, 0.25); - --blockquote-color: rgb(195, 205, 210); - --blockquote-bg-color: rgba(213, 203, 198, 0.05); - --blockquote-left-border-color: rgb(195, 205, 210); - --body-background-color: #181a1b; - --body-foreground-color: #e8e6e3; - --body-color: white; - --div-brand-a-color: white; - --white-elem-color: white; - --white-elem-a: #d5cbc6; - --nav-links-a: #d5cbc6; - --publish-date-author: #d5cbc6; - --section-header-h2-color: white; - --theme-icon: #43484d; - --theme-popup-border: #43484d; - --theme-popup-bg: #141617; - --theme-hover: #474c51; - --theme-choice-color: #d5cbc6; - --rust-logo-filter: drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff); + --gray: var(--dark-theme-gray); + --red: var(--dark-theme-red); + --yellow: var(--dark-theme-yellow); + --code-color: var(--dark-theme-code-color); + --code-bg-color: var(--dark-theme-code-bg-color); + --code-border-color: var(--dark-theme-code-border-color); + --blockquote-color: var(--dark-theme-blockquote-color); + --blockquote-bg-color: var(--dark-theme-blockquote-bg-color); + --blockquote-left-border-color: var(--dark-theme-blockquote-left-border-color); + --body-background-color: var(--dark-theme-body-background-color); + --body-foreground-color: var(--dark-theme-body-foreground-color); + --body-color: var(--dark-theme-body-color); + --div-brand-a-color: var(--dark-theme-div-brand-a-color); + --white-elem-color: var(--dark-theme-white-elem-color); + --white-elem-a: var(--dark-theme-white-elem-a); + --nav-links-a: var(--dark-theme-nav-links-a); + --publish-date-author: var(--dark-theme-publish-date-author); + --section-header-h2-color: var(--dark-theme-section-header-h2-color); + --theme-icon: var(--dark-theme-theme-icon); + --theme-popup-border: var(--dark-theme-theme-popup-border); + --theme-popup-bg: var(--dark-theme-theme-popup-bg); + --theme-hover: var(--dark-theme-theme-hover); + --theme-choice-color: var(--dark-theme-theme-choice-color); + --rust-logo-filter: var(--dark-theme-rust-logo-filter); } html { diff --git a/src/styles/noscript.scss b/src/styles/noscript.scss index 94a877e0c..138f8a52c 100644 --- a/src/styles/noscript.scss +++ b/src/styles/noscript.scss @@ -1,53 +1,4 @@ // This stylesheet is used when the user agent has no JavaScript capabilities (or has it disabled) -// Sets dark theme according to user agent preferences - -// Default light theme -:root, :root:not([data-theme]) { - --gray: #2a3439; - --red: #a72145; - --yellow: #ffc832; - --code-color: black; - --code-bg-color: rgba(42, 52, 57, 0.05); - --code-border-color: rgba(42, 52, 57, 0.25); - --blockquote-color: black; - --blockquote-bg-color: rgb(247, 249, 249); - --blockquote-left-border-color: rgb(195, 205, 210); - --body-background-color: white; - --body-foreground-color: white; - --body-color: rgb(34,34,34); - --div-brand-a-color: black; - --white-elem-color: black; - --white-a: #2a3439; - --nav-links-a: #2a3439; - --publish-date-author: #2a3439; - --section-header-h2-color: black; - --rust-logo-filter: initial; -} - -// Dark theme (probed from user prefs) -@media (prefers-color-scheme: dark) { - :root, :root:not([data-theme]) { - --gray: #2a3439; - --red: #a72145; - --yellow: #ffc832; - --code-color: white; - --code-bg-color: rgba(213, 203, 198, 0.05); - --code-border-color: rgba(213, 203, 198, 0.25); - --blockquote-color: rgb(195, 205, 210); - --blockquote-bg-color: rgba(213, 203, 198, 0.05); - --blockquote-left-border-color: rgb(195, 205, 210); - --body-background-color: #181a1b; - --body-foreground-color: #e8e6e3; - --body-color: white; - --div-brand-a-color: white; - --white-elem-color: white; - --white-elem-a: #d5cbc6; - --nav-links-a: #d5cbc6; - --publish-date-author: #d5cbc6; - --section-header-h2-color: white; - --rust-logo-filter: drop-shadow(1px 0 0px #fff) drop-shadow(0 1px 0 #fff) drop-shadow(-1px 0 0 #fff) drop-shadow(0 -1px 0 #fff); - } -} // Don't show the theme selector button when JavaScript is disabled #theme-icon { diff --git a/static/scripts/theme-switch.js b/static/scripts/theme-switch.js index b8ffcd7a7..6cf44b06c 100644 --- a/static/scripts/theme-switch.js +++ b/static/scripts/theme-switch.js @@ -2,7 +2,7 @@ function changeThemeTo(val) { if (val === "system") { - setThemeToSystemPref(); + document.documentElement.removeAttribute("data-theme"); // delete explicit theme pref from browser storage if (storageAvailable("localStorage")) { localStorage.removeItem("blog-rust-lang-org-theme"); @@ -52,26 +52,15 @@ function handleBlur(event) { } } -function setThemeToSystemPref() { - if (window.matchMedia("(prefers-color-scheme: dark)").matches) { - document.documentElement.setAttribute("data-theme", "dark"); - } else { - document.documentElement.setAttribute("data-theme", "light"); - } -} - // close the theme dropdown if clicking somewhere else document.querySelector('.theme-icon').onblur = handleBlur; // Check for saved user preference on load, else check and save user agent prefs -let savedTheme = null; if (storageAvailable("localStorage")) { - savedTheme = localStorage.getItem("blog-rust-lang-org-theme"); -} -if (savedTheme) { - document.documentElement.setAttribute("data-theme", savedTheme); -} else { - setThemeToSystemPref(); + const savedTheme = localStorage.getItem("blog-rust-lang-org-theme"); + if (savedTheme) { + document.documentElement.setAttribute("data-theme", savedTheme); + } } // show the theme selector only if JavaScript is enabled/available