From efa3eac8fa688d0d1a22c4548d254532dc7cd8ed Mon Sep 17 00:00:00 2001 From: Oli Evans Date: Tue, 21 Jul 2020 09:44:34 +0100 Subject: [PATCH 1/2] feat: light mode with optional dark mode toggle Allow the user to specify their prefered color-scheme. Fixes: #983 License: MIT Signed-off-by: Oli Evans --- assets/_custom.scss | 66 +++++++-- assets/_variables.scss | 2 +- assets/book-dark.scss | 17 +++ assets/dark-mode/dark-mode.js | 52 +++++++ assets/plugins/_dark.scss | 2 +- assets/plugins/_toggles.scss | 208 +++++++++++++++++++++++++++ layouts/_default/baseof.html | 6 + layouts/partials/docs/brand.html | 8 ++ layouts/partials/docs/html-head.html | 8 +- layouts/partials/docs/toc.html | 2 +- 10 files changed, 357 insertions(+), 14 deletions(-) create mode 100644 assets/book-dark.scss create mode 100644 assets/dark-mode/dark-mode.js create mode 100644 assets/plugins/_toggles.scss create mode 100644 layouts/partials/docs/brand.html diff --git a/assets/_custom.scss b/assets/_custom.scss index 0c1e0d953..5db97d419 100644 --- a/assets/_custom.scss +++ b/assets/_custom.scss @@ -4,6 +4,7 @@ // @import "plugins/numbered"; @import "plugins/scrollbars"; +@import "plugins/toggles"; // SVG Diagrams .diagrams-container { @@ -188,28 +189,32 @@ blockquote { } .book-toc { - p { + .book-toc-toggles { + top:0px; + opacity: 0.4; + transition: opacity 0.3s ease-in-out; + } + .book-toc-toggles:hover { + opacity: 1; + } + .book-toc-contents { + top:78px; + } + p, .dark-mode-toggle-label { font-size: 10px; font-weight: 700; + display: block; } i[class^=gg-] { display: inline-block; margin-left: 0; margin-right: 0; - vertical-align: text-top; + vertical-align: middle; font-size: 10px; transform: scale(0.7); } } -h1, -h2, -h3, -h4, -h5 { - color: rgba(255,255,255,0.87) -} - // Colors .color-incorrect { color: #BF616A; @@ -424,4 +429,45 @@ i[class^="gg-"] { box-shadow: 4px -6px 0,8px -12px 0; border-radius: 4px; background: currentColor +} + + .gg-dark-mode { + box-sizing: border-box; + position: relative; + display: block; + transform: scale(var(--ggs,1)); + border:2px solid; + border-radius:100px; + width:20px; + height:20px +} + +.gg-dark-mode::after, +.gg-dark-mode::before { + content: ""; + box-sizing: border-box; + position: absolute; + display: block +} + +.gg-dark-mode::before { + border:5px solid; + border-top-left-radius:100px; + border-bottom-left-radius:100px; + border-right: 0; + width:9px; + height:18px; + top:-1px; + left:-1px +} + +.gg-dark-mode::after { + border:4px solid; + border-top-right-radius:100px; + border-bottom-right-radius:100px; + border-left: 0; + width:4px; + height:8px; + right:4px; + top:4px } \ No newline at end of file diff --git a/assets/_variables.scss b/assets/_variables.scss index ff57f82a7..ee4473aef 100644 --- a/assets/_variables.scss +++ b/assets/_variables.scss @@ -1,4 +1,4 @@ /* You can override SASS variables here. */ -@import "plugins/dark"; +// @import "plugins/dark"; diff --git a/assets/book-dark.scss b/assets/book-dark.scss new file mode 100644 index 000000000..ae14d7eee --- /dev/null +++ b/assets/book-dark.scss @@ -0,0 +1,17 @@ +@import "defaults"; +@import "variables"; + +// Import the dark overrides. This file is otherwise identical to dark.scss +@import "plugins/dark.scss"; + +@import "normalize"; +@import "utils"; +@import "main"; +@import "fonts"; +@import "print"; + +@import "markdown"; +@import "shortcodes"; + +// Custom defined styles +@import "custom"; diff --git a/assets/dark-mode/dark-mode.js b/assets/dark-mode/dark-mode.js new file mode 100644 index 000000000..2bed8d3f8 --- /dev/null +++ b/assets/dark-mode/dark-mode.js @@ -0,0 +1,52 @@ +// iife to avoid polluting the global. +(function () { + // Run me as soon as possible after the the css links are in the dom. + // This assumes this js file is added to the page after the css links. + const lightMode = document.getElementById('light-mode-link') + const darkMode = document.getElementById('dark-mode-link') + const btn = document.querySelector('.dark-mode-toggle') + const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)').matches + let theme = prefersDarkScheme ? 'dark' : 'light' + + function enableLightMode () { + lightMode.disabled = false + darkMode.disabled = true + btn.setAttribute('aria-pressed', theme === 'dark') + } + + function enableDarkMode () { + darkMode.disabled = false + lightMode.disabled = true + btn.setAttribute('aria-pressed', theme === 'dark') + } + + // enable dark theme optimistically on OS with dark theme enabled to reduce flashing of white theme. + if (prefersDarkScheme) { + enableDarkMode() + } + + // wait for localstorage... + const previousChoice = localStorage.getItem('theme') + theme = previousChoice || theme + + // Light is default, so enable dark if user previously chose it but their OS pref is light. + if (theme === 'dark') { + enableDarkMode() + } + + // set up the toggle once the DOM is ready. + document.addEventListener("DOMContentLoaded", function(event) { + + btn.addEventListener('click', function () { + theme = (theme === 'light' ? 'dark' : 'light') + if (theme === 'dark') { + enableDarkMode() + } else { + enableLightMode() + } + localStorage.setItem('theme', theme) + }) + // init the button state to match the currently selected theme. + btn.setAttribute('aria-pressed', theme === 'dark') + }); +})() diff --git a/assets/plugins/_dark.scss b/assets/plugins/_dark.scss index 94e255513..d2c57e39c 100644 --- a/assets/plugins/_dark.scss +++ b/assets/plugins/_dark.scss @@ -3,7 +3,7 @@ $gray-200: #282a36; // https://www.colorhexa.com/090909 $body-background: #090909; -$body-font-color: rgba(255, 255, 255, 0.60); +$body-font-color: rgba(255, 255, 255, 0.80); $color-link: #0090ff; $color-visited-link: #0090ff; diff --git a/assets/plugins/_toggles.scss b/assets/plugins/_toggles.scss new file mode 100644 index 000000000..10c55189d --- /dev/null +++ b/assets/plugins/_toggles.scss @@ -0,0 +1,208 @@ +// edited from https://adrianroselli.com/2019/08/under-engineered-toggles-too.html +.toggle[aria-pressed] { + display: block; + box-sizing: border-box; + border: none; + color: inherit; + background: none; + font: inherit; + line-height: inherit; + text-align: left; + padding: .4em 0 .4em 4em; + position: relative; + +} + +.toggle[aria-pressed][disabled], +.toggle[aria-pressed][disabled]:hover { + color: #999; +} + +.toggle[aria-pressed]:focus, +.toggle[aria-pressed]:hover { + color: #00f; + outline: none; +} + +// .toggle[aria-pressed]:focus::before, +// .toggle[aria-pressed]:hover::before { +// box-shadow: 0 0 0.5em #333; +// } + +.toggle[aria-pressed]:focus::after, +.toggle[aria-pressed]:hover::after { + background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='50' cy='50' r='50' fill='rgba(0,0,0,.25)'/%3E%3C/svg%3E"); + background-size: 30%; + background-repeat: no-repeat; + background-position: center center; +} + +.toggle[aria-pressed]::before, +.toggle[aria-pressed]::after { + content: ""; + position: absolute; + height: 1.5em; + transition: all 0.25s ease; +} + +.toggle[aria-pressed]::before { + left: 0; + top: 0.2em; + width: 3em; + border: 0.2em solid #767676; + background: #767676; + border-radius: 1.1em; +} + +.toggle[aria-pressed]::after { + left: 0; + top: 0.25em; + background-color: #fff; + background-position: center center; + border-radius: 50%; + width: 1.5em; + border: 0.15em solid #767676; +} + +.toggle[aria-pressed=true]::after { + left: 1.6em; + border-color: #0090ff; + color: #0090ff; +} + +.toggle[aria-pressed=true]::before { + background-color: #0090ff; + border-color: #0090ff; +} + +.toggle[aria-pressed][disabled]::before { + background-color: transparent; + border-color: #ddd; +} + +.toggle[aria-pressed][disabled]::after { + border-color: #ddd; +} + +.toggle[aria-pressed][disabled]:hover { + color: #999; /* case for CSS custom property if not supporting IE/Edge */ +} + +.toggle[aria-pressed][disabled]:hover::before { + box-shadow: none; +} + +.toggle[aria-pressed][disabled]:hover::after { + background-image: none; +} + +/* Put toggles on the right like the iOS the kids like */ + +.toggle.flip[aria-pressed]::before, +.toggle.flip[aria-pressed]::after { + left: auto; + right: 0; +} + +.toggle.flip[aria-pressed]::after { + left: auto; + right: 1.6em; +} + +.toggle.flip[aria-pressed=true]::after { + right: 0; +} + +.toggle.flip[aria-pressed] { + padding-left: 0; + padding-right: 4em; +} + +/* Windows High Contrast Mode Support */ +@media screen and (-ms-high-contrast: active) { + .toggle[aria-pressed]:focus::before, + .toggle[aria-pressed]:hover::before { + outline: 1px dotted windowText; + outline-offset: 0.25em; + } + .toggle[aria-pressed]::after { + background-color: windowText; + } + .toggle[aria-pressed][disabled]::after { + background-color: transparent; + } +} + +/* Reduced motion */ +@media screen and (prefers-reduced-motion: reduce) { + .toggle[aria-pressed]::before, + .toggle[aria-pressed]::after { + transition: none; + } +} + +/* Dark mode */ +@media screen and (prefers-color-scheme: dark) { + .toggle[aria-pressed]:focus, + .toggle[aria-pressed]:hover { + color: #99f; + } + .toggle[aria-pressed]::before { + border-color: #808080; + background: #808080; + } + .toggle[aria-pressed]::after { + background-color: #101010; + } + .toggle[aria-pressed]:focus::after, + .toggle[aria-pressed]:hover::after { + background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='50' cy='50' r='50' fill='rgba(255,255,255,.25)'/%3E%3C/svg%3E"); + } + .toggle[aria-pressed][disabled]::before, + .toggle[aria-pressed][disabled]::after { + border-color: #555; + } +} + +/* RTL */ +/* https://twitter.com/dror3go/status/1102946375396982784 */ +*[dir="rtl"] .toggle[aria-pressed] { + padding-left: 0; + padding-right: 4em; +} + +*[dir="rtl"] .toggle[aria-pressed]::before, +*[dir="rtl"] .toggle[aria-pressed]::after { + left: auto; + right: 0; +} + +*[dir="rtl"] .toggle[aria-pressed]::after { + right: 0; +} + +*[dir="rtl"] .toggle[aria-pressed=true]::after { + right: 1.6em; +} + +/* Put toggles on the right like the iOS the kids like */ + +*[dir="rtl"] .toggle.flip[aria-pressed]::before, +*[dir="rtl"] .toggle.flip[aria-pressed]::after { + left: 0; + right: auto; +} + +*[dir="rtl"] .toggle.flip[aria-pressed]::after { + right: auto; + left: 1.6em; +} + +*[dir="rtl"] .toggle.flip[aria-pressed=true]::after { + left: 0; +} + +*[dir="rtl"] .toggle.flip[aria-pressed] { + padding-right: 0; + padding-left: 4em; +} \ No newline at end of file diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html index c737c522a..7785308b1 100644 --- a/layouts/_default/baseof.html +++ b/layouts/_default/baseof.html @@ -34,6 +34,12 @@ {{ if default true (default .Site.Params.BookToC .Params.BookToC) }} {{ end }} diff --git a/layouts/partials/docs/brand.html b/layouts/partials/docs/brand.html new file mode 100644 index 000000000..f08b4adfc --- /dev/null +++ b/layouts/partials/docs/brand.html @@ -0,0 +1,8 @@ +

