diff --git a/README.md b/README.md index 266aba6..39ad58d 100644 --- a/README.md +++ b/README.md @@ -62,3 +62,6 @@ The file is in this format: Publishing the site content requires AWS credentials which are not stored in this repository. Attempts to use the `gulp publish` command will fail. +## Attributions + +Sun/moon svg icons forked from [glow-ui/glow-icons](https://github.com/glow-ui/glow-icons). Licensed under the [MIT license](https://github.com/glow-ui/glow-icons/blob/main/LICENSE). diff --git a/js/_dark.js b/js/_dark.js new file mode 100644 index 0000000..eefb467 --- /dev/null +++ b/js/_dark.js @@ -0,0 +1,25 @@ +(function () { + // Check if local storage is available and if dark mode is set; overrides everything else + if (window.localStorage && window.localStorage.getItem('dark-mode') !== null) { + if (window.localStorage.getItem('dark-mode') === 'true') { + document.documentElement.classList.add('dark-mode'); + } + // Evaluate device preferences + } else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { + document.documentElement.classList.add('dark-mode'); + } +}()); + +$(function () { + $('.dark-toggle').on('click', function toggleDarkMode (e) { + e.preventDefault(); + + if (document.documentElement.classList.contains('dark-mode')) { + document.documentElement.classList.remove('dark-mode'); + window.localStorage.setItem('dark-mode', 'false'); + } else { + document.documentElement.classList.add('dark-mode'); + window.localStorage.setItem('dark-mode', 'true'); + } + }); +}); diff --git a/scss/_card.scss b/scss/_card.scss index e220200..fc0394f 100644 --- a/scss/_card.scss +++ b/scss/_card.scss @@ -3,12 +3,30 @@ text-decoration: none; background: white; + @include dark-mode { + background: $content-bg-dark; + border: 1px solid $primary; + box-shadow: 0 0 5px 2px rgba($primary, 0.5); + + .card-header { + background: lighten($content-bg-dark, 5); + } + } + &.borderless { border: none; + + @include dark-mode { + box-shadow: none; + } } .card-img, .card-img-top { // width: unset; + @include dark-mode { + // Some card images don't work on dark backgrounds + background-color: white; + } } .card-img-top + .card-img-top { @@ -23,6 +41,10 @@ font-size: 12px; font-family: $font-secondary; + @include dark-mode { + color: $body-color-dark; + } + ul { padding-left: 1.2em; } } @@ -70,6 +92,10 @@ font-family: Roboto, Helvetica, Arial,sans-serif; text-decoration:none; + @include dark-mode { + background: $content-bg-dark; + color: $body-color-dark; + } .ig-header { display: flex; diff --git a/scss/_dark.scss b/scss/_dark.scss new file mode 100644 index 0000000..565f849 --- /dev/null +++ b/scss/_dark.scss @@ -0,0 +1,17 @@ +$disable-dark-mode: false !default; + +@mixin dark-mode { + @if not $disable-dark-mode { + .dark-mode & { + @content + } + } +} + +@mixin light-mode { + @if not $disable-dark-mode { + :root:not(.dark-mode) & { + @content + } + } +} diff --git a/scss/_dropdown.scss b/scss/_dropdown.scss new file mode 100644 index 0000000..495de2a --- /dev/null +++ b/scss/_dropdown.scss @@ -0,0 +1,13 @@ +.dropdown-menu { + @include dark-mode { + background-color: #2d2d2d; + + .dropdown-item, .dropdown-item:hover:active:not(.active) { + color: white; + } + + .dropdown-item:hover:not(:active) { + color: black; + } + } +} diff --git a/scss/_global.scss b/scss/_global.scss index f37f3fd..f57760b 100644 --- a/scss/_global.scss +++ b/scss/_global.scss @@ -1,6 +1,19 @@ +a { + @include dark-mode { + color: lighten($primary, 10%); + + &:hover { + color: lighten($primary, 20%); + } + } +} body { font-family: $font-primary; + + @include dark-mode { + color: $body-color-dark; + } } // Prevents the page header from blocking view of @@ -16,6 +29,10 @@ body { } h1:target, h2:target, h3:target, h4:target, h5:target, h6:target { background-color: yellow !important; + + @include dark-mode { + color: black; + } } span.svg-icon { diff --git a/scss/_header.scss b/scss/_header.scss index ef64f92..a11553a 100644 --- a/scss/_header.scss +++ b/scss/_header.scss @@ -10,6 +10,10 @@ text-decoration: none; } } + + @include dark-mode { + color: $body-color-dark; + } } body.inner-page header { @@ -63,6 +67,12 @@ header { @media (max-width: 500px) { font-size: 1.8em; } + + @include dark-mode { + abbr { + color: $primary; + } + } } &.active { @@ -72,13 +82,16 @@ header { .top-brand { max-height: 0; } + + .top-nav.aux-nav { + display: none; + } } } nav { flex: 1; - max-width: 1200px; display: flex; justify-content: space-between; @@ -92,6 +105,36 @@ header { @media (max-width: 500px) { font-size: 0.8rem; } + + @include dark-mode { + a { + color: inherit; + + &:hover { + color: inherit; + text-decoration: none; + } + } + } + } + + .top-nav { + &.main-nav { + margin-left: auto; + order: 2; + + @media (max-width: 800px) { + margin-left: 0; + } + } + + &.aux-nav { + order: 3; + + @media (max-width: 800px) { + order: 1; + } + } } .top-nav-inner { @@ -99,13 +142,9 @@ header { justify-content: space-between; align-items: center; list-style: none; - margin: 0 0 0 1em; + margin: 0; padding: 0; - @media (max-width: 800px) { - margin: 0; - } - .top-nav-item { transition-property: color, padding, margin; @@ -144,38 +183,49 @@ header { } } - @media (max-width: 400px) { - .disposable { - display: none; - } - } - .dropdown-menu { max-height: 90vh; overflow: auto; } - } - @media (max-width: 500px) { + .dark-toggle { + display: grid; - .top-nav-inner .disposable { - transition: max-width 0.5s ease-out; - max-width: 50px; - } + > span { + grid-column: 1; + grid-row: 1; + transition: all 0.5s; + transition-behavior: allow-discrete; + } - &.active .top-nav-inner .disposable { - max-width: 0; - overflow: hidden; + .dark-toggle-sun { + opacity: 1; + } + + .dark-toggle-moon { + opacity: 0; + } + + @include dark-mode { + .dark-toggle-sun { + opacity: 0; + } + + .dark-toggle-moon { + opacity: 1; + } + } } } - } -// @media (min-width: 500px) { - body:not(.front-page) header + * { - margin-top: $header-full-height; +body:not(.front-page) header + * { + margin-top: $header-mobile-height; + + @media (min-width: 800px) { + margin-top: $header-desktop-height; } -// } +} .disclaimer { $cwColor: darken($gray-100, 2%); @@ -193,6 +243,12 @@ header { background-color: $cwColor; + @include dark-mode { + background-color: $gray-800; + color: white; + border-bottom: 1px solid $primary; + } + font-size: 0.8rem; font-weight: 300; diff --git a/scss/_index.scss b/scss/_index.scss index f65293a..9e36f00 100644 --- a/scss/_index.scss +++ b/scss/_index.scss @@ -1,11 +1,25 @@ .front-page { + @include dark-mode { + background-color: #000; + } + + header.active { + @include dark-mode { + background-color: $gray-900; + } + } .top-fold { min-height: 100vh; background-color: $header-bg-mid; background-image: linear-gradient(135deg, $header-bg-1, $header-bg-2); - padding-top: $header-full-height; + padding-top: 150px; + + @include dark-mode { + background-color: inherit; + background-image: none; + } @media (max-width: 400px) { padding-top: 100px; @@ -33,6 +47,13 @@ text-shadow: 0px 1px 3px rgba(black, 0.9); color: $gray-100; + @include dark-mode { + background: darken($trans-blue-darkest, 10%); + border: 1px solid #0087bb; + box-shadow: 0 0 5px 2px rgba($trans-blue, .5); + color: $body-color-dark; + } + max-width: 500px; width: 50vw; font-family: $font-secondary; @@ -60,6 +81,17 @@ box-shadow: inset 0px 1px 5px rgba(#000, 0.2), 0px 1px 5px rgba(#000, 0.9); text-shadow: 0px 1px 3px rgba(#fff, 0.6); + @include dark-mode { + background-color: $content-bg-dark; + box-shadow: 0 0 1pc 6px $primary; + color: $body-color-dark; + text-shadow: none; + + @media (max-width: 800px) { + box-shadow: none; + } + } + max-width: 850px; margin-left: 0.5em; } @@ -83,6 +115,13 @@ } } + @media (max-width: 500px) { + .copy { + margin-left: 0; + margin-right: 0; + } + } + } .pager { diff --git a/scss/_markup.scss b/scss/_markup.scss index aafc0ae..777c3ae 100644 --- a/scss/_markup.scss +++ b/scss/_markup.scss @@ -1,16 +1,19 @@ .markup { -webkit-font-smoothing: antialiased; + transition: background-color 0.5s; p a { transition-property: color, padding, margin; transition-duration: 0.3s; transition-timing-function: ease; - color: $primary; text-decoration: underline; - &:hover { - color: #e84992; + @include light-mode { + color: $primary; + &:hover { + color: #e84992; + } } } @@ -34,6 +37,13 @@ padding: 15px; color: black; + @include dark-mode { + background: darken($trans-blue-darkest, 10%); + border: 1px solid #0087bb; + color: $body-color-dark; + box-shadow: 0 0 5px 2px rgba($trans-blue,0.5); + } + color-adjust: exact; -webkit-print-color-adjust: exact; print-color-adjust: exact; @@ -87,6 +97,12 @@ img { height: 1em; } + + @include dark-mode { + // Convert the black svg icon to the $primary color at runtime using CSS filters + // https://stackoverflow.com/a/43960991 + filter: invert(34%) sepia(87%) saturate(2698%) hue-rotate(313deg) brightness(108%) contrast(98%); + } } + { @@ -152,6 +168,11 @@ -webkit-print-color-adjust: exact; print-color-adjust: exact; + @include dark-mode { + background-color: lighten($content-bg-dark, 5%); + color: #fff; + } + font-size: 0.8rem; font-weight: 300; diff --git a/scss/_page.scss b/scss/_page.scss index b8731af..a03564d 100644 --- a/scss/_page.scss +++ b/scss/_page.scss @@ -7,6 +7,10 @@ body.page, body.post { header { background: $gray-800; &.active { background: white; } + + @include dark-mode { + background: $gray-900; + } } } @@ -27,15 +31,32 @@ body.post { } body.gdb { + transition: background-color 0.5s; + + @include dark-mode { + background-color: $body-bg-dark; + } #body { background-color: $gutter-bg; + border-bottom: 1px solid $gutter-border; + + @include dark-mode { + background-color: $gutter-bg-dark; + border-bottom: 1px solid $gutter-border-dark; + color: $body-color-dark; + box-shadow: 0px 6px 11px -6px $gutter-border-dark; + } @media (max-width: 500px) { border-left: 1px solid $gutter-border; border-right: 1px solid $gutter-border; + + @include dark-mode { + border-left: 0; + border-right: 0; + } } - border-bottom: 1px solid $gutter-border; max-width: 1200px; margin-left: auto; @@ -54,6 +75,11 @@ body.gdb { background: $content-bg; border-right: 1px solid $gutter-border; + @include dark-mode { + background: $content-bg-dark; + border-right: none; + } + padding: 1em 1em; > h1:first-child { diff --git a/scss/_pager.scss b/scss/_pager.scss index 78e0260..35f4a6c 100644 --- a/scss/_pager.scss +++ b/scss/_pager.scss @@ -33,6 +33,12 @@ overflow: hidden; text-overflow: ellipsis; } + + @include dark-mode { + &:hover { + color: white; + } + } } .btn.left { diff --git a/scss/_tweet.scss b/scss/_tweet.scss index 9a40b64..7f3fa62 100644 --- a/scss/_tweet.scss +++ b/scss/_tweet.scss @@ -8,6 +8,10 @@ $borderRadius: .35em; $avatarSize: 36px; + $borderColorDark: $trans-blue-dark; + $textLightDark: #697882; + $textDarkDark: #b4d0dd; + font-size: 12px; body.post & { font-size: 15px; @@ -22,6 +26,15 @@ overflow-x: hidden; overflow-y: auto; + @include dark-mode { + background-color: $content-bg-dark; + border-color: $borderColorDark; + + &:not(.grid-row) { + box-shadow: 0 0 5px 2px rgba($trans-blue, 0.5); + } + } + page-break-inside: avoid; @include media-breakpoint-up(md) { @@ -47,6 +60,10 @@ border-top: 1px solid $borderColor; border-top-left-radius: 0; border-top-right-radius: 0; + + @include dark-mode { + border-top: 1px solid $borderColorDark; + } } .tweet-item:first-child .tweet-logo { display: block; } @@ -69,6 +86,10 @@ margin-bottom: 1em; line-height: 1; + @include dark-mode { + color: $textDarkDark; + } + &:hover { color: $textHover; } @@ -348,6 +369,10 @@ .tweet-item { border-top: none; padding: 0; + + @include dark-mode { + border-top: none; + } } .tweet-item + .tweet-item { @@ -383,6 +408,13 @@ border: none; max-height: unset; + @include dark-mode { + // Box shadow hackery + margin-left: -1em; + margin-right: -1em; + padding: 1em; + } + .tweet-item { background-color: white; border: 1px solid $borderColor; @@ -391,6 +423,12 @@ border-top-left-radius: $borderRadius; border-top-right-radius: $borderRadius; + @include dark-mode { + background-color: $content-bg-dark; + border: 1px solid $borderColorDark; + box-shadow: 0 0 5px 2px rgba($trans-blue, 0.5); + } + .tweet-logo { display: block; } .tweet-text { @@ -416,6 +454,11 @@ flex-direction: column; margin: -1px; + @include dark-mode { + background-color: $content-bg-dark; + + } + .tweet-logo { display: block; } .tweet-text { diff --git a/scss/print.scss b/scss/print.scss index c571df1..e0b52ea 100644 --- a/scss/print.scss +++ b/scss/print.scss @@ -11,6 +11,8 @@ $font-brand: 'Sriracha', Geneva, monospace; $font-family-sans-serif: $font-primary; $font-family-base: $font-primary; +$disable-dark-mode: true; + $grid-breakpoints: ( xs: 0, sm: 500px, @@ -94,6 +96,7 @@ $header-full-height: 100px; @import "bootstrap/scss/utilities"; // @import "bootstrap/scss/print"; +@import "./dark"; // Need to disable for print styles @import "./global"; // @import "./header"; @import "./footer"; diff --git a/scss/style.scss b/scss/style.scss index 6ba4d2f..491f81c 100644 --- a/scss/style.scss +++ b/scss/style.scss @@ -52,7 +52,18 @@ $primary: #fc0a7e; $header-bg-1: #E81179; $header-bg-2: #281362; $header-bg-mid: mix($header-bg-1, $header-bg-2); -$header-full-height: 100px; +$header-desktop-height: 100px; +$header-mobile-height: 142px; + +/* -- Dark Mode -- */ +$body-bg-dark: black; +$body-color-dark: #e2e2e2; +$content-bg-dark: #222; +$gutter-bg-dark: #171717; +$gutter-border-dark: #fc0a7e; + +$primary-light-dark: lighten($primary, 10%); +$primary-lighter-dark: lighten($primary, 10%); @import "bootstrap/scss/functions"; @import "bootstrap/scss/variables"; @@ -94,8 +105,10 @@ $header-full-height: 100px; @import "bootstrap/scss/utilities"; // @import "bootstrap/scss/print"; +@import "./dark"; @import "./global"; @import "./header"; +@import "./dropdown"; @import "./footer"; @import "./tweet"; @import "./index"; @@ -108,4 +121,4 @@ $header-full-height: 100px; @import "./longform"; @import "./font-compatibility"; -|node_modules/magnific-popup/dist/magnific-popup.css| +//|node_modules/magnific-popup/dist/magnific-popup.css| diff --git a/svg/moon.svg b/svg/moon.svg new file mode 100644 index 0000000..4862d8a --- /dev/null +++ b/svg/moon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/svg/sun.svg b/svg/sun.svg new file mode 100644 index 0000000..aeca207 --- /dev/null +++ b/svg/sun.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/templates/layout.hbs b/templates/layout.hbs index 47fd391..3faec1f 100644 --- a/templates/layout.hbs +++ b/templates/layout.hbs @@ -61,7 +61,7 @@