diff --git a/README.md b/README.md index faabe7a..2d08619 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ The minimalist [Pelican](http://blog.getpelican.com/) theme. - Minute read (via [plugin](https://github.com/getpelican/pelican-plugins/tree/master/post_stats)) (new in 2.0) - [Multiple Code Highlight Styles](https://github.com/alexandrevicenzi/Flex/wiki/Code-Highlight) - [Translation Support](https://github.com/alexandrevicenzi/Flex/wiki/Translations) (new in 2.0) +- [Dark Mode](https://github.com/alexandrevicenzi/Flex/wiki/Dark-Mode) ## Integrations diff --git a/gulpfile.js b/gulpfile.js index b7e671c..e56ce77 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,18 +1,31 @@ var gulp = require('gulp'), less = require('gulp-less'), rename = require('gulp-rename'), - minify = require('gulp-cssnano'); + cssnano = require('gulp-cssnano'), + uglify = require('gulp-uglify'); gulp.task('less', function () { - return gulp.src('./static/stylesheet/style.less') + return gulp.src([ + './static/stylesheet/dark-theme.less', + './static/stylesheet/style.less', + ]) .pipe(less()) - .pipe(minify()) + .pipe(cssnano()) .pipe(rename({ extname: '.min.css' })) .pipe(gulp.dest('./static/stylesheet')); }); +gulp.task('uglify', function () { + return gulp.src('./static/dark_theme/dark_theme.js') + .pipe(uglify()) + .pipe(rename({ + extname: '.min.js' + })) + .pipe(gulp.dest('./static/dark_theme')); +}); + gulp.task('cp', function () { return gulp.src('./node_modules/font-awesome/**/*.{min.css,otf,eot,svg,ttf,woff,woff2}') .pipe(gulp.dest('./static/font-awesome')); @@ -20,7 +33,7 @@ gulp.task('cp', function () { gulp.task('pygments', function () { return gulp.src(['./static/pygments/*.css', '!./static/pygments/*min.css']) - .pipe(minify()) + .pipe(cssnano()) .pipe(rename({ extname: '.min.css' })) @@ -28,4 +41,4 @@ gulp.task('pygments', function () { }); -gulp.task('default', gulp.series(['less', 'cp', 'pygments'])); +gulp.task('default', gulp.series(['less', 'uglify', 'cp', 'pygments'])); diff --git a/package.json b/package.json index 68afb63..4d3eea5 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "gulp": "^4.0.1", "gulp-cssnano": "^2.1.3", "gulp-less": "^3.5.0", - "gulp-rename": "^1.3.0" + "gulp-rename": "^1.3.0", + "gulp-uglify": "^3.0.2" } } diff --git a/static/dark_theme/dark_theme.js b/static/dark_theme/dark_theme.js new file mode 100644 index 0000000..0b0162e --- /dev/null +++ b/static/dark_theme/dark_theme.js @@ -0,0 +1,58 @@ +function ThemeManager(options) { + var defaultTheme = options.defaultTheme; + var enableAutoDetectTheme = options.enableAutoDetectTheme.toLowerCase() === 'true'; + + var darkThemeMatch = window.matchMedia( + defaultTheme === 'light' ? + '(prefers-color-scheme: dark)' : + '(prefers-color-scheme: dark), (prefers-color-scheme: no-preference)' + ); + + function setEnabledAndDisableMediaQuery(elementId, enabled) { + var element = document.getElementById(elementId); + element.disabled = !enabled; + element.media = ''; + } + + function detectThemeAndSwitchStyle() { + var theme = localStorage.getItem('themeOverride'); + if (theme !== 'light' && theme !== 'dark') { + if (theme === 'browser' || enableAutoDetectTheme) { + theme = darkThemeMatch.matches ? 'dark' : 'light'; + } else { + theme = defaultTheme; + } + } + + // (Dis|En)able the styles according to the user's desired theme. Get rid + // of the media queries, since we are handling it in JS. + setEnabledAndDisableMediaQuery('dark-theme-style', theme === 'dark'); + setEnabledAndDisableMediaQuery('pygments-dark-theme', theme === 'dark'); + setEnabledAndDisableMediaQuery('pygments-light-theme', theme === 'light'); + + if (theme === 'dark') { + document.body.classList.add('dark-theme'); + document.body.classList.remove('light-theme'); + } else { + document.body.classList.add('light-theme'); + document.body.classList.remove('dark-theme'); + } + } + + this.switch = function(themeOverride) { + localStorage.setItem('themeOverride', themeOverride); + detectThemeAndSwitchStyle(); + }; + + // If there's an override, then apply it, otherwise, don't incur the + // overhead of determining whether or not to switch themes. + var themeOverride = localStorage.getItem('themeOverride'); + if (themeOverride === 'light' || themeOverride === 'dark') { + detectThemeAndSwitchStyle(); + } + + // If theme auto-detection is enabled, then add a listenr on the matchMedia. + darkThemeMatch.addListener(detectThemeAndSwitchStyle); +} + +window.theme = new ThemeManager(document.getElementById('dark-theme-script').dataset); diff --git a/static/dark_theme/dark_theme.min.js b/static/dark_theme/dark_theme.min.js new file mode 100644 index 0000000..7433dd5 --- /dev/null +++ b/static/dark_theme/dark_theme.min.js @@ -0,0 +1 @@ +function ThemeManager(e){var t=e.defaultTheme,r="true"===e.enableAutoDetectTheme.toLowerCase(),a=window.matchMedia("light"===t?"(prefers-color-scheme: dark)":"(prefers-color-scheme: dark), (prefers-color-scheme: no-preference)");function d(e,t){var r=document.getElementById(e);r.disabled=!t,r.media=""}function m(){var e=localStorage.getItem("themeOverride");"light"!==e&&"dark"!==e&&(e="browser"===e||r?a.matches?"dark":"light":t),d("dark-theme-style","dark"===e),d("pygments-dark-theme","dark"===e),d("pygments-light-theme","light"===e),"dark"===e?(document.body.classList.add("dark-theme"),document.body.classList.remove("light-theme")):(document.body.classList.add("light-theme"),document.body.classList.remove("dark-theme"))}this.switch=function(e){localStorage.setItem("themeOverride",e),m()};var o=localStorage.getItem("themeOverride");"light"!==o&&"dark"!==o||m(),a.addListener(m)}window.theme=new ThemeManager(document.getElementById("dark-theme-script").dataset); \ No newline at end of file diff --git a/static/stylesheet/dark-theme.less b/static/stylesheet/dark-theme.less new file mode 100644 index 0000000..49a4d59 --- /dev/null +++ b/static/stylesheet/dark-theme.less @@ -0,0 +1,156 @@ +// +// Dark Theme CSS styles. +// + +@import "variables.less"; + +body { + background-color: @body-bg-dark-theme; + color: @text-color-dark-theme; +} + +hr { + background-color: @solid-dark-grey; + color: @solid-dark-grey; +} + +aside { + background-color: @sidebar-bg-dark-theme; + color: @sidebar-text-color; + + form.navbar-search input#tipue_search_input { + background-color: @very-dark-grey; + color: @light-grey; + } +} + +main { + nav { + border-bottom-color: @nav-border-color-dark-theme; + } + + nav, .translations { + a { + border-color: @nav-border-color-dark-theme; + } + } + + article { + kbd { + background-color: #080808; + color: @light-grey; + } + + blockquote, pre { + border-left: 8px solid #ffffff33; + } + + *:not(pre) > code { + background-color: #080808; + border-color: #000; + } + + div#tipue_search_content .tipue_search_result { + span.tipue_search_content_bold { + color: white; + } + } + + section#isso-thread { + div.textarea, + .auth-section p.input-wrapper input, + .notification-section input { + background: @very-dark-grey; + color: @light-grey; + } + + >h4 { + color: @light-grey; + } + + .isso-postbox > .form-wrapper .preview { + background: repeating-linear-gradient( + -45deg, + @solid-dark-grey, @solid-dark-grey 10px, + @very-dark-grey 10px, @very-dark-grey 20px + ); + } + + .isso-comment > div.text-wrapper { + > .isso-comment-header { + .author { + color: @light-grey; + } + + .spacer, a.permalink, .note, a.parent { + color: @med-grey; + + &:hover { + color: @light-grey; + } + } + } + + > .isso-comment-footer a:hover { + // Unfortunately, Isso uses a lot of !important in its styles, so I + // have to override that. + text-shadow: @dark-grey 0 0 1px !important; + } + } + } + } + + footer { + border-top-color: @footer-border-color-dark-theme; + } +} + +div.related-posts { + border-color: @rel-post-border-color-dark-theme; +} + +// Admonition +div.admonition.attention { + color: @admonition-attention-color-dark-theme; + background-color: @admonition-attention-bg-color-dark-theme; +} + +div.admonition.caution { + color: @admonition-caution-color-dark-theme; + background-color: @admonition-caution-bg-color-dark-theme; +} + +div.admonition.danger { + color: @admonition-danger-color-dark-theme; + background-color: @admonition-danger-bg-color-dark-theme; +} + +div.admonition.error { + color: @admonition-error-color-dark-theme; + background-color: @admonition-error-bg-color-dark-theme; +} + +div.admonition.hint { + color: @admonition-hint-color-dark-theme; + background-color: @admonition-hint-bg-color-dark-theme; +} + +div.admonition.important { + color: @admonition-important-color-dark-theme; + background-color: @admonition-important-bg-color-dark-theme; +} + +div.admonition.note { + color: @admonition-note-color-dark-theme; + background-color: @admonition-note-bg-color-dark-theme; +} + +div.admonition.tip { + color: @admonition-tip-color-dark-theme; + background-color: @admonition-tip-bg-color-dark-theme; +} + +div.admonition.warning { + color: @admonition-warning-color-dark-theme; + background-color: @admonition-warning-bg-color-dark-theme; +} diff --git a/static/stylesheet/dark-theme.min.css b/static/stylesheet/dark-theme.min.css new file mode 100644 index 0000000..9f2be03 --- /dev/null +++ b/static/stylesheet/dark-theme.min.css @@ -0,0 +1 @@ +body{background-color:#333;color:#eee}hr{color:#222}aside,hr{background-color:#222}aside{color:#fff}aside form.navbar-search input#tipue_search_input{background-color:#1a1a1a;color:#eee}main nav{border-bottom-color:#222}main .translations a,main nav a{border-color:#222}main article kbd{background-color:#080808;color:#eee}main article blockquote,main article pre{border-left:8px solid #ffffff33}main article :not(pre)>code{background-color:#080808;border-color:#000}main article div#tipue_search_content .tipue_search_result span.tipue_search_content_bold{color:#fff}main article section#isso-thread .auth-section p.input-wrapper input,main article section#isso-thread .notification-section input,main article section#isso-thread div.textarea{background:#1a1a1a;color:#eee}main article section#isso-thread>h4{color:#eee}main article section#isso-thread .isso-postbox>.form-wrapper .preview{background:repeating-linear-gradient(-45deg,#222,#222 10px,#1a1a1a 0,#1a1a1a 20px)}main article section#isso-thread .isso-comment>div.text-wrapper>.isso-comment-header .author{color:#eee}main article section#isso-thread .isso-comment>div.text-wrapper>.isso-comment-header .note,main article section#isso-thread .isso-comment>div.text-wrapper>.isso-comment-header .spacer,main article section#isso-thread .isso-comment>div.text-wrapper>.isso-comment-header a.parent,main article section#isso-thread .isso-comment>div.text-wrapper>.isso-comment-header a.permalink{color:#999}main article section#isso-thread .isso-comment>div.text-wrapper>.isso-comment-header .note:hover,main article section#isso-thread .isso-comment>div.text-wrapper>.isso-comment-header .spacer:hover,main article section#isso-thread .isso-comment>div.text-wrapper>.isso-comment-header a.parent:hover,main article section#isso-thread .isso-comment>div.text-wrapper>.isso-comment-header a.permalink:hover{color:#eee}main article section#isso-thread .isso-comment>div.text-wrapper>.isso-comment-footer a:hover{text-shadow:#242121 0 0 1px!important}main footer{border-top-color:#222}div.related-posts{border-color:#222}div.admonition.attention,div.admonition.caution{color:#fbda7a;background-color:#4a3900}div.admonition.danger,div.admonition.error{color:#ebadb3;background-color:#28070a}div.admonition.hint{color:#7abaff;background-color:#001933}div.admonition.important,div.admonition.note{color:#a8eab7;background-color:#122b18}div.admonition.tip{color:#7abaff;background-color:#001933}div.admonition.warning{color:#fbda7a;background-color:#4a3900} \ No newline at end of file diff --git a/static/stylesheet/style.less b/static/stylesheet/style.less index bc18dce..a7b8aa4 100644 --- a/static/stylesheet/style.less +++ b/static/stylesheet/style.less @@ -288,6 +288,11 @@ main { color: @footer-text-color; font-size: 11px; } + + // Flex credits and theme selection separator + span.footer-separator { + margin: 0 6px; + } } } diff --git a/static/stylesheet/variables.less b/static/stylesheet/variables.less index 4978c55..7cde418 100644 --- a/static/stylesheet/variables.less +++ b/static/stylesheet/variables.less @@ -6,6 +6,8 @@ @light-grey: #eeeeee; @med-grey: #999999; @dark-grey: #242121; +@solid-dark-grey: #222222; +@very-dark-grey: #1a1a1a; // Font family @sans: 'Source Sans Pro', 'Roboto', 'Open Sans', 'Liberation Sans', 'DejaVu Sans', 'Verdana', 'Helvetica', 'Arial', sans-serif; @@ -13,7 +15,9 @@ // Body @body-bg: @white; +@body-bg-dark-theme: @grey; @text-color: @dark-grey; +@text-color-dark-theme: @light-grey; // Links @link-color: @orange; @@ -21,6 +25,7 @@ // Sidebar (aside) @sidebar-bg: @grey; +@sidebar-bg-dark-theme: @solid-dark-grey; @sidebar-text-color: @white; @sidebar-link-color: @white; @sidebar-link-hover-color: @light-grey; @@ -41,12 +46,15 @@ // Footer @footer-text-color: @med-grey; @footer-border-color: @light-grey; +@footer-border-color-dark-theme: @solid-dark-grey; -// Relativer posts +// Relative posts @rel-post-border-color: @light-grey; +@rel-post-border-color-dark-theme: @solid-dark-grey; // Nav @nav-border-color: @light-grey; +@nav-border-color-dark-theme: @solid-dark-grey; // Social buttons @@ -77,37 +85,55 @@ // Admonition colors @admonition-attention-color: #856404; +@admonition-attention-color-dark-theme: #fbda7a; @admonition-attention-bg-color: #fff3cd; +@admonition-attention-bg-color-dark-theme: #4a3900; @admonition-attention-icon: "\f071\00a0 "; @admonition-caution-color: @admonition-attention-color; +@admonition-caution-color-dark-theme: @admonition-attention-color-dark-theme; @admonition-caution-bg-color: @admonition-attention-bg-color; +@admonition-caution-bg-color-dark-theme: @admonition-attention-bg-color-dark-theme; @admonition-caution-icon: @admonition-attention-icon; @admonition-warning-color: @admonition-attention-color; +@admonition-warning-color-dark-theme: @admonition-attention-color-dark-theme; @admonition-warning-bg-color: @admonition-attention-bg-color; +@admonition-warning-bg-color-dark-theme: @admonition-attention-bg-color-dark-theme; @admonition-warning-icon: @admonition-attention-icon; @admonition-danger-color: #721c24; +@admonition-danger-color-dark-theme: #ebadb3; @admonition-danger-bg-color: #f8d7da; +@admonition-danger-bg-color-dark-theme: #28070a; @admonition-danger-icon: "\f06a\00a0 "; @admonition-error-color: @admonition-danger-color; +@admonition-error-color-dark-theme: @admonition-danger-color-dark-theme; @admonition-error-bg-color: @admonition-danger-bg-color; +@admonition-error-bg-color-dark-theme: @admonition-danger-bg-color-dark-theme; @admonition-error-icon: @admonition-danger-icon; @admonition-hint-color: #004085 ; +@admonition-hint-color-dark-theme: #7abaff; @admonition-hint-bg-color: #cce5ff; +@admonition-hint-bg-color-dark-theme: #001933; @admonition-hint-icon: "\f0eb\00a0 "; @admonition-tip-color: @admonition-hint-color; +@admonition-tip-color-dark-theme: @admonition-hint-color-dark-theme; @admonition-tip-bg-color: @admonition-hint-bg-color; +@admonition-tip-bg-color-dark-theme: @admonition-hint-bg-color-dark-theme; @admonition-tip-icon: @admonition-hint-icon; @admonition-important-color: #155724; +@admonition-important-color-dark-theme: #a8eab7; @admonition-important-bg-color: #d4edda; +@admonition-important-bg-color-dark-theme: #122b18; @admonition-important-icon: "\f05a\00a0 "; @admonition-note-color: @admonition-important-color; +@admonition-note-color-dark-theme: @admonition-important-color-dark-theme; @admonition-note-bg-color: @admonition-important-bg-color; +@admonition-note-bg-color-dark-theme: @admonition-important-bg-color-dark-theme; @admonition-note-icon: @admonition-important-icon; diff --git a/templates/base.html b/templates/base.html index 81ba723..da4208f 100644 --- a/templates/base.html +++ b/templates/base.html @@ -28,6 +28,47 @@ {% endif %} + {# DARK THEME STYLES #} + {% if THEME_COLOR == "dark" or THEME_COLOR_AUTO_DETECT_BROWSER_PREFERENCE or THEME_COLOR_ENABLE_USER_OVERRIDE %} + + {% endif %} + + {# PYGMENTS STYLES #} + {% if THEME_COLOR_AUTO_DETECT_BROWSER_PREFERENCE or THEME_COLOR_ENABLE_USER_OVERRIDE or THEME_COLOR == "dark" %} + + {% endif %} + {% if THEME_COLOR_AUTO_DETECT_BROWSER_PREFERENCE or not THEME_COLOR or THEME_COLOR == "light" %} + + {% endif %} + {% if USE_TIPUE_SEARCH %} @@ -37,7 +78,6 @@ {% endif %} - @@ -96,7 +136,11 @@ {% include "partial/gtm.html" %} {% endif %} - + {% if GOOGLE_TAG_MANAGER %} {% include "partial/gtm_noscript.html" %} {% endif %} diff --git a/templates/partial/flex.html b/templates/partial/flex.html index 4447454..02d1465 100644 --- a/templates/partial/flex.html +++ b/templates/partial/flex.html @@ -1,3 +1,22 @@ -

{{ _('Built with %(pelican_url)s using %(flex_url)s theme', - pelican_url='Pelican', - flex_url='Flex'|safe) }}

+

+{{ + _('Built with %(pelican_url)s using %(flex_url)s theme', + pelican_url='Pelican', + flex_url='Flex'|safe) +}} +{% if THEME_COLOR_ENABLE_USER_OVERRIDE %} + | + {{ + _('Switch to the %(dark_url)s | %(light_url)s | %(browser_url)s theme', + dark_url='dark', + light_url='light', + browser_url='browser'|safe) + }} + +{% endif %} +

diff --git a/translations/messages.pot b/translations/messages.pot index ae67d05..2b6660d 100644 --- a/translations/messages.pot +++ b/translations/messages.pot @@ -87,11 +87,16 @@ msgstr "" msgid "Please enable JavaScript to view comments." msgstr "" -#: templates/partial/flex.html:1 +#: templates/partial/flex.html:3 #, python-format msgid "Built with %(pelican_url)s using %(flex_url)s theme" msgstr "" +#: templates/partial/flex.html:10 +#, python-format +msgid "Switch to the %(dark_url)s | %(light_url)s | %(browser_url)s theme" +msgstr "" + #: templates/partial/neighbors.html:5 msgid "Previous Post" msgstr ""