1
mirror of https://github.com/jakejarvis/jarv.is.git synced 2025-04-26 14:08:29 -04:00

move light/dark class from <body> to <html> and inline restoration script to (hopefully) improve the white flashes

This commit is contained in:
Jake Jarvis 2021-12-15 13:49:56 -05:00
parent c2789e24d4
commit 766362a9da
Signed by: jake
GPG Key ID: 2B0C9CF251E69A39
14 changed files with 61 additions and 67 deletions

View File

@ -0,0 +1,21 @@
/* eslint-disable no-var */
// A super tiny script to restore dark mode off the bat (to hopefully avoid blinding flashes of white).
// NOTE: This is inlined by Hugo/esbuild (see layouts/partials/head/restore-theme.html) instead of Webpack.
import { getDarkPref } from "./src/utils/theme.js";
try {
var cl = document.documentElement.classList;
var pref = getDarkPref();
// set `<html class="dark">` if either the user has explicitly toggled in the past or if their OS is in dark mode
if (pref === "true" || (!pref && window.matchMedia("(prefers-color-scheme: dark)").matches)) {
cl.remove("light");
cl.add("dark");
}
// TODO: fix real-time switching (works but bulb icon isn't updated)
// window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (e) => e.matches && setDark(true));
// window.matchMedia("(prefers-color-scheme: light)").addEventListener("change", (e) => e.matches && setDark(false));
} catch (error) {}

View File

