mirror of
https://github.com/CaiJimmy/hugo-theme-stack.git
synced 2025-04-29 03:53:30 +08:00
feat(darkmode): initial support for dark mode toggle
This commit is contained in:
parent
df74fe5f19
commit
a3d191f818
26
assets/scss/partials/darkmode.scss
Normal file
26
assets/scss/partials/darkmode.scss
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
[data-scheme="dark"] {
|
||||||
|
#dark-mode-toggle {
|
||||||
|
color: var(--accent-color);
|
||||||
|
font-weight: 700;
|
||||||
|
|
||||||
|
.icon-tabler-toggle-left {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-tabler-toggle-right {
|
||||||
|
display: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#dark-mode-toggle {
|
||||||
|
margin-top: auto;
|
||||||
|
color: var(--body-text-color);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.icon-tabler-toggle-right {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
@ -3,11 +3,6 @@
|
|||||||
* https://xyproto.github.io/splash/docs/monokai.html
|
* https://xyproto.github.io/splash/docs/monokai.html
|
||||||
*/
|
*/
|
||||||
|
|
||||||
:root {
|
|
||||||
--pre-text-color: #f8f8f2;
|
|
||||||
--pre-background-color: #272822;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Background */
|
/* Background */
|
||||||
.chroma {
|
.chroma {
|
||||||
color: #f8f8f2;
|
color: #f8f8f2;
|
@ -2,10 +2,6 @@
|
|||||||
* Style: monokailight
|
* Style: monokailight
|
||||||
* https://xyproto.github.io/splash/docs/monokailight.html
|
* https://xyproto.github.io/splash/docs/monokailight.html
|
||||||
*/
|
*/
|
||||||
:root {
|
|
||||||
--pre-text-color: #272822;
|
|
||||||
--pre-background-color: #fafafa;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Background */
|
/* Background */
|
||||||
.chroma {
|
.chroma {
|
@ -129,6 +129,8 @@
|
|||||||
margin-top: var(--sidebar-element-separation);
|
margin-top: var(--sidebar-element-separation);
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
flex-grow: 1;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
|
||||||
@media (min-width: $on-desktop-large) {
|
@media (min-width: $on-desktop-large) {
|
||||||
margin-top: 30px;
|
margin-top: 30px;
|
||||||
@ -192,7 +194,6 @@
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: var(--body-text-color);
|
color: var(--body-text-color);
|
||||||
font-size: 1.5rem;
|
|
||||||
|
|
||||||
@media (max-width: $on-desktop-large) {
|
@media (max-width: $on-desktop-large) {
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
@import "partials/pagination.scss";
|
@import "partials/pagination.scss";
|
||||||
@import "partials/sidebar.scss";
|
@import "partials/sidebar.scss";
|
||||||
@import "partials/base.scss";
|
@import "partials/base.scss";
|
||||||
|
@import "partials/darkmode.scss";
|
||||||
@import "partials/layout/archives.scss";
|
@import "partials/layout/archives.scss";
|
||||||
@import "partials/layout/article.scss";
|
@import "partials/layout/article.scss";
|
||||||
@import "partials/layout/list.scss";
|
@import "partials/layout/list.scss";
|
||||||
|
@ -1,6 +1,18 @@
|
|||||||
$defaultTagBackgrounds: #8ea885, #df7988, #0177b8, #ffb900, #6b69d6;
|
$defaultTagBackgrounds: #8ea885, #df7988, #0177b8, #ffb900, #6b69d6;
|
||||||
$defaultTagColors: #fff, #fff, #fff, #fff, #fff;
|
$defaultTagColors: #fff, #fff, #fff, #fff, #fff;
|
||||||
|
|
||||||
|
[data-scheme="light"] {
|
||||||
|
--pre-text-color: #272822;
|
||||||
|
--pre-background-color: #fafafa;
|
||||||
|
@import "partials/highlight/light.scss";
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-scheme="dark"] {
|
||||||
|
--pre-text-color: #f8f8f2;
|
||||||
|
--pre-background-color: #272822;
|
||||||
|
@import "partials/highlight/dark.scss";
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Global style
|
* Global style
|
||||||
*/
|
*/
|
||||||
@ -13,7 +25,6 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff;
|
|||||||
--main-top-padding: 50px;
|
--main-top-padding: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
--body-background: #f5f5fa;
|
--body-background: #f5f5fa;
|
||||||
|
|
||||||
--accent-color: #34495e;
|
--accent-color: #34495e;
|
||||||
@ -25,7 +36,7 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff;
|
|||||||
|
|
||||||
--section-separation: 40px;
|
--section-separation: 40px;
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
[data-scheme="dark"] {
|
||||||
--body-background: #303030;
|
--body-background: #303030;
|
||||||
--accent-color: #ecf0f1;
|
--accent-color: #ecf0f1;
|
||||||
--accent-color-darker: #bdc3c7;
|
--accent-color-darker: #bdc3c7;
|
||||||
@ -72,7 +83,7 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff;
|
|||||||
--small-card-padding: 25px 20px;
|
--small-card-padding: 25px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
[data-scheme="dark"] {
|
||||||
--card-background: #424242;
|
--card-background: #424242;
|
||||||
--card-background-selected: rgba(255, 255, 255, 0.16);
|
--card-background-selected: rgba(255, 255, 255, 0.16);
|
||||||
--card-text-color-main: rgba(255, 255, 255, 0.9);
|
--card-text-color-main: rgba(255, 255, 255, 0.9);
|
||||||
@ -116,7 +127,7 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff;
|
|||||||
--table-border-color: #dadada;
|
--table-border-color: #dadada;
|
||||||
--tr-even-background-color: #efefee;
|
--tr-even-background-color: #efefee;
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
[data-scheme="dark"] {
|
||||||
--code-background-color: #272822;
|
--code-background-color: #272822;
|
||||||
--code-text-color: rgba(255, 255, 255, 0.9);
|
--code-text-color: rgba(255, 255, 255, 0.9);
|
||||||
|
|
||||||
|
76
assets/ts/darkmode.ts
Normal file
76
assets/ts/darkmode.ts
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
type colorScheme = 'light' | 'dark' | 'auto';
|
||||||
|
|
||||||
|
class StackDarkMode {
|
||||||
|
private localStorageKey = 'StackColorScheme';
|
||||||
|
private currentScheme: colorScheme;
|
||||||
|
private systemPreferScheme: colorScheme;
|
||||||
|
|
||||||
|
constructor(toggleEl: HTMLElement) {
|
||||||
|
this.bindMatchMedia();
|
||||||
|
this.currentScheme = this.getSavedScheme();
|
||||||
|
this.setBodyClass();
|
||||||
|
this.bindClick(toggleEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private saveScheme() {
|
||||||
|
localStorage.setItem(this.localStorageKey, this.currentScheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bindClick(toggleEl) {
|
||||||
|
toggleEl.addEventListener('click', (e) => {
|
||||||
|
if (this.isDark()) {
|
||||||
|
/// Disable dark mode
|
||||||
|
this.currentScheme = 'light';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.currentScheme = 'dark';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setBodyClass();
|
||||||
|
|
||||||
|
if (this.currentScheme == this.systemPreferScheme) {
|
||||||
|
/// Set to auto
|
||||||
|
this.currentScheme = 'auto';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.saveScheme();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private isDark() {
|
||||||
|
return (this.currentScheme == 'dark' || this.currentScheme == 'auto' && this.preferDarkMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private setBodyClass() {
|
||||||
|
if (document.body.style.transition == '')
|
||||||
|
document.body.style.setProperty('transition', 'background-color .3s ease');
|
||||||
|
|
||||||
|
if (this.isDark()) {
|
||||||
|
document.body.dataset.scheme = 'dark';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
document.body.dataset.scheme = 'light';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getSavedScheme(): colorScheme {
|
||||||
|
const savedScheme = localStorage.getItem(this.localStorageKey);
|
||||||
|
|
||||||
|
if (savedScheme == 'light' || savedScheme == 'dark' || savedScheme == 'auto') return savedScheme;
|
||||||
|
else return 'auto';
|
||||||
|
}
|
||||||
|
|
||||||
|
private bindMatchMedia() {
|
||||||
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
|
||||||
|
if (e.matches) {
|
||||||
|
this.systemPreferScheme = 'dark';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.systemPreferScheme = 'light';
|
||||||
|
}
|
||||||
|
this.setBodyClass();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StackDarkMode;
|
@ -9,6 +9,7 @@ import StackGallery from "ts/gallery";
|
|||||||
import { getColor } from 'ts/color';
|
import { getColor } from 'ts/color';
|
||||||
import menu from 'ts/menu';
|
import menu from 'ts/menu';
|
||||||
import createElement from 'ts/createElement';
|
import createElement from 'ts/createElement';
|
||||||
|
import StackDarkMode from 'ts/darkmode';
|
||||||
|
|
||||||
let Stack = {
|
let Stack = {
|
||||||
init: () => {
|
init: () => {
|
||||||
@ -52,6 +53,8 @@ let Stack = {
|
|||||||
|
|
||||||
observer.observe(articleTile)
|
observer.observe(articleTile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
new StackDarkMode(document.getElementById('dark-mode-toggle'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,27 @@
|
|||||||
{{- block "head" . -}}{{ end }}
|
{{- block "head" . -}}{{ end }}
|
||||||
</head>
|
</head>
|
||||||
<body class="{{ block `body-class` . }}{{ end }}">
|
<body class="{{ block `body-class` . }}{{ end }}">
|
||||||
<div class="container flex on-phone--column align-items--flex-start {{ if .Site.Params.widgets.enabled }}extended{{ else }}compact{{ end }} {{ block `container-class` . }}{{end}}">
|
<script>
|
||||||
|
(function() {
|
||||||
|
const colorSchemeKey = 'StackColorScheme';
|
||||||
|
const colorSchemeItem = localStorage.getItem(colorSchemeKey);
|
||||||
|
const supportDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches === true;
|
||||||
|
|
||||||
|
if (colorSchemeItem == 'dark' || !colorSchemeItem && supportDarkMode || colorSchemeItem === 'auto' && supportDarkMode) {
|
||||||
|
/**
|
||||||
|
* Enable dark mode if:
|
||||||
|
* 1. If dark mode is set already (in local storage)
|
||||||
|
* 2. No color scheme preference is set in localstorage, and user's browser indicates preference for dark mode
|
||||||
|
* 3. Auto mode & prefere color scheme is dark
|
||||||
|
*/
|
||||||
|
document.body.dataset.scheme = 'dark';
|
||||||
|
} else {
|
||||||
|
document.body.dataset.scheme = 'light';
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="container flex on-phone--column {{ if .Site.Params.widgets.enabled }}extended{{ else }}compact{{ end }} {{ block `container-class` . }}{{end}}">
|
||||||
{{ partial "sidebar/left.html" . }}
|
{{ partial "sidebar/left.html" . }}
|
||||||
<main class="main full-width">
|
<main class="main full-width">
|
||||||
{{- block "main" . }}{{- end }}
|
{{- block "main" . }}{{- end }}
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
{{- $light := resources.Get "css/highlight/light.css" | minify -}}
|
|
||||||
{{- $dark := resources.Get "css/highlight/dark.css" | minify -}}
|
|
||||||
<link rel="stylesheet" href="{{ $light.RelPermalink }}" media="(prefers-color-scheme: light)">
|
|
||||||
<link rel="stylesheet" href="{{ $dark.RelPermalink }}" media="(prefers-color-scheme: dark)">
|
|
@ -1,4 +1,3 @@
|
|||||||
{{ partialCached "footer/components/script.html" . }}
|
{{ partialCached "footer/components/script.html" . }}
|
||||||
{{ partialCached "footer/components/custom-font.html" . }}
|
{{ partialCached "footer/components/custom-font.html" . }}
|
||||||
{{ partialCached "footer/components/highlight.html" . }}
|
|
||||||
{{ partial "footer/custom.html" . }}
|
{{ partial "footer/custom.html" . }}
|
@ -45,5 +45,11 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
<li id="dark-mode-toggle">
|
||||||
|
{{ partial "helper/icon" "toggle-left" }}
|
||||||
|
{{ partial "helper/icon" "toggle-right" }}
|
||||||
|
<span>Dark Mode</span>
|
||||||
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</aside>
|
</aside>
|
||||||
|
Loading…
Reference in New Issue
Block a user