From f2184fb36fcacd6e581b87c75c3732fa0636455e Mon Sep 17 00:00:00 2001 From: himself65 <himself65@outlook.com> Date: Mon, 16 Dec 2019 22:53:46 +0800 Subject: [PATCH] feat: support dark theme --- components/Header.vue | 14 ++--- components/MobileHeader.vue | 4 +- components/Pagination.vue | 6 +-- components/ThemeToggle.vue | 44 +++++++++++++++ components/Toc.vue | 8 +-- components/util.js | 21 ++++++++ global-components/BaseListLayout.vue | 81 ++++++++++++++++++---------- global-components/BlogTag.vue | 4 +- global-components/BlogTags.vue | 2 +- global-components/NavLink.vue | 4 +- layouts/FrontmatterKey.vue | 10 ++++ layouts/GlobalLayout.vue | 24 ++++++++- layouts/Layout.vue | 10 ++++ layouts/Post.vue | 22 +++++--- package.json | 3 +- styles/code.styl | 8 +-- styles/index.styl | 20 +++---- yarn.lock | 5 ++ 18 files changed, 219 insertions(+), 71 deletions(-) create mode 100644 components/ThemeToggle.vue diff --git a/components/Header.vue b/components/Header.vue index 19b36c3..1ef4e79 100644 --- a/components/Header.vue +++ b/components/Header.vue @@ -26,12 +26,11 @@ import SearchBox from '@SearchBox' export default { - components: { SearchBox }, + components: { SearchBox } } </script> <style lang="stylus"> - @import '~@app/style/config' #header z-index 12 position fixed @@ -39,7 +38,7 @@ width 100vw box-sizing border-box // background lighten(#3eaf7c, 90%) - background-color #FFF + background-color var(--background) padding 20px 32px 20px margin auto box-shadow: 0 5px 20px rgba(0, 0, 0, 0.03), 0 6px 6px rgba(0, 0, 0, 0.05) @@ -64,7 +63,7 @@ /*flex 0 0 200px*/ // color #3eaf7c // color lighten(#3eaf7c, 10%) - color #000 + color var(--text) font-size 30px margin 0 letter-spacing 2px @@ -72,7 +71,7 @@ text-transform uppercase a - color #000 + color var(--text) font-weight bold font-family PT Serif, Serif text-decoration none @@ -93,6 +92,7 @@ margin-left 20px a + color var(--text) font-family PT Serif, Serif font-size 20px // color lighten(#3eaf7c, 30%) @@ -102,8 +102,10 @@ .search-box font-family PT Serif, Serif margin-left 20px + background-color transparent input + background-color transparent border-radius 5px transition all .5s border: 1px solid #cecece @@ -118,7 +120,7 @@ right 0 a - color #000 + color var(--text) text-decoration none &.focused diff --git a/components/MobileHeader.vue b/components/MobileHeader.vue index 1189d9c..58324a2 100644 --- a/components/MobileHeader.vue +++ b/components/MobileHeader.vue @@ -50,7 +50,7 @@ top 0 width 100vw box-sizing border-box - background-color #fff + background-color var(--background) margin auto box-shadow 0 5px 20px rgba(0,0,0,0.03), 0 6px 6px rgba(0,0,0,0.05) transition all 1s cubic-bezier(0.25, 0.8, 0.25, 1) @@ -84,7 +84,7 @@ max-height 0 overflow hidden transition 0.3s ease - background-color #fff + background-color var(--background) .mobile-menu-wrapper.open max-height 450px diff --git a/components/Pagination.vue b/components/Pagination.vue index 28936ad..3ccb61e 100644 --- a/components/Pagination.vue +++ b/components/Pagination.vue @@ -32,21 +32,21 @@ .pagination a margin-right: 20px - color: #000 + color: var(--text) height: 38px line-height: 38px transition: all .3s ease position: relative overflow: hidden display: inline-block - background #FFF + background var(--background) padding: 0 15px text-decoration: none border 1px solid #000 border-radius 5px transition all .5s &:hover - color #FFF + color var(--text) border 1px solid $accentColor background-color $accentColor </style> diff --git a/components/ThemeToggle.vue b/components/ThemeToggle.vue new file mode 100644 index 0000000..7bfc2e8 --- /dev/null +++ b/components/ThemeToggle.vue @@ -0,0 +1,44 @@ +<template> + <ToggleButton + :labels="{ checked: 'Dark', unchecked: 'Light' }" + :value="this.defaultValue" + :width="55" + @change="onChange" + color="#8a278c" + /> +</template> + +<script> + import {ToggleButton} from 'vue-js-toggle-button' + import {darkThemeKey, darkTheme} from './util' + + export default { + components: {ToggleButton}, + data() { + return { + defaultValue: false + } + }, + created() { + this.defaultValue = window.localStorage.getItem(darkThemeKey) === 'true' || false + }, + methods: { + onChange({value}) { + window.localStorage.setItem(darkThemeKey, value) + this.setTheme(value) + }, + + setTheme(isDark) { + const style = window.document.body.style + Object.keys(darkTheme).forEach(key => { + isDark && style.setProperty(`--${key}`, darkTheme[key]) + !isDark && style.removeProperty(`--${key}`) + }) + } + } + } +</script> + +<style scoped> + +</style> diff --git a/components/Toc.vue b/components/Toc.vue index a980767..f879e16 100644 --- a/components/Toc.vue +++ b/components/Toc.vue @@ -157,7 +157,7 @@ a display: block; - color: #2c3e50; + color: var(--text--link); width: 100%; box-sizing: border-box; font-size: 12px; @@ -169,14 +169,14 @@ white-space: nowrap; &.active - border-left-color: $accentColor; + border-left-color: var(--text--link--lighten); a - color: $accentColor; + color: var(--text--link--lighten); &:hover a - color: $accentColor; + color: var(--text--link--lighten); for i in range(3, 6) .vuepress-toc-h{i} a diff --git a/components/util.js b/components/util.js index 613a489..e78ee70 100644 --- a/components/util.js +++ b/components/util.js @@ -2,6 +2,18 @@ export const hashRE = /#.*$/ export const extRE = /\.(md|html)$/ export const endingSlashRE = /\/$/ export const outboundRE = /^(https?:|mailto:|tel:)/ +export const darkThemeKey = 'ui-dark-theme' +export const darkTheme = { + 'background': '#282c35', + 'code--background': '#373c49', + 'text': '#fff', + 'text--code': '#fff', + 'text--mask': 'rgba(255, 255, 255, 0.84)', + 'text--mask2': 'rgba(255, 255, 255, 0.54)', + 'text--link': '#ffa7c4', + 'text--link--lighten': '#d23669', + 'title': '#8a278c' +} export function normalize (path) { return decodeURI(path) @@ -21,6 +33,15 @@ export function isTel (path) { return /^tel:/.test(path) } +export function initTheme() { + const isDark = window.localStorage.getItem(darkThemeKey) === 'true' || false + const style = window.document.body.style + Object.keys(darkTheme).forEach(key => { + isDark && style.setProperty(`--${key}`, darkTheme[key]) + !isDark && style.removeProperty(`--${key}`) + }) +} + export function ensureExt (path) { if (isExternal(path)) { return path diff --git a/global-components/BaseListLayout.vue b/global-components/BaseListLayout.vue index 6af0b7a..cda7ccb 100644 --- a/global-components/BaseListLayout.vue +++ b/global-components/BaseListLayout.vue @@ -1,11 +1,16 @@ <template> <div id="base-list-layout"> <div class="ui-posts"> + <div class="ui-functional"> + <span class="ui-site-title">{{ $site.title }}</span> + <component v-if="themeToggle" :is="themeToggle"/> + </div> + <div class="ui-post" v-for="page in pages"> <div class="ui-post-title"> <NavLink :link="page.path">{{ page.title }}</NavLink> </div> - + <div class="ui-post-summary"> {{ page.frontmatter.summary || page.summary }} <!-- <Content :page-key="page.key" slot-key="intro"/>--> @@ -22,37 +27,45 @@ </div> </div> </div> - + <component v-if="$pagination.length > 1 && paginationComponent" :is="paginationComponent"></component> </div> </template> <script> /* global THEME_BLOG_PAGINATION_COMPONENT */ - + import Vue from 'vue' - import { NavigationIcon, ClockIcon } from 'vue-feather-icons' - import { Pagination, SimplePagination } from '@vuepress/plugin-blog/lib/client/components' - + import {NavigationIcon, ClockIcon} from 'vue-feather-icons' + import {Pagination, SimplePagination} from '@vuepress/plugin-blog/lib/client/components' + export default { - components: { NavigationIcon, ClockIcon }, + components: {NavigationIcon, ClockIcon}, data() { return { - paginationComponent: null + paginationComponent: null, + themeToggle: null } }, - + created() { this.paginationComponent = this.getPaginationComponent() }, - + + beforeMount() { + import('@theme/components/ThemeToggle.vue') + .then(module => { + this.themeToggle = module.default + }) + }, + computed: { pages() { return this.$pagination.pages }, }, - + methods: { getPaginationComponent() { const n = THEME_BLOG_PAGINATION_COMPONENT @@ -74,64 +87,78 @@ } </script> -<style lang="stylus"> +<style lang="stylus" scoped> .common-layout .content-wrapper padding-bottom 80px - + + .ui-functional + display flex + flex-direction row + align-items center + justify-content space-between + width 100% + margin-bottom 2rem + + .ui-site-title + color var(--title) + font-size 25px + font-weight 900 + + .ui-post padding-bottom 25px margin-bottom 25px border-bottom 1px solid #f1f1f1 - + &:last-child border-bottom 0px margin-bottom 0px - + p margin 0 - + .ui-post-title font-family PT Serif, Serif font-size 28px border-bottom 0 - + a cursor pointer - color #000 + color var(--text) transition all .2s text-decoration none - + &:hover text-decoration underline - + .ui-post-summary font-size 14px margin-bottom 15px - color rgba(0, 0, 0, 0.54) + color var(--text--mask) font-weight 200 - + .ui-post-author display flex align-items center font-size 12px line-height 12px - color rgba(0, 0, 0, 0.84) + color var(--text--mask2) margin-bottom 3px font-weight 400 - + svg margin-right 5px width 14px height 14px - + .ui-post-date display flex align-items center font-size 12px - color rgba(0, 0, 0, 0.54) + color var(--text--mask) font-weight 200 - + svg margin-right 5px width 14px diff --git a/global-components/BlogTag.vue b/global-components/BlogTag.vue index b656621..844bcc3 100644 --- a/global-components/BlogTag.vue +++ b/global-components/BlogTag.vue @@ -25,8 +25,8 @@ text-align left box-sizing border-box transition: background-color .3s - color #000 - border 1px solid #000 + color var(--text) + border 1px solid var(--text) text-decoration none transition all .5s diff --git a/global-components/BlogTags.vue b/global-components/BlogTags.vue index 4ecb9fb..b2db956 100644 --- a/global-components/BlogTags.vue +++ b/global-components/BlogTags.vue @@ -17,7 +17,7 @@ <style lang="stylus"> .blog-tags width 66% - + @media screen and (max-width: 1000px) .blog-tags width 90% diff --git a/global-components/NavLink.vue b/global-components/NavLink.vue index f853eca..1b348dc 100644 --- a/global-components/NavLink.vue +++ b/global-components/NavLink.vue @@ -51,9 +51,9 @@ export default { <style lang="stylus"> .nav-link - color #000 + color var(--text) .nav-link &:hover, &.router-link-active - color $accentColor + color var(--text--link) </style> diff --git a/layouts/FrontmatterKey.vue b/layouts/FrontmatterKey.vue index 071037a..b5e2a92 100644 --- a/layouts/FrontmatterKey.vue +++ b/layouts/FrontmatterKey.vue @@ -3,3 +3,13 @@ <BlogTags :tags="$frontmatterKey.list"/> </div> </template> + +<script> + import {initTheme} from '../components/util' + + export default { + mounted() { + initTheme() + } + } +</script> diff --git a/layouts/GlobalLayout.vue b/layouts/GlobalLayout.vue index 9dd285a..1bb831b 100644 --- a/layouts/GlobalLayout.vue +++ b/layouts/GlobalLayout.vue @@ -14,6 +14,7 @@ import Header from '@theme/components/Header.vue' import MobileHeader from '@theme/components/MobileHeader.vue' import Footer from '@theme/components/Footer.vue' + import {initTheme} from '../components/util' export default { components: { @@ -30,14 +31,35 @@ }, mounted() { - this.$router.afterEach(()=>{ + initTheme() + this.$router.afterEach(() => { this.isMobileHeaderOpen = false }) } } </script> + +<style lang="css"> + body { + --background: #fff; + --code--background: #000; + --text: #000; + --text--code: #fff; + --text--mask: rgba(0, 0, 0, 0.84); + --text--mask2: rgba(0, 0, 0, 0.54); + --text--link: #d05dd2; + --text--link--lighten: #8a278c; + --title: #2c3e50; + } +</style> + <style lang="stylus"> + @import "../styles/config.styl" + + #vuperess-theme-blog__global-layout + background-color var(--background) + .content-wrapper padding 160px 15px 80px 15px min-height calc(100vh - 80px - 60px - 160px) diff --git a/layouts/Layout.vue b/layouts/Layout.vue index 106e6df..d7120cb 100644 --- a/layouts/Layout.vue +++ b/layouts/Layout.vue @@ -5,4 +5,14 @@ </div> </template> +<script> + import {initTheme} from '../components/util' + + export default { + mounted() { + initTheme() + } + } +</script> + <style src="prismjs/themes/prism-okaidia.css"></style> diff --git a/layouts/Post.vue b/layouts/Post.vue index b70c986..1f9c440 100644 --- a/layouts/Post.vue +++ b/layouts/Post.vue @@ -11,30 +11,36 @@ <script> import Toc from '@theme/components/Toc.vue' - import { Comment } from '@vuepress/plugin-blog/lib/client/components' - + import {Comment} from '@vuepress/plugin-blog/lib/client/components' + import {initTheme} from '../components/util' + export default { components: { Toc, - Comment, + Comment }, + + mounted() { + initTheme() + } } </script> <style lang="stylus"> -@require '../styles/wrapper.styl'; + @require '../styles/wrapper.styl'; .vuepress-blog-theme-content @extend $wrapper font-size 16px letter-spacing 0px font-family PT Serif, Serif - color #2c3e50 + color var(--text) + background-color var(--background) position relative - @media(min-width: $MQNarrow) - box-shadow: 0 10px 20px rgba(0,0,0,0.05), 0 6px 6px rgba(0,0,0,0.07) - + @media (min-width: $MQNarrow) + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.05), 0 6px 6px rgba(0, 0, 0, 0.07) + </style> <style src="prismjs/themes/prism-okaidia.css"></style> diff --git a/package.json b/package.json index 84d5411..0774cc3 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,8 @@ "@vuepress/plugin-pwa": "1.0.0", "@vuepress/plugin-search": "1.0.0", "remove-markdown": "^0.3.0", - "vue-feather-icons": "^4.21.0" + "vue-feather-icons": "^4.21.0", + "vue-js-toggle-button": "^1.3.3" }, "devDependencies": { "conventional-changelog-cli": "^2.0.1", diff --git a/styles/code.styl b/styles/code.styl index 8d3c1d1..5854ede 100644 --- a/styles/code.styl +++ b/styles/code.styl @@ -1,6 +1,6 @@ {$contentClass} code - color lighten($textColor, 20%) + color var(--text--link) padding 0.1rem 0.4rem margin 0.1rem font-size 0.85em @@ -21,14 +21,14 @@ border-radius 6px overflow auto code - color #fff + color var(--text--code) padding 0 background-color transparent border-radius 0 div[class*="language-"] position relative - background-color $codeBgColor + background-color var(--code--background) border-radius 6px .highlight-lines user-select none @@ -95,7 +95,7 @@ div[class*="language-"] height 100% border-radius 6px 0 0 6px border-right 1px solid rgba(0, 0, 0, 66%) - background-color $codeBgColor + background-color var(--code--background) for lang in $codeLang diff --git a/styles/index.styl b/styles/index.styl index 5fa0348..48686da 100644 --- a/styles/index.styl +++ b/styles/index.styl @@ -10,15 +10,15 @@ html, body padding 0 margin 0 - background-color #fff + background-color var(--background) body font-family -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif -webkit-font-smoothing antialiased -moz-osx-font-smoothing grayscale font-size 16px - color $textColor - background-color #FFF + color var(--title) + background-color var(--background) .page padding-left $sidebarWidth @@ -30,7 +30,7 @@ body left 0 right 0 height $navbarHeight - background-color #fff + background-color var(--background) box-sizing border-box border-bottom 1px solid $borderColor @@ -45,7 +45,7 @@ body .sidebar font-size 16px - background-color #fff + background-color var(--background) width $sidebarWidth position fixed z-index 10 @@ -81,14 +81,14 @@ body a font-weight 500 - color $accentColor + color var(--text--link) text-decoration underline &:hover - color darken($accentColor, 20%) + color var(--text--link--lighten) p a code font-weight 400 - color $accentColor + color var(--text--link--lighten) kbd background #eee @@ -99,8 +99,8 @@ kbd blockquote font-size .9rem - color darken($accentColor, 50%) - border-left: 3px solid $accentColor; + color var(--text--link--lighten) + border-left: 3px solid var(--text--link--lighten); margin 0.5rem 0 padding .25rem 0 .25rem 1rem diff --git a/yarn.lock b/yarn.lock index 6866b81..2a90416 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8204,6 +8204,11 @@ vue-i18n@^8.11.2: resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-8.15.0.tgz#9b11ef8e7a124f67cdf788c8c90a81f3606240ed" integrity sha512-juJ/avAP39bOMycC+qQDLJ8U9z9LtLF/9PsRoJLBSfsYZo9bqYntyyX5QPicwlb1emJKjgxhZ3YofHiQcXBu0Q== +vue-js-toggle-button@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/vue-js-toggle-button/-/vue-js-toggle-button-1.3.3.tgz#d603089039e41d45e607355ad2e0478c6a52aceb" + integrity sha512-0b920oztgK+1SqlYF26MPiT28hAieL5aAQE7u21XEym5ryfzD4EMer4hLkgDC/1sWsCHb22GvV+t1Kb4AI6QFw== + vue-loader@^15.7.1: version "15.7.1" resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.7.1.tgz#6ccacd4122aa80f69baaac08ff295a62e3aefcfd"