@ -1,6 +1,6 @@
import { h } from "preact";
import { useState, useEffect } from "preact/hooks";
import { isDark, setDarkClass, setDarkPref } from "../utils/theme.js";
import { isDark, setDarkPref } from "../utils/theme.js";
// react components:
import BulbOn from "../assets/bulb-on.svg";
@ -11,7 +11,9 @@ const ThemeToggle = () => {
const [dark, setDark] = useState(isDark());
useEffect(() => {
return setDarkClass(dark);
// sets appropriate `<html class="...">`
document.documentElement.classList.remove("dark", "light");
document.documentElement.classList.add(dark ? "dark" : "light");
}, [dark]);
const handleToggle = () => {

View File

@ -1,28 +1,8 @@
import { h, render } from "preact";
import { getDarkPref, setDarkClass } from "./utils/theme.js";
// react components:
import ThemeToggle from "./components/ThemeToggle.js";
// check for existing preference in local storage
const pref = getDarkPref();
// do initialization before *any* react-related stuff to avoid white flashes as much as possible
if (pref) {
// restore user's preference if they've explicitly toggled it in the past
setDarkClass(pref === "true");
} else {
// check for OS dark mode preference and switch accordingly
// https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme
try {
setDarkClass(window.matchMedia("(prefers-color-scheme: dark)").matches);
} catch (e) {}
// TODO: fix real-time switching (works but bulb icon isn't updated)
// window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (e) => e.matches && setDark(true));
// window.matchMedia("(prefers-color-scheme: light)").addEventListener("change", (e) => e.matches && setDark(false));
}
// finally render the nifty lightbulb in the header
if (typeof window !== "undefined" && document.querySelector(".theme-toggle")) {
render(<ThemeToggle />, document.querySelector(".theme-toggle"));

View File

@ -3,17 +3,6 @@ const storageKey = "dark_mode";
export const getDarkPref = () => localStorage.getItem(storageKey);
export const setDarkPref = (pref) => localStorage.setItem(storageKey, pref);
// use the body class as a hint to what the theme was set to outside of the button component
// use the `<html class="...">` as a hint to what the theme was set to outside of the button component
// there's probably (definitely) a cleaner way to do this..?
export const isDark = () => document.body.classList?.contains("dark");
// sets appropriate `<body class="...">`
export const setDarkClass = (dark) => {
if (dark) {
document.body.classList.add("dark");
document.body.classList.remove("light");
} else {
document.body.classList.add("light");
document.body.classList.remove("dark");
}
};
export const isDark = () => document.documentElement.classList.contains("dark");

View File

@ -1,25 +1,18 @@
@use "sass:map";
// Takes a map of CSS properties and theme keys (see below) and set both body.light and body.dark selectors.
// Takes a map of CSS properties and theme keys (see below) and set both html.light and html.dark selectors.
// ex. @include themes.themed((color: "text", background-color: "background-inner"));
@mixin themed($properties) {
// keep track of the original selector(s) calling this mixin for below
$selectors: #{&};
// add corresponding body.light and body.dark root selectors
// add corresponding html.light and html.dark root selectors
@each $theme, $map in $themes {
@at-root body.#{$theme} {
// support theming root body element
@if $selectors == "body" {
@at-root html.#{$theme} {
#{$selectors} {
@each $property, $color in $properties {
#{$property}: map.get($map, $color);
}
} @else {
#{$selectors} {
@each $property, $color in $properties {
#{$property}: map.get($map, $color);
}
}
}
}
}

View File

@ -100,7 +100,7 @@ main {
// cool link underlines via messy SCSS hacking (see ../abstracts/_functions)
@each $theme, $map in themes.$themes {
@at-root body.#{$theme} #{&} {
@at-root html.#{$theme} #{&} {
background-image: functions.underline-hack(map.get($map, "links"), map.get($map, "background-inner"));
}
}

View File

@ -110,23 +110,25 @@ header {
}
// Dark mode toggle
&.theme-toggle button {
border: 0;
padding: 0;
background: none;
margin: 0.3em -0.3em 0 1.4em;
cursor: pointer;
&.theme-toggle {
margin-left: 1.4em;
svg {
width: 1.56em; // 24.33px, don't ask
height: 1.56em;
button {
border: 0;
padding: 0;
background: none;
margin: 0.3em -0.3em 0 0;
cursor: pointer;
svg {
width: 1.56em; // 24.33px, don't ask
height: 1.56em;
}
}
}
// no margin on the first child to make more room on narrow windows (before responsiveness kicks in).
// last child is the dark mode toggle -- margin is set directly on it in case it's hidden (if JS is disabled).
&:first-child,
&:last-child {
// no margin on the first child to make more room on narrow windows (before responsiveness kicks in)
&:first-child {
margin-left: 0;
}
}

View File

@ -129,7 +129,7 @@ div.highlight {
}
// Syntax Highlighting (light) - modified from Monokai Light: https://github.com/mlgill/pygments-style-monokailight
body.light {
html.light {
div.highlight,
button.copy-button,
:not(pre) > code {
@ -215,7 +215,7 @@ body.light {
}
// Syntax Highlighting (dark) - modified from Dracula: https://github.com/dracula/pygments
body.dark {
html.dark {
div.highlight,
button.copy-button,
:not(pre) > code {

View File

@ -200,7 +200,7 @@ div.layout-home {
// Loop through $colors-home (see above)
@each $id, $colors in $colors-home {
@each $theme, $color in $colors {
@at-root body.#{$theme} div.layout-home a##{$id} {
@at-root html.#{$theme} div.layout-home a##{$id} {
color: $color;
background-image: functions.underline-hack($color, map.get(map.get(themes.$themes, $theme), "background-inner"));
}

View File

@ -17,7 +17,7 @@ css: |
div#content a#octocat svg {
vertical-align: text-bottom;
}
body.dark div#content a#octocat svg path {
html.dark div#content a#octocat svg path {
fill: #d3d3d3;
}
draft: false

View File

@ -72,7 +72,7 @@ function optimizeHtml() {
collapseBooleanAttributes: true,
removeComments: true,
minifyCSS: true,
minifyJS: false,
minifyJS: true,
})
)
.pipe(gulp.dest(".", { overwrite: true }));

View File

@ -7,11 +7,11 @@
{{- partial "functions/prepare-js" . -}}
{{- partial "functions/prepare-css" . -}}
<html lang="{{ .Site.LanguageCode | default "en" }}">
<html lang="{{ .Site.LanguageCode | default "en" }}" class="{{ .Site.Params.Theme.defaultTheme }}">
<head>
{{ partial "head/_head" . }}
</head>
<body class="{{ .Site.Params.Theme.defaultTheme }}">
<body>
{{ partialCached "page/header" . }}
<main>
{{ block "main" . }}{{ end }}

View File

@ -1,6 +1,7 @@
{{ partial "head/meta" . -}}
{{ partial "head/open-graph" . -}}
{{ partial "head/canonical" . -}}
{{ partialCached "head/restore-theme" . -}}
{{ partialCached "head/preload" . -}}
{{ partial "head/styles" . -}}
{{ partialCached "head/favicons" . -}}

View File

@ -0,0 +1,6 @@
{{ $themeScript := resources.Get "js/restore-theme.js" | js.Build (dict "target" "es2015" "format" "iife") }}
{{ with $themeScript }}
<script>
{{ .Content | safeJS }}
</script>
{{ end }}