diff --git a/assets/icons/toggle-left.svg b/assets/icons/toggle-left.svg new file mode 100644 index 0000000..c3048d4 --- /dev/null +++ b/assets/icons/toggle-left.svg @@ -0,0 +1,7 @@ + + + diff --git a/assets/icons/toggle-right.svg b/assets/icons/toggle-right.svg new file mode 100644 index 0000000..d3edd63 --- /dev/null +++ b/assets/icons/toggle-right.svg @@ -0,0 +1,7 @@ + + + diff --git a/assets/css/highlight/dark.css b/assets/scss/partials/highlight/dark.scss similarity index 98% rename from assets/css/highlight/dark.css rename to assets/scss/partials/highlight/dark.scss index 7f5b872..00e8942 100644 --- a/assets/css/highlight/dark.css +++ b/assets/scss/partials/highlight/dark.scss @@ -3,11 +3,6 @@ * https://xyproto.github.io/splash/docs/monokai.html */ -:root { - --pre-text-color: #f8f8f2; - --pre-background-color: #272822; -} - /* Background */ .chroma { color: #f8f8f2; diff --git a/assets/css/highlight/light.css b/assets/scss/partials/highlight/light.scss similarity index 98% rename from assets/css/highlight/light.css rename to assets/scss/partials/highlight/light.scss index 9c4e0da..7ac7d71 100644 --- a/assets/css/highlight/light.css +++ b/assets/scss/partials/highlight/light.scss @@ -2,10 +2,6 @@ * Style: monokailight * https://xyproto.github.io/splash/docs/monokailight.html */ -:root { - --pre-text-color: #272822; - --pre-background-color: #fafafa; -} /* Background */ .chroma { diff --git a/assets/scss/partials/menu.scss b/assets/scss/partials/menu.scss index 5cefeb3..024b3a6 100644 --- a/assets/scss/partials/menu.scss +++ b/assets/scss/partials/menu.scss @@ -129,6 +129,8 @@ margin-top: var(--sidebar-element-separation); margin-bottom: 0; overflow-y: auto; + flex-grow: 1; + font-size: 1.5rem; @media (min-width: $on-desktop-large) { margin-top: 30px; @@ -192,7 +194,6 @@ display: inline-flex; align-items: center; color: var(--body-text-color); - font-size: 1.5rem; @media (max-width: $on-desktop-large) { font-size: 1.4rem; diff --git a/assets/scss/partials/sidebar.scss b/assets/scss/partials/sidebar.scss index 8eddafc..b6722a9 100644 --- a/assets/scss/partials/sidebar.scss +++ b/assets/scss/partials/sidebar.scss @@ -134,3 +134,30 @@ } } } + +[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; + } +} diff --git a/assets/scss/variables.scss b/assets/scss/variables.scss index 14d9e07..3a8a1a5 100644 --- a/assets/scss/variables.scss +++ b/assets/scss/variables.scss @@ -1,6 +1,18 @@ $defaultTagBackgrounds: #8ea885, #df7988, #0177b8, #ffb900, #6b69d6; $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 */ @@ -13,7 +25,6 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff; --main-top-padding: 50px; } - --body-background: #f5f5fa; --accent-color: #34495e; @@ -25,7 +36,7 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff; --section-separation: 40px; - @media (prefers-color-scheme: dark) { + [data-scheme="dark"] { --body-background: #303030; --accent-color: #ecf0f1; --accent-color-darker: #bdc3c7; @@ -72,7 +83,7 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff; --small-card-padding: 25px 20px; } - @media (prefers-color-scheme: dark) { + [data-scheme="dark"] { --card-background: #424242; --card-background-selected: rgba(255, 255, 255, 0.16); --card-text-color-main: rgba(255, 255, 255, 0.9); @@ -116,7 +127,7 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff; --table-border-color: #dadada; --tr-even-background-color: #efefee; - @media (prefers-color-scheme: dark) { + [data-scheme="dark"] { --code-background-color: #272822; --code-text-color: rgba(255, 255, 255, 0.9); diff --git a/assets/ts/colorScheme.ts b/assets/ts/colorScheme.ts new file mode 100644 index 0000000..e17ff07 --- /dev/null +++ b/assets/ts/colorScheme.ts @@ -0,0 +1,86 @@ +type colorScheme = 'light' | 'dark' | 'auto'; + +class StackColorScheme { + private localStorageKey = 'StackColorScheme'; + private currentScheme: colorScheme; + private systemPreferScheme: colorScheme; + + constructor(toggleEl: HTMLElement) { + this.bindMatchMedia(); + this.currentScheme = this.getSavedScheme(); + + if (toggleEl) + this.bindClick(toggleEl); + + if (document.body.style.transition == '') + document.body.style.setProperty('transition', 'background-color .3s ease'); + } + + 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.systemPreferScheme == 'dark'); + } + + private dispatchEvent(colorScheme: colorScheme) { + const event = new CustomEvent('onColorSchemeChange', { + detail: colorScheme + }); + window.dispatchEvent(event); + } + + private setBodyClass() { + if (this.isDark()) { + document.body.dataset.scheme = 'dark'; + } + else { + document.body.dataset.scheme = 'light'; + } + + this.dispatchEvent(document.body.dataset.scheme as colorScheme); + } + + 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 StackColorScheme; \ No newline at end of file diff --git a/assets/ts/main.ts b/assets/ts/main.ts index 4d07631..2fcc152 100644 --- a/assets/ts/main.ts +++ b/assets/ts/main.ts @@ -9,6 +9,7 @@ import StackGallery from "ts/gallery"; import { getColor } from 'ts/color'; import menu from 'ts/menu'; import createElement from 'ts/createElement'; +import StackColorScheme from 'ts/colorScheme'; let Stack = { init: () => { @@ -52,6 +53,8 @@ let Stack = { observer.observe(articleTile) } + + new StackColorScheme(document.getElementById('dark-mode-toggle')); } } diff --git a/exampleSite/config.yaml b/exampleSite/config.yaml index 20dcda6..f06128d 100644 --- a/exampleSite/config.yaml +++ b/exampleSite/config.yaml @@ -79,6 +79,13 @@ params: local: false src: + colorScheme: + # Display toggle + toggle: true + + # Available values: auto, light, dark + default: auto + menu: main: - identifier: home diff --git a/i18n/en.yaml b/i18n/en.yaml index 7f3b4fa..e131b61 100644 --- a/i18n/en.yaml +++ b/i18n/en.yaml @@ -1,6 +1,9 @@ toggleMenu: other: Toggle Menu +darkMode: + other: Dark Mode + list: page: one: "{{ .Count }} page" diff --git a/i18n/zh-CN.yaml b/i18n/zh-CN.yaml index 78615bf..0583bf7 100644 --- a/i18n/zh-CN.yaml +++ b/i18n/zh-CN.yaml @@ -1,6 +1,9 @@ toggleMenu: other: 切换菜单 +darkMode: + other: 暗色模式 + archives: categories: other: 分类 diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html index 06a31e5..ad5f295 100644 --- a/layouts/_default/baseof.html +++ b/layouts/_default/baseof.html @@ -5,7 +5,8 @@ {{- block "head" . -}}{{ end }}
-