]> BookStack Code Mirror - bookstack/commitdiff
Started work on supporting a dark-mode
authorDan Brown <redacted>
Fri, 10 Apr 2020 21:38:29 +0000 (22:38 +0100)
committerDan Brown <redacted>
Fri, 10 Apr 2020 21:38:29 +0000 (22:38 +0100)
- Most elements done, but still need to do editors, tables and final
pass.
- Toggled only by quick js check at the moment, checking via css media
query. Need to make into user-preference toggle.

For #1234

16 files changed:
resources/js/services/code.js
resources/sass/_blocks.scss
resources/sass/_buttons.scss
resources/sass/_codemirror.scss
resources/sass/_colors.scss
resources/sass/_forms.scss
resources/sass/_header.scss
resources/sass/_html.scss
resources/sass/_lists.scss
resources/sass/_mixins.scss
resources/sass/_tables.scss
resources/sass/_text.scss
resources/sass/_variables.scss
resources/sass/styles.scss
resources/views/base.blade.php
resources/views/settings/index.blade.php

index 14801de9c9aaec93cf89d8955c36d947ff2a68bb..a7dfa587f203dcea41b2c781c36887e0d6b28d82 100644 (file)
@@ -191,7 +191,8 @@ function getMode(suggestion, content) {
  * @returns {*|string}
  */
 function getTheme() {
-    return window.codeTheme || 'default';
+    const darkMode = document.documentElement.classList.contains('dark-mode');
+    return window.codeTheme || (darkMode ? 'darcula' : 'default');
 }
 
 /**
index d02d25db4baa127018365b945ac1febe58381417..9e04420a0e8aa85860eced6d0574fe382d364fc9 100644 (file)
@@ -58,7 +58,7 @@
  */
 
 .card {
-  background-color: #FFF;
+  @include lightDark(background-color, #FFF, #222);
   box-shadow: $bs-card;
   border-radius: 3px;
   border: 1px solid transparent;
index 7df1d61a47668c1927f12eea2112f81eb4c015a8..8c8bac54ec72ef65c7075b400d0d66278eca4721 100644 (file)
@@ -22,17 +22,17 @@ button {
   box-shadow: none;
   background-color: var(--color-primary);
   color: #FFF;
-  fill: #FFF;
   text-transform: uppercase;
   border: 1px solid var(--color-primary);
   vertical-align: top;
+  @include lightDark(filter, none, saturate(0.8) brightness(0.8));
   &:hover, &:focus, &:active {
     background-color: var(--color-primary);
     text-decoration: none;
     color: #FFFFFF;
   }
   &:hover {
-    box-shadow: $bs-light;
+    @include lightDark(box-shadow, $bs-light, $bs-dark);
     filter: brightness(110%);
   }
   &:focus {
@@ -48,13 +48,14 @@ button {
 
 .button.outline {
   background-color: transparent;
-  color: #666;
+  @include lightDark(color, #666, #aaa);
   fill: currentColor;
   border: 1px solid #CCC;
   &:hover, &:focus, &:active {
     border: 1px solid #CCC;
     box-shadow: none;
     background-color: #F2F2F2;
+    @include lightDark(background-color, #f2f2f2, #555);
     filter: none;
   }
   &:active {
index dc1aef9bb2921b112acb3344754cbc5726d3224b..c679e4899fcceb94e1b66342ebc77c4194616a5d 100644 (file)
@@ -390,6 +390,63 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
 
 /* STOP */
 
+/**
+ * Codemirror Darcula theme
+ */
+
+/**
+    Name: IntelliJ IDEA darcula theme
+    From IntelliJ IDEA by JetBrains
+ */
+
+.cm-s-darcula  { font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif;}
+.cm-s-darcula.CodeMirror { background: #2B2B2B; color: #A9B7C6; }
+
+.cm-s-darcula span.cm-meta { color: #BBB529; }
+.cm-s-darcula span.cm-number { color: #6897BB; }
+.cm-s-darcula span.cm-keyword { color: #CC7832; line-height: 1em; font-weight: bold; }
+.cm-s-darcula span.cm-def { color: #A9B7C6; font-style: italic; }
+.cm-s-darcula span.cm-variable { color: #A9B7C6; }
+.cm-s-darcula span.cm-variable-2 { color: #A9B7C6; }
+.cm-s-darcula span.cm-variable-3 { color: #9876AA; }
+.cm-s-darcula span.cm-type { color: #AABBCC; font-weight: bold; }
+.cm-s-darcula span.cm-property { color: #FFC66D; }
+.cm-s-darcula span.cm-operator { color: #A9B7C6; }
+.cm-s-darcula span.cm-string { color: #6A8759; }
+.cm-s-darcula span.cm-string-2 { color: #6A8759; }
+.cm-s-darcula span.cm-comment { color: #61A151; font-style: italic; }
+.cm-s-darcula span.cm-link { color: #CC7832; }
+.cm-s-darcula span.cm-atom { color: #CC7832; }
+.cm-s-darcula span.cm-error { color: #BC3F3C; }
+.cm-s-darcula span.cm-tag { color: #629755; font-weight: bold; font-style: italic; text-decoration: underline; }
+.cm-s-darcula span.cm-attribute { color: #6897bb; }
+.cm-s-darcula span.cm-qualifier { color: #6A8759; }
+.cm-s-darcula span.cm-bracket { color: #A9B7C6; }
+.cm-s-darcula span.cm-builtin { color: #FF9E59; }
+.cm-s-darcula span.cm-special { color: #FF9E59; }
+.cm-s-darcula span.cm-matchhighlight { color: #FFFFFF; background-color: rgba(50, 89, 48, .7); font-weight: normal;}
+.cm-s-darcula span.cm-searching { color: #FFFFFF; background-color: rgba(61, 115, 59, .7); font-weight: normal;}
+
+.cm-s-darcula .CodeMirror-cursor { border-left: 1px solid #A9B7C6; }
+.cm-s-darcula .CodeMirror-activeline-background { background: #323232; }
+.cm-s-darcula .CodeMirror-gutters { background: #313335; border-right: 1px solid #313335; }
+.cm-s-darcula .CodeMirror-guttermarker { color: #FFEE80; }
+.cm-s-darcula .CodeMirror-guttermarker-subtle { color: #D0D0D0; }
+.cm-s-darcula .CodeMirrir-linenumber { color: #606366; }
+.cm-s-darcula .CodeMirror-matchingbracket { background-color: #3B514D; color: #FFEF28 !important; font-weight: bold; }
+
+.cm-s-darcula div.CodeMirror-selected { background: #214283; }
+
+.CodeMirror-hints.darcula {
+  font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;
+  color: #9C9E9E;
+  background-color: #3B3E3F !important;
+}
+
+.CodeMirror-hints.darcula .CodeMirror-hint-active {
+  background-color: #494D4E !important;
+  color: #9C9E9E !important;
+}
 
 /**
  * Custom BookStack overrides
@@ -401,7 +458,8 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
   font-size: 12px;
   height: auto;
   margin-bottom: $-l;
-  border: 1px solid #DDD;;
+  border: 1px solid;
+  @include lightDark(border-color, #DDD, #111)
 }
 
 .cm-s-mdn-like .CodeMirror-gutters { background: #f8f8f8; border-left: 0; color: #333; }
@@ -424,24 +482,25 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
   top: -1px;
   right: -1px;
   background-color: #EEE;
+  border: 1px solid #DDD;
+  @include lightDark(background-color, #eee, #333);
+  @include lightDark(border-color, #ddd, #444);
+  @include lightDark(fill, #444, #888);
   padding: $-xs;
   line-height: 0;
-  border: 1px solid #DDD;
   cursor: pointer;
-  fill: #444;
   z-index: 5;
-  transition: all ease-in 240ms;
   user-select: none;
   opacity: 0;
   pointer-events: none;
   svg {
-    transition: transform ease-in 240ms;
+    transition: all ease-in 240ms;
     transform: translateY(0);
   }
   &.success {
     background-color: lighten($positive, 10%);
-    fill: #FFF;
     svg {
+      fill: #FFF;
       transform: translateY(-3px);
     }
   }
index 77f51b3243eb96a9473721eb5adc344c4779b273..e9ddd2214c4976c351cd33ec3ecb60f3447420d3 100644 (file)
 }
 
 .text-muted {
-  color: #575757 !important;
-  fill: #575757 !important;
+  @include lightDark(color, #575757, #888888, true);
+  fill: currentColor !important;
 }
 
 .text-dark {
-  color: #222 !important;
-  fill: #222 !important;
+  @include lightDark(color, #222, #ccc, true);
+  fill: currentColor !important;
 }
 
 /*
index f306a717b3608225ef15daf3f992c4e2c7491054..535bc54ea7cbe1c0851e6e0a601f01d39077b15a 100644 (file)
@@ -1,12 +1,13 @@
 
 .input-base {
-  background-color: #FFF;
   border-radius: 3px;
   border: 1px solid #D4D4D4;
+  @include lightDark(background-color, #fff, #333);
+  @include lightDark(border-color, #d4d4d4, #111);
+  @include lightDark(color, #666, #AAA);
   display: inline-block;
-  font-size: $fs-s;
+  font-size: $fs-m;
   padding: $-xs*1.5;
-  color: #666;
   width: 250px;
   max-width: 100%;
 
 
 
 label {
+  @include lightDark(color, #666, #ddd);
   display: block;
   line-height: 1.4em;
   font-size: 0.94em;
   font-weight: 400;
-  color: #666;
   padding-bottom: 2px;
   margin-bottom: 0.2em;
   &.inline {
index 5503a0895d5c5ca6b83e4a08ca7357dc43589f2d..1c4bf948ff0f3c9366a048b29ca3290ace6df3b3 100644 (file)
@@ -18,11 +18,14 @@ header {
   display: block;
   z-index: 11;
   top: 0;
-  color: #fff;
-  fill: #fff;
+  color: rgb(250, 250, 250);
   border-bottom: 1px solid #DDD;
   box-shadow: $bs-card;
   padding: $-xxs 0;
+  @include lightDark(border-bottom-color, #DDD, #000);
+  @include whenDark {
+    filter: saturate(0.6) brightness(0.8);
+  }
   .links {
     display: inline-block;
     vertical-align: top;
@@ -31,7 +34,6 @@ header {
     display: inline-block;
     padding: $-m;
     color: #FFF;
-    fill: #FFF;
   }
   .dropdown-container {
     padding-inline-start: $-m;
@@ -94,7 +96,6 @@ header .search-box {
     }
   }
   button {
-    fill: #EEE;
     z-index: 1;
     left: 16px;
     @include rtl {
index e4a8c14bb8faf0b75bc3abeb0e1ad0d10320eb46..57869d6520b04aa6a90ea5405c36d8a95de0927f 100644 (file)
@@ -15,12 +15,14 @@ html {
   &.flexbox {
     overflow-y: hidden;
   }
+  &.dark-mode {
+    background-color: #111;
+  }
 }
 
 body {
   font-size: $fs-m;
   line-height: 1.6;
-  color: #444;
+  @include lightDark(color, #444, #AAA);
   -webkit-font-smoothing: antialiased;
-  background-color: #F2F2F2;
 }
\ No newline at end of file
index 7beb63d4e57b01ad1840a8425ba7e365ec4efb93..2e12c20093ae1a9bfacbad8bdf06165421e43492 100644 (file)
@@ -99,7 +99,7 @@
       left: auto;
       right: 0;
     }
-    background-color: rgba(0, 0, 0, 0.2);
+    @include lightDark(background-color, rgba(0, 0, 0, 0.2), rgba(255, 255, 255, 0.2));
     width: 2px;
     top: 5px;
     bottom: 5px;
     font-weight: bold;
   }
   li:not(.current-heading) .sidebar-page-nav-bullet {
-    background-color: #BBB !important;
+    @include lightDark(background-color, #BBB, #666, true);
   }
   .sidebar-page-nav-bullet {
     width: 6px;
     top: 30%;
     border-radius: 50%;
     box-shadow: 0 0 0 6px #F2F2F2;
+    @include lightDark(box-shadow, 0 0 0 6px #F2F2F2, 0 0 0 6px #111);
     z-index: 1;
     @include rtl {
       left: auto;
@@ -438,7 +439,7 @@ ul.pagination {
     border-color: rgba(0, 0, 0, 0.1);
   }
   &:focus {
-    background-color: #eee;
+    @include lightDark(background-color, #eee, #222);
     outline: 1px dotted #666;
     outline-offset: -2px;
   }
@@ -455,7 +456,7 @@ ul.pagination {
 }
 
 .card .entity-list-item:not(.no-hover):hover {
-  background-color: #F2F2F2;
+  @include lightDark(background-color, #F2F2F2, #2d2d2d)
 }
 .card .entity-list-item .entity-list-item:hover {
   background-color: #EEEEEE;
@@ -554,14 +555,15 @@ ul.pagination {
   list-style: none;
   right: 0;
   margin: $-m 0;
-  background-color: #FFFFFF;
+  @include lightDark(background-color, #fff, #333);
   box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.1);
   border-radius: 1px;
   border: 1px solid #EEE;
+  @include lightDark(border-color, #eee, #000);
   min-width: 180px;
   padding: $-xs 0;
-  color: #555;
-  fill: #555;
+  @include lightDark(color, #555, #eee);
+  fill: currentColor;
   text-align: start !important;
   &.wide {
     min-width: 220px;
@@ -576,7 +578,7 @@ ul.pagination {
   a, button {
     display: block;
     padding: $-xs $-m;
-    color: #555;
+    @include lightDark(color, #555, #eee);
     fill: currentColor;
     white-space: nowrap;
     &:hover, &:focus {
@@ -653,11 +655,10 @@ ul.pagination {
     padding: $-s;
   }
   a:not(.active) {
-    color: #444;
-    fill: #444;
+    @include lightDark(color, #444, #666);
   }
   a:hover {
-    background-color: rgba(0, 0, 0, 0.05);
+    @include lightDark(background-color, rgba(0, 0, 0, 0.05), rgba(255, 255, 255, 0.05));
     border-radius: 3px;
     text-decoration: none;
   }
index 8a6becf6b70746131a0e54b05545b4bc288721f9..0a419c08abdeb1372e331277fcd157394d100918 100644 (file)
   html[dir=rtl] & {
     @content;
   }
+}
+
+// Define a property for both light and dark mode
+@mixin lightDark($prop, $light, $dark, $important: false) {
+  #{$prop}: if($important, $light !important, $light);
+  html.dark-mode & {
+    #{$prop}: if($important, $dark !important, $dark);
+  }
+}
+
+@mixin whenDark {
+    html.dark-mode & {
+      @content;
+    }
 }
\ No newline at end of file
index 2778736083662ac7901612bc4b590c09547da625..d0a920efcbcd7f706f7d78e88748586c33b3a9a4 100644 (file)
@@ -33,7 +33,7 @@ table.table {
     font-weight: bold;
   }
   tr:hover {
-    background-color: #EEE;
+    @include lightDark(background-color, #eee, #333);
   }
   .text-right {
     text-align: end;
index 8d2759b91700262b5919e6c1fcb206a2cf305c4a..e629724978b6933afbeba7a37d6a6c3f0523ff8f 100644 (file)
@@ -42,7 +42,7 @@ h1, h2, h3, h4, h5, h6 {
   font-weight: 400;
   position: relative;
   display: block;
-  color: #222;
+  @include lightDark(color, #222, #BBB);
   .subheader {
     font-size: 0.5em;
     line-height: 1em;
@@ -91,11 +91,14 @@ h2.list-heading {
  */
 a {
   color: var(--color-primary);
-  fill: var(--color-primary);
+  fill: currentColor;
   cursor: pointer;
   text-decoration: none;
   transition: filter ease-in-out 80ms;
   line-height: 1.6;
+  @include whenDark {
+    filter: brightness(1.3) saturate(0.7);
+  }
   &:hover {
     text-decoration: underline;
   }
@@ -130,7 +133,7 @@ p, ul, ol, pre, table, blockquote {
 hr {
   border: 0;
   height: 1px;
-  background: #EAEAEA;
+  @include lightDark(background, #eaeaea, #222);
   margin-bottom: $-l;
   &.faded {
     background-image: linear-gradient(to right, #FFF, #e3e0e0 20%, #e3e0e0 80%, #FFF);
@@ -153,7 +156,7 @@ em, i, .italic {
 
 small, p.small, span.small, .text-small {
   font-size: 0.75rem;
-  color: lighten($text-dark, 10%);
+  @include lightDark(color, #5e5e5e, #999);
 }
 
 sup, .superscript {
@@ -168,8 +171,9 @@ sub, .subscript {
 
 pre {
   font-size: 12px;
-  background-color: #f5f5f5;
   border: 1px solid #DDD;
+  @include lightDark(background-color, #f5f5f5, #2B2B2B);
+  @include lightDark(border-color, #DDD, #111);
   padding-left: 31px;
   position: relative;
   padding-top: 3px;
@@ -181,9 +185,9 @@ pre {
     top: 0;
     width: 29px;
     left: 0;
-    background-color: #f5f5f5;
     height: 100%;
-    border-right: 1px solid #DDD;
+    @include lightDark(background-color, #f5f5f5, #313335);
+    @include lightDark(border-right, 1px solid #DDD, none);
   }
 }
 
@@ -226,10 +230,11 @@ blockquote {
 }
 
 .code-base {
-    background-color: #F8F8F8;
-    font-size: 0.80em;
-    border: 1px solid #DDD;
-    border-radius: 3px;
+  font-size: 0.84em;
+  border: 1px solid #DDD;
+  border-radius: 3px;
+  @include lightDark(background-color, #f8f8f8f, #2b2b2b);
+  @include lightDark(border-color, #DDD, #444);
 }
 
 code {
@@ -385,4 +390,5 @@ span.sep {
   bottom: -0.105em;
   margin-inline-end: $-xs;
   pointer-events: none;
+  fill: currentColor;
 }
index 2d4d3970af2d028b98f825cf2b5833395db81fb1..061a837467b2abe0f927aaaba1936aefe8fe33d3 100644 (file)
@@ -63,6 +63,7 @@ $text-light: #EEE;
 
 // Shadows
 $bs-light: 0 0 4px 1px #CCC;
+$bs-dark: 0 0 4px 1px rgba(0, 0, 0, 0.5);
 $bs-med: 0 1px 3px 1px rgba(76, 76, 76, 0.26);
 $bs-large: 0 1px 6px 1px rgba(22, 22, 22, 0.2);
 $bs-card: 0 1px 6px -1px rgba(0, 0, 0, 0.1);
index 09d8b41000f199d6599a1fd96a400a7e226a0e0f..74e4a4fde2f88bb6c1de3ee408d3c4bcf042ffaf 100644 (file)
@@ -161,6 +161,7 @@ $btt-size: 40px;
 
 .entity-selector {
   border: 1px solid #DDD;
+  @include lightDark(border-color, #ddd, #111);
   border-radius: 3px;
   overflow: hidden;
   font-size: 0.8em;
@@ -176,12 +177,12 @@ $btt-size: 40px;
   .entity-list {
     overflow-y: scroll;
     height: 400px;
-    background-color: #EEEEEE;
+    @include lightDark(background-color, #eee, #222);
     margin-inline-end: 0;
     margin-inline-start: 0;
   }
   .entity-list-item {
-    background-color: #FFF;
+    @include lightDark(background-color, #fff, #222);
   }
   .entity-list-item p {
     margin-bottom: 0;
index d362ef37360dcafe48a5157865ba85dd11529a5a..0f43532fb8f9b7464b35a5fd5a7bf3f609331ca2 100644 (file)
     <!-- Translations for JS -->
     @stack('translations')
 
+    <script>
+        if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
+            document.documentElement.classList.add('dark-mode');
+        }
+    </script>
 </head>
 <body class="@yield('body-class')">
 
index 07ca1fcc19f5555c9691a9f92f686a9d27cdf849..6ccb8d8f9d1d50aabf1290991aaccdcab9f2858b 100644 (file)
@@ -92,7 +92,7 @@
                             <label for="setting-app-name" class="setting-list-label">{{ trans('settings.app_name') }}</label>
                             <p class="small">{{ trans('settings.app_name_desc') }}</p>
                         </div>
-                        <div>
+                        <div class="pt-xs">
                             <input type="text" value="{{ setting('app-name', 'BookStack') }}" name="setting-app-name" id="setting-app-name">
                             @include('components.toggle-switch', [
                                 'name' => 'setting-app-name-header',
                             <label class="setting-list-label">{{ trans('settings.app_editor') }}</label>
                             <p class="small">{{ trans('settings.app_editor_desc') }}</p>
                         </div>
-                        <div>
+                        <div class="pt-xs">
                             <select name="setting-app-editor" id="setting-app-editor">
                                 <option @if(setting('app-editor') === 'wysiwyg') selected @endif value="wysiwyg">WYSIWYG</option>
                                 <option @if(setting('app-editor') === 'markdown') selected @endif value="markdown">Markdown</option>
                             <label class="setting-list-label">{{ trans('settings.app_logo') }}</label>
                             <p class="small">{!! trans('settings.app_logo_desc') !!}</p>
                         </div>
-                        <div>
+                        <div class="pt-xs">
                             @include('components.image-picker', [
                                      'removeName' => 'setting-app-logo',
                                      'removeValue' => 'none',
                             <label class="setting-list-label">{{ trans('settings.app_primary_color') }}</label>
                             <p class="small">{!! trans('settings.app_primary_color_desc') !!}</p>
                         </div>
-                        <div setting-app-color-picker class="text-m-right">
+                        <div setting-app-color-picker class="text-m-right pt-xs">
                             <input type="color" data-default="#206ea7" data-current="{{ setting('app-color') }}" value="{{ setting('app-color') }}" name="setting-app-color" id="setting-app-color" placeholder="#206ea7">
                             <input type="hidden" value="{{ setting('app-color-light') }}" name="setting-app-color-light" id="setting-app-color-light">
                             <div class="pr-s">
                             <label for="setting-app-homepage" class="setting-list-label">{{ trans('settings.app_homepage') }}</label>
                             <p class="small">{{ trans('settings.app_homepage_desc') }}</p>
                         </div>
-                        <div>
+                        <div class="pt-xs">
                             <select name="setting-app-homepage-type" id="setting-app-homepage-type">
                                 <option @if(setting('app-homepage-type') === 'default') selected @endif value="default">{{ trans('common.default') }}</option>
                                 <option @if(setting('app-homepage-type') === 'books') selected @endif value="books">{{ trans('entities.books') }}</option>
                             <label for="setting-registration-restrict" class="setting-list-label">{{ trans('settings.reg_confirm_restrict_domain') }}</label>
                             <p class="small">{!! trans('settings.reg_confirm_restrict_domain_desc') !!}</p>
                         </div>
-                        <div>
+                        <div class="pt-xs">
                             <input type="text" id="setting-registration-restrict" name="setting-registration-restrict" placeholder="{{ trans('settings.reg_confirm_restrict_domain_placeholder') }}" value="{{ setting('registration-restrict', '') }}">
                         </div>
                     </div>