+ + {{- with .Site.Params.BookLogo -}} + Logo + {{- end -}} + {{ .Site.Title }} + +

diff --git a/layouts/partials/docs/html-head.html b/layouts/partials/docs/html-head.html index 0ef3ba52a..adc01be45 100644 --- a/layouts/partials/docs/html-head.html +++ b/layouts/partials/docs/html-head.html @@ -20,7 +20,13 @@ {{- $styles := resources.Get "book.scss" | resources.ExecuteAsTemplate "book.scss" . | resources.ToCSS | resources.Minify | resources.Fingerprint }} - + + +{{- $darkStyles := resources.Get "book-dark.scss" | resources.ExecuteAsTemplate "book-dark.scss" . | resources.ToCSS | resources.Minify | resources.Fingerprint }} + + +{{- $darkModeJS := resources.Get "dark-mode/dark-mode.js" | resources.Minify | resources.Fingerprint }} + {{- if default false ($.Param "math-mode") -}} diff --git a/layouts/partials/docs/toc.html b/layouts/partials/docs/toc.html index 7810ecb4a..689337fab 100644 --- a/layouts/partials/docs/toc.html +++ b/layouts/partials/docs/toc.html @@ -1,4 +1,4 @@ -
+

CONTENTS

{{ .TableOfContents }}
From a5cb936dac9b9da27a54dfd63aeb28c07378c10d Mon Sep 17 00:00:00 2001 From: Oli Evans Date: Tue, 21 Jul 2020 09:58:57 +0100 Subject: [PATCH 2/2] chore: tidy up formatting License: MIT Signed-off-by: Oli Evans --- assets/_custom.scss | 60 +++++++++++++++++++++--------------------- assets/_variables.scss | 3 --- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/assets/_custom.scss b/assets/_custom.scss index 5db97d419..aae910ac7 100644 --- a/assets/_custom.scss +++ b/assets/_custom.scss @@ -431,43 +431,43 @@ i[class^="gg-"] { background: currentColor } - .gg-dark-mode { - box-sizing: border-box; - position: relative; - display: block; - transform: scale(var(--ggs,1)); - border:2px solid; - border-radius:100px; - width:20px; - height:20px +.gg-dark-mode { + box-sizing: border-box; + position: relative; + display: block; + transform: scale(var(--ggs,1)); + border:2px solid; + border-radius:100px; + width:20px; + height:20px } .gg-dark-mode::after, .gg-dark-mode::before { - content: ""; - box-sizing: border-box; - position: absolute; - display: block + content: ""; + box-sizing: border-box; + position: absolute; + display: block } .gg-dark-mode::before { - border:5px solid; - border-top-left-radius:100px; - border-bottom-left-radius:100px; - border-right: 0; - width:9px; - height:18px; - top:-1px; - left:-1px + border:5px solid; + border-top-left-radius:100px; + border-bottom-left-radius:100px; + border-right: 0; + width:9px; + height:18px; + top:-1px; + left:-1px } .gg-dark-mode::after { - border:4px solid; - border-top-right-radius:100px; - border-bottom-right-radius:100px; - border-left: 0; - width:4px; - height:8px; - right:4px; - top:4px -} \ No newline at end of file + border:4px solid; + border-top-right-radius:100px; + border-bottom-right-radius:100px; + border-left: 0; + width:4px; + height:8px; + right:4px; + top:4px +} diff --git a/assets/_variables.scss b/assets/_variables.scss index ee4473aef..98b4d4ef2 100644 --- a/assets/_variables.scss +++ b/assets/_variables.scss @@ -1,4 +1 @@ /* You can override SASS variables here. */ - -// @import "plugins/dark"; -