From 1f69965c1e61a6ef3d46eb46ef4290889ba34381 Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sat, 28 Jan 2023 11:50:46 +0000
Subject: [PATCH 1/8] Updated settings view to have dark-mode color options

Also added link color option, not yet used.
Cleaned up tabbed interface control design as part of this.
---
 app/Config/setting-defaults.php               |  1 +
 resources/lang/en/settings.php                | 10 ++--
 resources/sass/_blocks.scss                   |  7 +++
 resources/sass/_components.scss               | 31 +++++-----
 .../views/pages/parts/image-manager.blade.php |  4 +-
 .../views/settings/customization.blade.php    | 56 ++++++++-----------
 ...ade.php => setting-color-picker.blade.php} | 19 ++++---
 .../parts/setting-color-scheme.blade.php      | 25 +++++++++
 8 files changed, 92 insertions(+), 61 deletions(-)
 rename resources/views/settings/parts/{setting-entity-color-picker.blade.php => setting-color-picker.blade.php} (57%)
 create mode 100644 resources/views/settings/parts/setting-color-scheme.blade.php

diff --git a/app/Config/setting-defaults.php b/app/Config/setting-defaults.php
index 5e1e4348a..37270cf31 100644
--- a/app/Config/setting-defaults.php
+++ b/app/Config/setting-defaults.php
@@ -16,6 +16,7 @@ return [
     'app-editor'           => 'wysiwyg',
     'app-color'            => '#206ea7',
     'app-color-light'      => 'rgba(32,110,167,0.15)',
+    'link-color'           => '#206ea7',
     'bookshelf-color'      => '#a94747',
     'book-color'           => '#077b70',
     'chapter-color'        => '#af4d0d',
diff --git a/resources/lang/en/settings.php b/resources/lang/en/settings.php
index 023cf1beb..f9abadc0c 100755
--- a/resources/lang/en/settings.php
+++ b/resources/lang/en/settings.php
@@ -36,8 +36,6 @@ return [
     'app_logo_desc' => 'This is used in the application header bar, among other areas. This image should be 86px in height. Large images will be scaled down.',
     'app_icon' => 'Application Icon',
     'app_icon_desc' => 'This icon is used for browser tabs and shortcut icons. This should be a 256px square PNG image.',
-    'app_primary_color' => 'Application Primary Color',
-    'app_primary_color_desc' => 'Sets the primary color for the application including the banner, buttons, and links.',
     'app_homepage' => 'Application Homepage',
     'app_homepage_desc' => 'Select a view to show on the homepage instead of the default view. Page permissions are ignored for selected pages.',
     'app_homepage_select' => 'Select a page',
@@ -51,8 +49,12 @@ return [
     'app_disable_comments_desc' => 'Disables comments across all pages in the application. <br> Existing comments are not shown.',
 
     // Color settings
-    'content_colors' => 'Content Colors',
-    'content_colors_desc' => 'Sets colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
+    'color_scheme' => 'Application Color Scheme',
+    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light mode to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the primary and link colors used in BookStack. The primary color is mainly used for the header banner, buttons and UI decorations, in addition to a few other components.',
+    'app_color' => 'Primary Color',
+    'link_color' => 'Link Color',
+    'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
     'bookshelf_color' => 'Shelf Color',
     'book_color' => 'Book Color',
     'chapter_color' => 'Chapter Color',
diff --git a/resources/sass/_blocks.scss b/resources/sass/_blocks.scss
index 2794dd954..1d9bfc272 100644
--- a/resources/sass/_blocks.scss
+++ b/resources/sass/_blocks.scss
@@ -237,6 +237,13 @@
   }
 }
 
+.sub-card {
+  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
+  border: 1.5px solid;
+  @include lightDark(border-color, #E2E2E2, #444);
+  border-radius: 4px;
+}
+
 .outline-hover {
   border: 1px solid transparent !important;
   &:hover {
diff --git a/resources/sass/_components.scss b/resources/sass/_components.scss
index ab1d506c7..b902220a7 100644
--- a/resources/sass/_components.scss
+++ b/resources/sass/_components.scss
@@ -608,36 +608,39 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
 
 
 .tab-container .nav-tabs {
+  display: flex;
+  align-items: end;
+  justify-items: start;
   text-align: start;
   border-bottom: 1px solid #DDD;
   @include lightDark(border-color, #ddd, #444);
   margin-bottom: $-m;
-  .tab-item {
-    padding: $-s;
-    @include lightDark(color, #666, #999);
-    &.selected {
-      border-bottom-width: 3px;
-    }
-  }
 }
 
 .nav-tabs {
   text-align: center;
-  a, .tab-item {
-    padding: $-m;
+  .tab-item {
     display: inline-block;
-    @include lightDark(color, #666, #999);
+    padding: $-s;
+    @include lightDark(color, rgba(0, 0, 0, .5), rgba(255, 255, 255, .5));
     cursor: pointer;
-    border-right: 1px solid rgba(0, 0, 0, 0.1);
     border-bottom: 2px solid transparent;
+    margin-bottom: -1px;
     &.selected {
-      border-bottom: 2px solid var(--color-primary);
+      color: var(--color-primary) !important;
+      border-bottom-color: var(--color-primary) !important;
     }
-    &:last-child {
-      border-right: 0;
+    &:hover, &:focus {
+      @include lightDark(color, rgba(0, 0, 0, .8), rgba(255, 255, 255, .8));
+      @include lightDark(border-bottom-color,  rgba(0, 0, 0, .2), rgba(255, 255, 255, .2));
     }
   }
 }
+.nav-tabs.controls-card {
+  margin-bottom: 0;
+  border-bottom: 0;
+  padding: 0 $-xs;
+}
 
 .image-picker .none {
   display: none;
diff --git a/resources/views/pages/parts/image-manager.blade.php b/resources/views/pages/parts/image-manager.blade.php
index 50a0cd8c3..a21a5fdac 100644
--- a/resources/views/pages/parts/image-manager.blade.php
+++ b/resources/views/pages/parts/image-manager.blade.php
@@ -21,10 +21,10 @@
                                 type="button" class="tab-item selected" title="{{ trans('components.image_all_title') }}">@icon('images') {{ trans('components.image_all') }}</button>
                         <button refs="image-manager@filterTabs"
                                 data-filter="book"
-                                type="button" class="tab-item" title="{{ trans('components.image_book_title') }}">@icon('book', ['class' => 'text-book svg-icon']) {{ trans('entities.book') }}</button>
+                                type="button" class="tab-item" title="{{ trans('components.image_book_title') }}">@icon('book', ['class' => 'svg-icon']) {{ trans('entities.book') }}</button>
                         <button refs="image-manager@filterTabs"
                                 data-filter="page"
-                                type="button" class="tab-item" title="{{ trans('components.image_page_title') }}">@icon('page', ['class' => 'text-page svg-icon']) {{ trans('entities.page') }}</button>
+                                type="button" class="tab-item" title="{{ trans('components.image_page_title') }}">@icon('page', ['class' => 'svg-icon']) {{ trans('entities.page') }}</button>
                     </div>
                     <div>
                         <form refs="image-manager@searchForm" class="contained-search-box">
diff --git a/resources/views/settings/customization.blade.php b/resources/views/settings/customization.blade.php
index aa37c30c9..d3c20c4b1 100644
--- a/resources/views/settings/customization.blade.php
+++ b/resources/views/settings/customization.blade.php
@@ -69,42 +69,32 @@
                 </div>
             </div>
 
-            <!-- Primary Color -->
-            <div class="grid half gap-xl">
-                <div>
-                    <label class="setting-list-label">{{ trans('settings.app_primary_color') }}</label>
-                    <p class="small">{!! trans('settings.app_primary_color_desc') !!}</p>
-                </div>
-                <div component="setting-app-color-picker setting-color-picker"
-                     option:setting-color-picker:default="#206ea7"
-                     option:setting-color-picker:current="{{ setting('app-color') }}"
-                     class="text-m-right pt-xs">
-                    <input refs="setting-color-picker@input setting-app-color-picker@input" type="color" value="{{ setting('app-color') }}" name="setting-app-color" id="setting-app-color" placeholder="#206ea7">
-                    <input refs="setting-app-color-picker@light-input" type="hidden" value="{{ setting('app-color-light') }}" name="setting-app-color-light" id="setting-app-color-light">
-                    <div class="pr-s">
-                        <button refs="setting-color-picker@default-button" type="button" class="text-button text-muted mt-s">{{ trans('common.default') }}</button>
-                        <span class="sep">|</span>
-                        <button refs="setting-color-picker@reset-button" type="button" class="text-button text-muted mt-s">{{ trans('common.reset') }}</button>
-                    </div>
-
-                </div>
-            </div>
-
-            <!-- Entity Color -->
+            <!-- App Color Scheme -->
             <div class="pb-l">
-                <div>
-                    <label class="setting-list-label">{{ trans('settings.content_colors') }}</label>
-                    <p class="small">{!! trans('settings.content_colors_desc') !!}</p>
+                <div class="mb-l">
+                    <label class="setting-list-label">{{ trans('settings.color_scheme') }}</label>
+                    <p class="small">{{ trans('settings.color_scheme_desc') }}</p>
                 </div>
-                <div class="grid half pt-m">
-                    <div>
-                        @include('settings.parts.setting-entity-color-picker', ['type' => 'bookshelf'])
-                        @include('settings.parts.setting-entity-color-picker', ['type' => 'book'])
-                        @include('settings.parts.setting-entity-color-picker', ['type' => 'chapter'])
+
+                @php
+                    $darkMode = boolval(setting()->getForCurrentUser('dark-mode-enabled'));
+                @endphp
+                <div component="tabs" class="tab-container">
+                    <div class="nav-tabs controls-card">
+                        <button refs="tabs@toggleLight"
+                                type="button"
+                                class="{{ $darkMode ? '' : 'selected' }} tab-item">@icon('light-mode'){{ trans('common.light_mode') }}</button>
+                        <button refs="tabs@toggleDark"
+                                type="button"
+                                class="{{ $darkMode ? 'selected' : '' }} tab-item">@icon('dark-mode'){{ trans('common.dark_mode') }}</button>
                     </div>
-                    <div>
-                        @include('settings.parts.setting-entity-color-picker', ['type' => 'page'])
-                        @include('settings.parts.setting-entity-color-picker', ['type' => 'page-draft'])
+                    <div class="sub-card">
+                        <div refs="tabs@contentLight attachments@list" class="{{ $darkMode ? 'hidden' : '' }} p-m">
+                            @include('settings.parts.setting-color-scheme', ['mode' => 'light'])
+                        </div>
+                        <div refs="tabs@contentDark" class="{{ $darkMode ? '' : 'hidden' }} p-m">
+                            @include('settings.parts.setting-color-scheme', ['mode' => 'dark'])
+                        </div>
                     </div>
                 </div>
             </div>
diff --git a/resources/views/settings/parts/setting-entity-color-picker.blade.php b/resources/views/settings/parts/setting-color-picker.blade.php
similarity index 57%
rename from resources/views/settings/parts/setting-entity-color-picker.blade.php
rename to resources/views/settings/parts/setting-color-picker.blade.php
index e7bfc3fe9..d6707fb50 100644
--- a/resources/views/settings/parts/setting-entity-color-picker.blade.php
+++ b/resources/views/settings/parts/setting-color-picker.blade.php
@@ -1,12 +1,15 @@
 {{--
-    @type - Name of entity type
+    @type - Name of color setting
 --}}
+@php
+    $keyAppends = ($mode === 'light' ? '' : '-' . $mode);
+@endphp
 <div component="setting-color-picker"
-     option:setting-color-picker:default="{{ config('setting-defaults.'. $type .'-color') }}"
-     option:setting-color-picker:current="{{ setting($type .'-color') }}"
+     option:setting-color-picker:default="{{ config('setting-defaults.'. $type .'-color' . $keyAppends) }}"
+     option:setting-color-picker:current="{{ setting($type .'-color' . $keyAppends) }}"
      class="grid no-break half mb-l">
     <div>
-        <label for="setting-{{ $type }}-color" class="text-dark">{{ trans('settings.'. str_replace('-', '_', $type) .'_color') }}</label>
+        <label for="setting-{{ $type }}-color{{ $keyAppends }}" class="text-dark">{{ trans('settings.'. str_replace('-', '_', $type) .'_color') }}</label>
         <button refs="setting-color-picker@default-button" type="button" class="text-button text-muted">{{ trans('common.default') }}</button>
         <span class="sep">|</span>
         <button refs="setting-color-picker@reset-button" type="button" class="text-button text-muted">{{ trans('common.reset') }}</button>
@@ -14,10 +17,10 @@
     <div>
         <input type="color"
                refs="setting-color-picker@input"
-               value="{{ setting($type .'-color') }}"
-               name="setting-{{ $type }}-color"
-               id="setting-{{ $type }}-color"
-               placeholder="{{ config('setting-defaults.'. $type .'-color') }}"
+               value="{{ setting($type . '-color' . $keyAppends) }}"
+               name="setting-{{ $type }}-color{{ $keyAppends }}"
+               id="setting-{{ $type }}-color{{ $keyAppends }}"
+               placeholder="{{ config('setting-defaults.'. $type .'-color' . $keyAppends) }}"
                class="small">
     </div>
 </div>
diff --git a/resources/views/settings/parts/setting-color-scheme.blade.php b/resources/views/settings/parts/setting-color-scheme.blade.php
new file mode 100644
index 000000000..1b18a9a6a
--- /dev/null
+++ b/resources/views/settings/parts/setting-color-scheme.blade.php
@@ -0,0 +1,25 @@
+{{--
+    @mode - 'light' or 'dark'.
+--}}
+<p class="small">{{ trans('settings.ui_colors_desc') }}</p>
+<div class="grid half pt-m">
+    <div>
+        @include('settings.parts.setting-color-picker', ['type' => 'app', 'mode' => $mode])
+    </div>
+    <div>
+        @include('settings.parts.setting-color-picker', ['type' => 'link', 'mode' => $mode])
+    </div>
+</div>
+<hr>
+<p class="small">{!! trans('settings.content_colors_desc') !!}</p>
+<div class="grid half pt-m">
+    <div>
+        @include('settings.parts.setting-color-picker', ['type' => 'bookshelf', 'mode' => $mode])
+        @include('settings.parts.setting-color-picker', ['type' => 'book', 'mode' => $mode])
+        @include('settings.parts.setting-color-picker', ['type' => 'chapter', 'mode' => $mode])
+    </div>
+    <div>
+        @include('settings.parts.setting-color-picker', ['type' => 'page', 'mode' => $mode])
+        @include('settings.parts.setting-color-picker', ['type' => 'page-draft', 'mode' => $mode])
+    </div>
+</div>
\ No newline at end of file

From e708ce93baa19715b0951c86c111320d4af2dc82 Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sat, 28 Jan 2023 12:50:51 +0000
Subject: [PATCH 2/8] Updated generic tab styles and js to force accessible
 usage

Added use of more accessible tags to create tabbed-interfaces then
updated css and JS to require use of those attributes rather than custom
techniques.

Updated relevant parts of app.
Some custom parts using their own tabs though, something to improve in
future.
---
 resources/js/components/attachments.js        |  2 +-
 resources/js/components/image-manager.js      |  7 +--
 resources/js/components/tabs.js               | 62 +++++++++----------
 resources/sass/_components.scss               | 36 +++++------
 resources/views/attachments/manager.blade.php | 45 +++++++++++---
 .../views/pages/parts/image-manager.blade.php | 10 ++-
 .../views/settings/customization.blade.php    | 32 +++++++---
 7 files changed, 119 insertions(+), 75 deletions(-)

diff --git a/resources/js/components/attachments.js b/resources/js/components/attachments.js
index b4e400aeb..d8a506270 100644
--- a/resources/js/components/attachments.js
+++ b/resources/js/components/attachments.js
@@ -45,7 +45,7 @@ export class Attachments extends Component {
         this.stopEdit();
         /** @var {Tabs} */
         const tabs = window.$components.firstOnElement(this.mainTabs, 'tabs');
-        tabs.show('items');
+        tabs.show('attachment-panel-items');
         window.$http.get(`/attachments/get/page/${this.pageId}`).then(resp => {
             this.list.innerHTML = resp.data;
             window.$components.init(this.list);
diff --git a/resources/js/components/image-manager.js b/resources/js/components/image-manager.js
index a44fffc1b..418b7c98a 100644
--- a/resources/js/components/image-manager.js
+++ b/resources/js/components/image-manager.js
@@ -140,10 +140,9 @@ export class ImageManager extends Component {
     }
 
     setActiveFilterTab(filterName) {
-        this.filterTabs.forEach(t => t.classList.remove('selected'));
-        const activeTab = this.filterTabs.find(t => t.dataset.filter === filterName);
-        if (activeTab) {
-            activeTab.classList.add('selected');
+        for (const tab of this.filterTabs) {
+            const selected = tab.dataset.filter === filterName;
+            tab.setAttribute('aria-selected', selected ? 'true' : 'false');
         }
     }
 
diff --git a/resources/js/components/tabs.js b/resources/js/components/tabs.js
index 46063d240..ebab4191c 100644
--- a/resources/js/components/tabs.js
+++ b/resources/js/components/tabs.js
@@ -1,48 +1,46 @@
-import {onSelect} from "../services/dom";
 import {Component} from "./component";
 
 /**
  * Tabs
- * Works by matching 'tabToggle<Key>' with 'tabContent<Key>' sections.
+ * Uses accessible attributes to drive its functionality.
+ * On tab wrapping element:
+ * - role=tablist
+ * On tabs (Should be a button):
+ * - id
+ * - role=tab
+ * - aria-selected=true/false
+ * - aria-controls=<id-of-panel-section>
+ * On panels:
+ * - id
+ * - tabindex=0
+ * - role=tabpanel
+ * - aria-labelledby=<id-of-tab-for-panel>
+ * - hidden (If not shown by default).
  */
 export class Tabs extends Component {
 
     setup() {
-        this.tabContentsByName = {};
-        this.tabButtonsByName = {};
-        this.allContents = [];
-        this.allButtons = [];
+        this.container = this.$el;
+        this.tabs = Array.from(this.container.querySelectorAll('[role="tab"]'));
+        this.panels = Array.from(this.container.querySelectorAll('[role="tabpanel"]'));
 
-        for (const [key, elems] of Object.entries(this.$manyRefs || {})) {
-            if (key.startsWith('toggle')) {
-                const cleanKey = key.replace('toggle', '').toLowerCase();
-                onSelect(elems, e => this.show(cleanKey));
-                this.allButtons.push(...elems);
-                this.tabButtonsByName[cleanKey] = elems;
+        this.container.addEventListener('click', event => {
+            const button = event.target.closest('[role="tab"]');
+            if (button) {
+                this.show(button.getAttribute('aria-controls'));
             }
-            if (key.startsWith('content')) {
-                const cleanKey = key.replace('content', '').toLowerCase();
-                this.tabContentsByName[cleanKey] = elems;
-                this.allContents.push(...elems);
-            }
-        }
+        });
     }
 
-    show(key) {
-        this.allContents.forEach(c => {
-            c.classList.add('hidden');
-            c.classList.remove('selected');
-        });
-        this.allButtons.forEach(b => b.classList.remove('selected'));
+    show(sectionId) {
+        for (const panel of this.panels) {
+            panel.toggleAttribute('hidden', panel.id !== sectionId);
+        }
 
-        const contents = this.tabContentsByName[key] || [];
-        const buttons = this.tabButtonsByName[key] || [];
-        if (contents.length > 0) {
-            contents.forEach(c => {
-                c.classList.remove('hidden')
-                c.classList.add('selected')
-            });
-            buttons.forEach(b => b.classList.add('selected'));
+        for (const tab of this.tabs) {
+            const tabSection = tab.getAttribute('aria-controls');
+            const selected = tabSection === sectionId;
+            tab.setAttribute('aria-selected', selected ? 'true' : 'false');
         }
     }
 
diff --git a/resources/sass/_components.scss b/resources/sass/_components.scss
index b902220a7..c8ecd438d 100644
--- a/resources/sass/_components.scss
+++ b/resources/sass/_components.scss
@@ -607,7 +607,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
 }
 
 
-.tab-container .nav-tabs {
+.tab-container [role="tablist"] {
   display: flex;
   align-items: end;
   justify-items: start;
@@ -617,26 +617,24 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   margin-bottom: $-m;
 }
 
-.nav-tabs {
-  text-align: center;
-  .tab-item {
-    display: inline-block;
-    padding: $-s;
-    @include lightDark(color, rgba(0, 0, 0, .5), rgba(255, 255, 255, .5));
-    cursor: pointer;
-    border-bottom: 2px solid transparent;
-    margin-bottom: -1px;
-    &.selected {
-      color: var(--color-primary) !important;
-      border-bottom-color: var(--color-primary) !important;
-    }
-    &:hover, &:focus {
-      @include lightDark(color, rgba(0, 0, 0, .8), rgba(255, 255, 255, .8));
-      @include lightDark(border-bottom-color,  rgba(0, 0, 0, .2), rgba(255, 255, 255, .2));
-    }
+.tab-container [role="tablist"] button[role="tab"],
+.image-manager [role="tablist"] button[role="tab"] {
+  display: inline-block;
+  padding: $-s;
+  @include lightDark(color, rgba(0, 0, 0, .5), rgba(255, 255, 255, .5));
+  cursor: pointer;
+  border-bottom: 2px solid transparent;
+  margin-bottom: -1px;
+  &[aria-selected="true"] {
+    color: var(--color-primary) !important;
+    border-bottom-color: var(--color-primary) !important;
+  }
+  &:hover, &:focus {
+    @include lightDark(color, rgba(0, 0, 0, .8), rgba(255, 255, 255, .8));
+    @include lightDark(border-bottom-color,  rgba(0, 0, 0, .2), rgba(255, 255, 255, .2));
   }
 }
-.nav-tabs.controls-card {
+.tab-container [role="tablist"].controls-card {
   margin-bottom: 0;
   border-bottom: 0;
   padding: 0 $-xs;
diff --git a/resources/views/attachments/manager.blade.php b/resources/views/attachments/manager.blade.php
index 724ca9c8e..7d14d00e7 100644
--- a/resources/views/attachments/manager.blade.php
+++ b/resources/views/attachments/manager.blade.php
@@ -9,25 +9,54 @@
     <div class="px-l files">
 
         <div refs="attachments@listContainer">
-            <p class="text-muted small">{{ trans('entities.attachments_explain') }} <span class="text-warn">{{ trans('entities.attachments_explain_instant_save') }}</span></p>
+            <p class="text-muted small">{{ trans('entities.attachments_explain') }} <span
+                        class="text-warn">{{ trans('entities.attachments_explain_instant_save') }}</span></p>
 
             <div component="tabs" refs="attachments@mainTabs" class="tab-container">
-                <div class="nav-tabs">
-                    <button refs="tabs@toggleItems" type="button" class="selected tab-item">{{ trans('entities.attachments_items') }}</button>
-                    <button refs="tabs@toggleUpload" type="button" class="tab-item">{{ trans('entities.attachments_upload') }}</button>
-                    <button refs="tabs@toggleLinks" type="button" class="tab-item">{{ trans('entities.attachments_link') }}</button>
+                <div role="tablist">
+                    <button id="attachment-tab-items"
+                            role="tab"
+                            aria-selected="true"
+                            aria-controls="attachment-panel-items"
+                            type="button"
+                            class="tab-item">{{ trans('entities.attachments_items') }}</button>
+                    <button id="attachment-tab-upload"
+                            role="tab"
+                            aria-selected="false"
+                            aria-controls="attachment-panel-upload"
+                            type="button"
+                            class="tab-item">{{ trans('entities.attachments_upload') }}</button>
+                    <button id="attachment-tab-links"
+                            role="tab"
+                            aria-selected="false"
+                            aria-controls="attachment-panel-links"
+                            type="button"
+                            class="tab-item">{{ trans('entities.attachments_link') }}</button>
                 </div>
-                <div refs="tabs@contentItems attachments@list">
+                <div id="attachment-panel-items"
+                     tabindex="0"
+                     role="tabpanel"
+                     aria-labelledby="attachment-tab-items"
+                     refs="attachments@list">
                     @include('attachments.manager-list', ['attachments' => $page->attachments->all()])
                 </div>
-                <div refs="tabs@contentUpload" class="hidden">
+                <div id="attachment-panel-upload"
+                     tabindex="0"
+                     role="tabpanel"
+                     hidden
+                     aria-labelledby="attachment-tab-upload">
                     @include('form.dropzone', [
                         'placeholder' => trans('entities.attachments_dropzone'),
                         'url' =>  url('/attachments/upload?uploaded_to=' . $page->id),
                         'successMessage' => trans('entities.attachments_file_uploaded'),
                     ])
                 </div>
-                <div refs="tabs@contentLinks" class="hidden link-form-container">
+                <div id="attachment-panel-links"
+                     tabindex="0"
+                     role="tabpanel"
+                     hidden
+                     aria-labelledby="attachment-tab-links"
+                     class="link-form-container">
                     @include('attachments.manager-link-form', ['pageId' => $page->id])
                 </div>
             </div>
diff --git a/resources/views/pages/parts/image-manager.blade.php b/resources/views/pages/parts/image-manager.blade.php
index a21a5fdac..5832c0954 100644
--- a/resources/views/pages/parts/image-manager.blade.php
+++ b/resources/views/pages/parts/image-manager.blade.php
@@ -15,15 +15,21 @@
             <div class="flex-fill image-manager-body">
 
                 <div class="image-manager-content">
-                    <div class="image-manager-header primary-background-light nav-tabs grid third no-gap">
+                    <div role="tablist" class="image-manager-header primary-background-light grid third no-gap">
                         <button refs="image-manager@filterTabs"
                                 data-filter="all"
-                                type="button" class="tab-item selected" title="{{ trans('components.image_all_title') }}">@icon('images') {{ trans('components.image_all') }}</button>
+                                role="tab"
+                                aria-selected="true"
+                                type="button" class="tab-item" title="{{ trans('components.image_all_title') }}">@icon('images') {{ trans('components.image_all') }}</button>
                         <button refs="image-manager@filterTabs"
                                 data-filter="book"
+                                role="tab"
+                                aria-selected="false"
                                 type="button" class="tab-item" title="{{ trans('components.image_book_title') }}">@icon('book', ['class' => 'svg-icon']) {{ trans('entities.book') }}</button>
                         <button refs="image-manager@filterTabs"
                                 data-filter="page"
+                                role="tab"
+                                aria-selected="false"
                                 type="button" class="tab-item" title="{{ trans('components.image_page_title') }}">@icon('page', ['class' => 'svg-icon']) {{ trans('entities.page') }}</button>
                     </div>
                     <div>
diff --git a/resources/views/settings/customization.blade.php b/resources/views/settings/customization.blade.php
index d3c20c4b1..ac1f678a5 100644
--- a/resources/views/settings/customization.blade.php
+++ b/resources/views/settings/customization.blade.php
@@ -80,19 +80,33 @@
                     $darkMode = boolval(setting()->getForCurrentUser('dark-mode-enabled'));
                 @endphp
                 <div component="tabs" class="tab-container">
-                    <div class="nav-tabs controls-card">
-                        <button refs="tabs@toggleLight"
-                                type="button"
-                                class="{{ $darkMode ? '' : 'selected' }} tab-item">@icon('light-mode'){{ trans('common.light_mode') }}</button>
-                        <button refs="tabs@toggleDark"
-                                type="button"
-                                class="{{ $darkMode ? 'selected' : '' }} tab-item">@icon('dark-mode'){{ trans('common.dark_mode') }}</button>
+                    <div role="tablist" class="controls-card">
+                        <button type="button"
+                                role="tab"
+                                id="color-scheme-tab-light"
+                                aria-selected="{{ $darkMode ? 'false' : 'true' }}"
+                                aria-controls="color-scheme-panel-light">@icon('light-mode'){{ trans('common.light_mode') }}</button>
+                        <button type="button"
+                                role="tab"
+                                id="color-scheme-tab-dark"
+                                aria-selected="{{ $darkMode ? 'true' : 'false' }}"
+                                aria-controls="color-scheme-panel-dark">@icon('dark-mode'){{ trans('common.dark_mode') }}</button>
                     </div>
                     <div class="sub-card">
-                        <div refs="tabs@contentLight attachments@list" class="{{ $darkMode ? 'hidden' : '' }} p-m">
+                        <div id="color-scheme-panel-light"
+                             tabindex="0"
+                             role="tabpanel"
+                             aria-labelledby="color-scheme-tab-light"
+                             @if($darkMode) hidden @endif
+                             class="p-m">
                             @include('settings.parts.setting-color-scheme', ['mode' => 'light'])
                         </div>
-                        <div refs="tabs@contentDark" class="{{ $darkMode ? '' : 'hidden' }} p-m">
+                        <div id="color-scheme-panel-dark"
+                             tabindex="0"
+                             role="tabpanel"
+                             aria-labelledby="color-scheme-tab-light"
+                             @if(!$darkMode) hidden @endif
+                             class="p-m">
                             @include('settings.parts.setting-color-scheme', ['mode' => 'dark'])
                         </div>
                     </div>

From 65ebdb72345b5afc7d98d750a599efc1943cdaea Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sat, 28 Jan 2023 15:20:08 +0000
Subject: [PATCH 3/8] Added usage and defaults for dark colors

---
 app/Config/setting-defaults.php               |  8 ++++++
 resources/sass/_header.scss                   |  3 ---
 resources/sass/_layout.scss                   |  3 +--
 .../views/common/custom-styles.blade.php      | 26 +++++++++++++------
 4 files changed, 27 insertions(+), 13 deletions(-)

diff --git a/app/Config/setting-defaults.php b/app/Config/setting-defaults.php
index 37270cf31..005fddfd0 100644
--- a/app/Config/setting-defaults.php
+++ b/app/Config/setting-defaults.php
@@ -22,6 +22,14 @@ return [
     'chapter-color'        => '#af4d0d',
     'page-color'           => '#206ea7',
     'page-draft-color'     => '#7e50b1',
+    'app-color-dark'       => '#195785',
+    'app-color-light-dark' => 'rgba(32,110,167,0.15)',
+    'link-color-dark'      => '#195785',
+    'bookshelf-color-dark' => '#ff5454',
+    'book-color-dark'      => '#389f60',
+    'chapter-color-dark'   => '#ee7a2d',
+    'page-color-dark'      => '#429fe3',
+    'page-draft-color-dark' => '#a66ce8',
     'app-custom-head'      => false,
     'registration-enabled' => false,
 
diff --git a/resources/sass/_header.scss b/resources/sass/_header.scss
index aa560e8e0..c1b6af4c6 100644
--- a/resources/sass/_header.scss
+++ b/resources/sass/_header.scss
@@ -22,9 +22,6 @@ header {
   border-bottom: 1px solid #DDD;
   box-shadow: $bs-card;
   @include lightDark(border-bottom-color, #DDD, #000);
-  @include whenDark {
-    filter: saturate(0.8) brightness(0.8);
-  }
   .header-links {
     display: flex;
     align-items: center;
diff --git a/resources/sass/_layout.scss b/resources/sass/_layout.scss
index 3fc419046..19333faf7 100644
--- a/resources/sass/_layout.scss
+++ b/resources/sass/_layout.scss
@@ -433,7 +433,7 @@ body.flexbox {
     display: none;
   }
   .tri-layout-left-contents > *, .tri-layout-right-contents > * {
-    @include lightDark(opacity, 0.6, 0.7);
+    @include lightDark(opacity, 0.6, 0.75);
     transition: opacity ease-in-out 120ms;
     &:hover, &:focus-within {
       opacity: 1 !important;
@@ -442,7 +442,6 @@ body.flexbox {
       opacity: 1 !important;
     }
   }
-
 }
 
 @include smaller-than($m) {
diff --git a/resources/views/common/custom-styles.blade.php b/resources/views/common/custom-styles.blade.php
index f81939109..d261110c7 100644
--- a/resources/views/common/custom-styles.blade.php
+++ b/resources/views/common/custom-styles.blade.php
@@ -1,11 +1,21 @@
-<style id="custom-styles" data-color="{{ setting('app-color') }}" data-color-light="{{ setting('app-color-light') }}">
+<style id="custom-styles"
+       data-color="{{ setting('app-color') }}"
+       data-color-dark="{{ setting('app-color-dark') }}"
+       data-color-light="{{ setting('app-color-light') }}"
+       data-color-light-dark="{{ setting('app-color-light-dark') }}">
+
+    @php
+        $settingSuffix = setting()->getForCurrentUser('dark-mode-enabled') ? '-dark' : '';
+    @endphp
+
     :root {
-        --color-primary: {{ setting('app-color') }};
-        --color-primary-light: {{ setting('app-color-light') }};
-        --color-bookshelf: {{ setting('bookshelf-color')}};
-        --color-book: {{ setting('book-color')}};
-        --color-chapter: {{ setting('chapter-color')}};
-        --color-page: {{ setting('page-color')}};
-        --color-page-draft: {{ setting('page-draft-color')}};
+        --color-primary: {{ setting('app-color' . $settingSuffix) }};
+        --color-primary-light: {{ setting('app-color-light' . $settingSuffix) }};
+        --color-link: {{ setting('link-color' . $settingSuffix) }};
+        --color-bookshelf: {{ setting('bookshelf-color' . $settingSuffix)}};
+        --color-book: {{ setting('book-color' . $settingSuffix)}};
+        --color-chapter: {{ setting('chapter-color' . $settingSuffix)}};
+        --color-page: {{ setting('page-color' . $settingSuffix)}};
+        --color-page-draft: {{ setting('page-draft-color' . $settingSuffix)}};
     }
 </style>

From c3374393703b55d5c9efa54870c760b8899ceeeb Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sat, 28 Jan 2023 16:06:11 +0000
Subject: [PATCH 4/8] Rolled out use of seperate link color style

---
 resources/js/components/setting-app-color-picker.js    |  1 +
 resources/js/components/tabs.js                        |  2 ++
 resources/lang/en/settings.php                         |  2 +-
 resources/sass/_buttons.scss                           | 10 +++-------
 resources/sass/_colors.scss                            | 10 +++++++++-
 resources/sass/_components.scss                        |  4 ++--
 resources/sass/_forms.scss                             |  1 -
 resources/sass/_text.scss                              |  4 ++--
 resources/sass/_variables.scss                         |  1 +
 resources/sass/styles.scss                             |  2 +-
 resources/views/attachments/manager-list.blade.php     |  6 +++---
 resources/views/books/index.blade.php                  |  2 +-
 resources/views/books/parts/convert-to-shelf.blade.php |  2 +-
 resources/views/books/parts/form.blade.php             |  4 ++--
 resources/views/books/show.blade.php                   |  2 +-
 resources/views/chapters/copy.blade.php                |  2 +-
 .../views/chapters/parts/convert-to-book.blade.php     |  2 +-
 resources/views/chapters/parts/form.blade.php          |  2 +-
 resources/views/chapters/show.blade.php                |  2 +-
 resources/views/entities/favourite-action.blade.php    |  2 +-
 resources/views/entities/view-toggle.blade.php         |  4 ++--
 resources/views/form/entity-permissions-row.blade.php  |  2 +-
 resources/views/home/books.blade.php                   |  6 +++---
 resources/views/home/default.blade.php                 |  4 ++--
 resources/views/home/shelves.blade.php                 |  6 +++---
 resources/views/home/specific-page.blade.php           |  6 +++---
 resources/views/layouts/tri.blade.php                  |  4 ++--
 resources/views/mfa/parts/setup-method-row.blade.php   |  2 +-
 resources/views/pages/copy.blade.php                   |  2 +-
 resources/views/pages/parts/editor-toolbar.blade.php   |  8 ++++----
 resources/views/pages/parts/form.blade.php             |  2 +-
 .../views/pages/parts/image-manager-list.blade.php     |  2 +-
 .../views/pages/parts/revisions-index-row.blade.php    |  2 +-
 resources/views/pages/show.blade.php                   |  4 ++--
 resources/views/settings/recycle-bin/index.blade.php   |  2 +-
 .../roles/parts/asset-permissions-row.blade.php        |  2 +-
 resources/views/settings/roles/parts/form.blade.php    |  4 ++--
 .../parts/related-asset-permissions-row.blade.php      |  2 +-
 resources/views/shelves/index.blade.php                |  2 +-
 resources/views/shelves/parts/form.blade.php           |  4 ++--
 resources/views/shelves/show.blade.php                 |  2 +-
 41 files changed, 71 insertions(+), 64 deletions(-)

diff --git a/resources/js/components/setting-app-color-picker.js b/resources/js/components/setting-app-color-picker.js
index 68e5abce5..f167e5fc9 100644
--- a/resources/js/components/setting-app-color-picker.js
+++ b/resources/js/components/setting-app-color-picker.js
@@ -3,6 +3,7 @@ import {Component} from "./component";
 export class SettingAppColorPicker extends Component {
 
     setup() {
+        // TODO
         this.colorInput = this.$refs.input;
         this.lightColorInput = this.$refs.lightInput;
 
diff --git a/resources/js/components/tabs.js b/resources/js/components/tabs.js
index ebab4191c..6d48048b5 100644
--- a/resources/js/components/tabs.js
+++ b/resources/js/components/tabs.js
@@ -42,6 +42,8 @@ export class Tabs extends Component {
             const selected = tabSection === sectionId;
             tab.setAttribute('aria-selected', selected ? 'true' : 'false');
         }
+
+        this.$emit('change', {section: sectionId});
     }
 
 }
\ No newline at end of file
diff --git a/resources/lang/en/settings.php b/resources/lang/en/settings.php
index f9abadc0c..90ad311a7 100755
--- a/resources/lang/en/settings.php
+++ b/resources/lang/en/settings.php
@@ -53,7 +53,7 @@ return [
     'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light mode to best fit the theme and ensure legibility.',
     'ui_colors_desc' => 'Set the primary and link colors used in BookStack. The primary color is mainly used for the header banner, buttons and UI decorations, in addition to a few other components.',
     'app_color' => 'Primary Color',
-    'link_color' => 'Link Color',
+    'link_color' => 'Default Link Color',
     'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
     'bookshelf_color' => 'Shelf Color',
     'book_color' => 'Book Color',
diff --git a/resources/sass/_buttons.scss b/resources/sass/_buttons.scss
index fb3af06e8..3c6775ad5 100644
--- a/resources/sass/_buttons.scss
+++ b/resources/sass/_buttons.scss
@@ -25,7 +25,6 @@ button {
   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;
@@ -85,10 +84,7 @@ button {
   user-select: none;
   font-size: 0.75rem;
   line-height: 1.4em;
-  color: var(--color-primary);
-  @include whenDark {
-    color: #AAA;
-  }
+  color: var(--color-link);
   &:active {
     outline: 0;
   }
@@ -96,8 +92,8 @@ button {
     text-decoration: none;
   }
   &:hover, &:focus {
-    color: var(--color-primary);
-    fill: var(--color-primary);
+    color: var(--color-link);
+    fill: var(--color-link);
   }
 }
 .text-button.hover-underline:hover {
diff --git a/resources/sass/_colors.scss b/resources/sass/_colors.scss
index c51f01659..aff9ff6d0 100644
--- a/resources/sass/_colors.scss
+++ b/resources/sass/_colors.scss
@@ -9,11 +9,14 @@
   background-color: var(--color-primary-light);
   @include whenDark {
     background: #000;
-    .text-primary {
+    .text-link {
       color: #AAA !important;
     }
   }
 }
+.link-background {
+  background-color: var(--color-link) !important;
+}
 
 /*
  * Status text colors
@@ -41,6 +44,11 @@
   fill: var(--color-primary) !important;
 }
 
+.text-link, .text-link:hover, .text-link-hover:hover  {
+  color: var(--color-link) !important;
+  fill: var(--color-link) !important;
+}
+
 .text-muted {
   @include lightDark(color, #575757, #888888, true);
   fill: currentColor !important;
diff --git a/resources/sass/_components.scss b/resources/sass/_components.scss
index c8ecd438d..2150f6d07 100644
--- a/resources/sass/_components.scss
+++ b/resources/sass/_components.scss
@@ -626,8 +626,8 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   border-bottom: 2px solid transparent;
   margin-bottom: -1px;
   &[aria-selected="true"] {
-    color: var(--color-primary) !important;
-    border-bottom-color: var(--color-primary) !important;
+    color: var(--color-link) !important;
+    border-bottom-color: var(--color-link) !important;
   }
   &:hover, &:focus {
     @include lightDark(color, rgba(0, 0, 0, .8), rgba(255, 255, 255, .8));
diff --git a/resources/sass/_forms.scss b/resources/sass/_forms.scss
index ef14f6221..b7fc52f7d 100644
--- a/resources/sass/_forms.scss
+++ b/resources/sass/_forms.scss
@@ -258,7 +258,6 @@ input[type=color] {
     border-radius: 2px;
     display: inline-block;
     border: 2px solid currentColor;
-    opacity: 0.6;
     overflow: hidden;
     fill: currentColor;
     .svg-icon {
diff --git a/resources/sass/_text.scss b/resources/sass/_text.scss
index 6c68bd12b..edf8ce614 100644
--- a/resources/sass/_text.scss
+++ b/resources/sass/_text.scss
@@ -90,7 +90,7 @@ h2.list-heading {
  * Link styling
  */
 a {
-  color: var(--color-primary);
+  color: var(--color-link);
   fill: currentColor;
   cursor: pointer;
   text-decoration: none;
@@ -107,7 +107,7 @@ a {
     display: inline-block;
   }
   &:focus img:only-child {
-    outline: 2px dashed var(--color-primary);
+    outline: 2px dashed var(--color-link);
     outline-offset: 2px;
   }
 }
diff --git a/resources/sass/_variables.scss b/resources/sass/_variables.scss
index e1242bdda..aac9223f9 100644
--- a/resources/sass/_variables.scss
+++ b/resources/sass/_variables.scss
@@ -39,6 +39,7 @@ $fs-s: 12px;
 :root {
   --color-primary: #206ea7;
   --color-primary-light: rgba(32,110,167,0.15);
+  --color-link: #206ea7;
 
   --color-page: #206ea7;
   --color-page-draft: #7e50b1;
diff --git a/resources/sass/styles.scss b/resources/sass/styles.scss
index e50a2f96a..668cb5c85 100644
--- a/resources/sass/styles.scss
+++ b/resources/sass/styles.scss
@@ -113,7 +113,7 @@ $loadingSize: 10px;
   &:focus {
     top: $-xl;
     outline-offset: -10px;
-    outline: 2px dotted var(--color-primary);
+    outline: 2px dotted var(--color-link);
   }
 }
 
diff --git a/resources/views/attachments/manager-list.blade.php b/resources/views/attachments/manager-list.blade.php
index ebb1c24aa..f1dfe2b82 100644
--- a/resources/views/attachments/manager-list.blade.php
+++ b/resources/views/attachments/manager-list.blade.php
@@ -14,13 +14,13 @@
                         option:event-emit-select:name="insert"
                         type="button"
                         title="{{ trans('entities.attachments_insert_link') }}"
-                        class="drag-card-action text-center text-primary">@icon('link')                 </button>
+                        class="drag-card-action text-center text-link">@icon('link')                 </button>
                 <button component="event-emit-select"
                         option:event-emit-select:name="edit"
                         option:event-emit-select:id="{{ $attachment->id }}"
                         type="button"
                         title="{{ trans('common.edit') }}"
-                        class="drag-card-action text-center text-primary">@icon('edit')</button>
+                        class="drag-card-action text-center text-link">@icon('edit')</button>
                 <div component="dropdown" class="flex-fill relative">
                     <button refs="dropdown@toggle"
                             type="button"
@@ -28,7 +28,7 @@
                             class="drag-card-action text-center text-neg">@icon('close')</button>
                     <div refs="dropdown@menu" class="dropdown-menu">
                         <p class="text-neg small px-m mb-xs">{{ trans('entities.attachments_delete') }}</p>
-                        <button refs="ajax-delete-row@delete" type="button" class="text-primary small delete text-item">{{ trans('common.confirm') }}</button>
+                        <button refs="ajax-delete-row@delete" type="button" class="text-link small delete text-item">{{ trans('common.confirm') }}</button>
                     </div>
                 </div>
             </div>
diff --git a/resources/views/books/index.blade.php b/resources/views/books/index.blade.php
index dc51a3a80..bf9795b3a 100644
--- a/resources/views/books/index.blade.php
+++ b/resources/views/books/index.blade.php
@@ -35,7 +35,7 @@
 
     <div class="actions mb-xl">
         <h5>{{ trans('common.actions') }}</h5>
-        <div class="icon-list text-primary">
+        <div class="icon-list text-link">
             @if(user()->can('book-create-all'))
                 <a href="{{ url("/create-book") }}" data-shortcut="new" class="icon-list-item">
                     <span>@icon('add')</span>
diff --git a/resources/views/books/parts/convert-to-shelf.blade.php b/resources/views/books/parts/convert-to-shelf.blade.php
index dde60aac0..2e52b310b 100644
--- a/resources/views/books/parts/convert-to-shelf.blade.php
+++ b/resources/views/books/parts/convert-to-shelf.blade.php
@@ -17,7 +17,7 @@
                 <li>
                     <form action="{{ $book->getUrl('/convert-to-shelf') }}" method="POST">
                         {!! csrf_field() !!}
-                        <button type="submit" class="text-primary text-item">{{ trans('common.confirm') }}</button>
+                        <button type="submit" class="text-link text-item">{{ trans('common.confirm') }}</button>
                     </form>
                 </li>
             </ul>
diff --git a/resources/views/books/parts/form.blade.php b/resources/views/books/parts/form.blade.php
index e893bcead..56d385c9e 100644
--- a/resources/views/books/parts/form.blade.php
+++ b/resources/views/books/parts/form.blade.php
@@ -11,7 +11,7 @@
 </div>
 
 <div class="form-group collapsible" component="collapsible" id="logo-control">
-    <button refs="collapsible@trigger" type="button" class="collapse-title text-primary" aria-expanded="false">
+    <button refs="collapsible@trigger" type="button" class="collapse-title text-link" aria-expanded="false">
         <label>{{ trans('common.cover_image') }}</label>
     </button>
     <div refs="collapsible@content" class="collapse-content">
@@ -27,7 +27,7 @@
 </div>
 
 <div class="form-group collapsible" component="collapsible" id="tags-control">
-    <button refs="collapsible@trigger" type="button" class="collapse-title text-primary" aria-expanded="false">
+    <button refs="collapsible@trigger" type="button" class="collapse-title text-link" aria-expanded="false">
         <label for="tag-manager">{{ trans('entities.book_tags') }}</label>
     </button>
     <div refs="collapsible@content" class="collapse-content">
diff --git a/resources/views/books/show.blade.php b/resources/views/books/show.blade.php
index 884082456..8bb41c18b 100644
--- a/resources/views/books/show.blade.php
+++ b/resources/views/books/show.blade.php
@@ -91,7 +91,7 @@
 
     <div class="actions mb-xl">
         <h5>{{ trans('common.actions') }}</h5>
-        <div class="icon-list text-primary">
+        <div class="icon-list text-link">
 
             @if(userCan('page-create', $book))
                 <a href="{{ $book->getUrl('/create-page') }}" data-shortcut="new" class="icon-list-item">
diff --git a/resources/views/chapters/copy.blade.php b/resources/views/chapters/copy.blade.php
index 3fd5de1ff..6c394716b 100644
--- a/resources/views/chapters/copy.blade.php
+++ b/resources/views/chapters/copy.blade.php
@@ -28,7 +28,7 @@
                 </div>
 
                 <div class="form-group" collapsible>
-                    <button type="button" class="collapse-title text-primary" collapsible-trigger aria-expanded="false">
+                    <button type="button" class="collapse-title text-link" collapsible-trigger aria-expanded="false">
                         <label for="entity_selection">{{ trans('entities.pages_copy_desination') }}</label>
                     </button>
                     <div class="collapse-content" collapsible-content>
diff --git a/resources/views/chapters/parts/convert-to-book.blade.php b/resources/views/chapters/parts/convert-to-book.blade.php
index 516d1117a..3bda32779 100644
--- a/resources/views/chapters/parts/convert-to-book.blade.php
+++ b/resources/views/chapters/parts/convert-to-book.blade.php
@@ -18,7 +18,7 @@
                     <li>
                         <form action="{{ $chapter->getUrl('/convert-to-book') }}" method="POST">
                             {!! csrf_field() !!}
-                            <button type="submit" class="text-primary text-item">{{ trans('common.confirm') }}</button>
+                            <button type="submit" class="text-link text-item">{{ trans('common.confirm') }}</button>
                         </form>
                     </li>
                 </ul>
diff --git a/resources/views/chapters/parts/form.blade.php b/resources/views/chapters/parts/form.blade.php
index 068c033ab..8abcebe13 100644
--- a/resources/views/chapters/parts/form.blade.php
+++ b/resources/views/chapters/parts/form.blade.php
@@ -12,7 +12,7 @@
 </div>
 
 <div class="form-group collapsible" component="collapsible" id="logo-control">
-    <button refs="collapsible@trigger" type="button" class="collapse-title text-primary" aria-expanded="false">
+    <button refs="collapsible@trigger" type="button" class="collapse-title text-link" aria-expanded="false">
         <label for="tags">{{ trans('entities.chapter_tags') }}</label>
     </button>
     <div refs="collapsible@content" class="collapse-content">
diff --git a/resources/views/chapters/show.blade.php b/resources/views/chapters/show.blade.php
index d2f8cec97..de52888dd 100644
--- a/resources/views/chapters/show.blade.php
+++ b/resources/views/chapters/show.blade.php
@@ -105,7 +105,7 @@
 
     <div class="actions mb-xl">
         <h5>{{ trans('common.actions') }}</h5>
-        <div class="icon-list text-primary">
+        <div class="icon-list text-link">
 
             @if(userCan('page-create', $chapter))
                 <a href="{{ $chapter->getUrl('/create-page') }}" data-shortcut="new" class="icon-list-item">
diff --git a/resources/views/entities/favourite-action.blade.php b/resources/views/entities/favourite-action.blade.php
index 24bd40950..35189044b 100644
--- a/resources/views/entities/favourite-action.blade.php
+++ b/resources/views/entities/favourite-action.blade.php
@@ -5,7 +5,7 @@
     {{ csrf_field() }}
     <input type="hidden" name="type" value="{{ get_class($entity) }}">
     <input type="hidden" name="id" value="{{ $entity->id }}">
-    <button type="submit" data-shortcut="favourite" class="icon-list-item text-primary">
+    <button type="submit" data-shortcut="favourite" class="icon-list-item text-link">
         <span>@icon($isFavourite ? 'star' : 'star-outline')</span>
         <span>{{ $isFavourite ? trans('common.unfavourite') : trans('common.favourite') }}</span>
     </button>
diff --git a/resources/views/entities/view-toggle.blade.php b/resources/views/entities/view-toggle.blade.php
index e376b878d..7546e230f 100644
--- a/resources/views/entities/view-toggle.blade.php
+++ b/resources/views/entities/view-toggle.blade.php
@@ -4,12 +4,12 @@
         {!! method_field('PATCH') !!}
 
         @if ($view === 'list')
-            <button type="submit" name="view" value="grid" class="icon-list-item text-primary">
+            <button type="submit" name="view" value="grid" class="icon-list-item text-link">
                 <span class="icon">@icon('grid')</span>
                 <span>{{ trans('common.grid_view') }}</span>
             </button>
         @else
-            <button type="submit" name="view" value="list" class="icon-list-item text-primary">
+            <button type="submit" name="view" value="list" class="icon-list-item text-link">
                 <span>@icon('list')</span>
                 <span>{{ trans('common.list_view') }}</span>
             </button>
diff --git a/resources/views/form/entity-permissions-row.blade.php b/resources/views/form/entity-permissions-row.blade.php
index 1cfb6ec98..6b515af86 100644
--- a/resources/views/form/entity-permissions-row.blade.php
+++ b/resources/views/form/entity-permissions-row.blade.php
@@ -16,7 +16,7 @@ $inheriting - Boolean if the current row should be marked as inheriting default
         </span>
         @if($role->id !== 0)
             <button type="button"
-                class="ml-auto flex-none text-small text-primary text-button hover-underline item-list-row-toggle-all hide-under-s"
+                class="ml-auto flex-none text-small text-link text-button hover-underline item-list-row-toggle-all hide-under-s"
                 refs="permissions-table@toggle-all"
                 ><strong>{{ trans('common.toggle_all') }}</strong></button>
         @endif
diff --git a/resources/views/home/books.blade.php b/resources/views/home/books.blade.php
index 75d4ae14a..95c0c9df2 100644
--- a/resources/views/home/books.blade.php
+++ b/resources/views/home/books.blade.php
@@ -11,7 +11,7 @@
 @section('right')
     <div class="actions mb-xl">
         <h5>{{ trans('common.actions') }}</h5>
-        <div class="icon-list text-primary">
+        <div class="icon-list text-link">
             @if(user()->can('book-create-all'))
                 <a href="{{ url("/create-book") }}" class="icon-list-item">
                     <span>@icon('add')</span>
@@ -19,8 +19,8 @@
                 </a>
             @endif
             @include('entities.view-toggle', ['view' => $view, 'type' => 'books'])
-            @include('home.parts.expand-toggle', ['classes' => 'text-primary', 'target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
-            @include('common.dark-mode-toggle', ['classes' => 'icon-list-item text-primary'])
+            @include('home.parts.expand-toggle', ['classes' => 'text-link', 'target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
+            @include('common.dark-mode-toggle', ['classes' => 'icon-list-item text-link'])
         </div>
     </div>
 @stop
diff --git a/resources/views/home/default.blade.php b/resources/views/home/default.blade.php
index f6a337e50..c273db276 100644
--- a/resources/views/home/default.blade.php
+++ b/resources/views/home/default.blade.php
@@ -6,12 +6,12 @@
         <div class="grid half">
             <div>
                 <div class="icon-list inline block">
-                    @include('home.parts.expand-toggle', ['classes' => 'text-muted text-primary', 'target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
+                    @include('home.parts.expand-toggle', ['classes' => 'text-muted text-link', 'target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
                 </div>
             </div>
             <div class="text-m-right">
                 <div class="icon-list inline block">
-                    @include('common.dark-mode-toggle', ['classes' => 'text-muted icon-list-item text-primary'])
+                    @include('common.dark-mode-toggle', ['classes' => 'text-muted icon-list-item text-link'])
                 </div>
             </div>
         </div>
diff --git a/resources/views/home/shelves.blade.php b/resources/views/home/shelves.blade.php
index fc99b915f..9699d6b96 100644
--- a/resources/views/home/shelves.blade.php
+++ b/resources/views/home/shelves.blade.php
@@ -11,7 +11,7 @@
 @section('right')
     <div class="actions mb-xl">
         <h5>{{ trans('common.actions') }}</h5>
-        <div class="icon-list text-primary">
+        <div class="icon-list text-link">
             @if(user()->can('bookshelf-create-all'))
                 <a href="{{ url("/create-shelf") }}" class="icon-list-item">
                     <span>@icon('add')</span>
@@ -19,8 +19,8 @@
                 </a>
             @endif
             @include('entities.view-toggle', ['view' => $view, 'type' => 'bookshelves'])
-            @include('home.parts.expand-toggle', ['classes' => 'text-primary', 'target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
-            @include('common.dark-mode-toggle', ['classes' => 'icon-list-item text-primary'])
+            @include('home.parts.expand-toggle', ['classes' => 'text-link', 'target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
+            @include('common.dark-mode-toggle', ['classes' => 'icon-list-item text-link'])
         </div>
     </div>
 @stop
diff --git a/resources/views/home/specific-page.blade.php b/resources/views/home/specific-page.blade.php
index 5c8e65b25..10ffe9629 100644
--- a/resources/views/home/specific-page.blade.php
+++ b/resources/views/home/specific-page.blade.php
@@ -19,9 +19,9 @@
 @section('right')
     <div class="actions mb-xl">
         <h5>{{ trans('common.actions') }}</h5>
-        <div class="icon-list text-primary">
-            @include('home.parts.expand-toggle', ['classes' => 'text-primary', 'target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
-            @include('common.dark-mode-toggle', ['classes' => 'icon-list-item text-primary'])
+        <div class="icon-list text-link">
+            @include('home.parts.expand-toggle', ['classes' => 'text-link', 'target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
+            @include('common.dark-mode-toggle', ['classes' => 'icon-list-item text-link'])
         </div>
     </div>
 @stop
\ No newline at end of file
diff --git a/resources/views/layouts/tri.blade.php b/resources/views/layouts/tri.blade.php
index e0f16321f..14016830d 100644
--- a/resources/views/layouts/tri.blade.php
+++ b/resources/views/layouts/tri.blade.php
@@ -11,7 +11,7 @@
                     refs="tri-layout@tab"
                     data-tab="info"
                     aria-label="{{ trans('common.tab_info_label') }}"
-                    class="tri-layout-mobile-tab px-m py-m text-primary">
+                    class="tri-layout-mobile-tab px-m py-m text-link">
                 {{ trans('common.tab_info') }}
             </button>
             <button type="button"
@@ -19,7 +19,7 @@
                     data-tab="content"
                     aria-label="{{ trans('common.tab_content_label') }}"
                     aria-selected="true"
-                    class="tri-layout-mobile-tab px-m py-m text-primary active">
+                    class="tri-layout-mobile-tab px-m py-m text-link active">
                 {{ trans('common.tab_content') }}
             </button>
         </div>
diff --git a/resources/views/mfa/parts/setup-method-row.blade.php b/resources/views/mfa/parts/setup-method-row.blade.php
index 271ec1bf4..b12028437 100644
--- a/resources/views/mfa/parts/setup-method-row.blade.php
+++ b/resources/views/mfa/parts/setup-method-row.blade.php
@@ -19,7 +19,7 @@
                     <form action="{{ url('/mfa/' . $method . '/remove') }}" method="post">
                         {{ csrf_field() }}
                         {{ method_field('delete') }}
-                        <button class="text-primary small text-item">{{ trans('common.confirm') }}</button>
+                        <button class="text-link small text-item">{{ trans('common.confirm') }}</button>
                     </form>
                 </div>
             </div>
diff --git a/resources/views/pages/copy.blade.php b/resources/views/pages/copy.blade.php
index 9f249863a..0d153bdf5 100644
--- a/resources/views/pages/copy.blade.php
+++ b/resources/views/pages/copy.blade.php
@@ -29,7 +29,7 @@
                 </div>
 
                 <div class="form-group" collapsible>
-                    <button type="button" class="collapse-title text-primary" collapsible-trigger aria-expanded="false">
+                    <button type="button" class="collapse-title text-link" collapsible-trigger aria-expanded="false">
                         <label for="entity_selection">{{ trans('entities.pages_copy_desination') }}</label>
                     </button>
                     <div class="collapse-content" collapsible-content>
diff --git a/resources/views/pages/parts/editor-toolbar.blade.php b/resources/views/pages/parts/editor-toolbar.blade.php
index fa5cb7374..c29e6de0e 100644
--- a/resources/views/pages/parts/editor-toolbar.blade.php
+++ b/resources/views/pages/parts/editor-toolbar.blade.php
@@ -3,14 +3,14 @@
 
         <div class="action-buttons text-left px-m py-xs">
             <a href="{{ $isDraft ? $page->getParent()->getUrl() : $page->getUrl() }}"
-               class="text-button text-primary">@icon('back')<span class="hide-under-l">{{ trans('common.back') }}</span></a>
+               class="text-button text-link">@icon('back')<span class="hide-under-l">{{ trans('common.back') }}</span></a>
         </div>
 
         <div class="text-center px-m">
             <div component="dropdown"
                  option:dropdown:move-menu="true"
                  class="dropdown-container draft-display text {{ $draftsEnabled ? '' : 'hidden' }}">
-                <button type="button" refs="dropdown@toggle" aria-haspopup="true" aria-expanded="false" title="{{ trans('entities.pages_edit_draft_options') }}" class="text-primary text-button py-s px-m"><span refs="page-editor@draftDisplay" class="faded-text"></span>&nbsp; @icon('more')</button>
+                <button type="button" refs="dropdown@toggle" aria-haspopup="true" aria-expanded="false" title="{{ trans('entities.pages_edit_draft_options') }}" class="text-link text-button py-s px-m"><span refs="page-editor@draftDisplay" class="faded-text"></span>&nbsp; @icon('more')</button>
                 @icon('check-circle', ['class' => 'text-pos draft-notification svg-icon', 'refs' => 'page-editor@draftDisplayIcon'])
                 <ul refs="dropdown@menu" class="dropdown-menu" role="menu">
                     <li>
@@ -68,7 +68,7 @@
             <div component="dropdown"
                  option:dropdown:move-menu="true"
                  class="dropdown-container">
-                <button refs="dropdown@toggle" type="button" aria-haspopup="true" aria-expanded="false" class="text-primary text-button">@icon('edit') <span refs="page-editor@changelogDisplay">{{ trans('entities.pages_edit_set_changelog') }}</span></button>
+                <button refs="dropdown@toggle" type="button" aria-haspopup="true" aria-expanded="false" class="text-link text-button">@icon('edit') <span refs="page-editor@changelogDisplay">{{ trans('entities.pages_edit_set_changelog') }}</span></button>
                 <ul refs="dropdown@menu" class="wide dropdown-menu">
                     <li class="px-l py-m">
                         <p class="text-muted pb-s">{{ trans('entities.pages_edit_enter_changelog_desc') }}</p>
@@ -82,7 +82,7 @@
                 <span>{{-- Prevents button jumping on menu show --}}</span>
             </div>
 
-            <button type="submit" id="save-button" class="float-left text-primary text-button text-pos-hover hide-under-m">@icon('save')<span>{{ trans('entities.pages_save') }}</span></button>
+            <button type="submit" id="save-button" class="float-left text-link text-button text-pos-hover hide-under-m">@icon('save')<span>{{ trans('entities.pages_save') }}</span></button>
         </div>
     </div>
 </div>
\ No newline at end of file
diff --git a/resources/views/pages/parts/form.blade.php b/resources/views/pages/parts/form.blade.php
index 8da5cbf39..a3a118527 100644
--- a/resources/views/pages/parts/form.blade.php
+++ b/resources/views/pages/parts/form.blade.php
@@ -44,7 +44,7 @@
     <button type="submit"
             id="save-button-mobile"
             title="{{ trans('entities.pages_save') }}"
-            class="text-primary text-button hide-over-m page-save-mobile-button">@icon('save')</button>
+            class="text-link text-button hide-over-m page-save-mobile-button">@icon('save')</button>
 
     {{--Editor Change Dialog--}}
     @component('common.confirm-dialog', ['title' => trans('entities.pages_editor_switch_title'), 'ref' => 'page-editor@switchDialog'])
diff --git a/resources/views/pages/parts/image-manager-list.blade.php b/resources/views/pages/parts/image-manager-list.blade.php
index e5562e10f..22fe7addd 100644
--- a/resources/views/pages/parts/image-manager-list.blade.php
+++ b/resources/views/pages/parts/image-manager-list.blade.php
@@ -3,7 +3,7 @@
     <div component="event-emit-select"
          option:event-emit-select:name="image"
          option:event-emit-select:data="{{ json_encode($image) }}"
-         class="image anim fadeIn text-primary"
+         class="image anim fadeIn text-link"
          style="animation-delay: {{ $index > 26 ? '160ms' : ($index * 25) . 'ms' }};">
         <img src="{{ $image->thumbs['gallery'] }}"
              alt="{{ $image->name }}"
diff --git a/resources/views/pages/parts/revisions-index-row.blade.php b/resources/views/pages/parts/revisions-index-row.blade.php
index fdc6a772d..db89284bb 100644
--- a/resources/views/pages/parts/revisions-index-row.blade.php
+++ b/resources/views/pages/parts/revisions-index-row.blade.php
@@ -46,7 +46,7 @@
                             <form action="{{ $revision->getUrl('/restore') }}" method="POST">
                                 {!! csrf_field() !!}
                                 <input type="hidden" name="_method" value="PUT">
-                                <button type="submit" class="text-primary icon-item">
+                                <button type="submit" class="text-link icon-item">
                                     @icon('history')
                                     <div>{{ trans('entities.pages_revisions_restore') }}</div>
                                 </button>
diff --git a/resources/views/pages/show.blade.php b/resources/views/pages/show.blade.php
index e05d9d739..2cbc7fe47 100644
--- a/resources/views/pages/show.blade.php
+++ b/resources/views/pages/show.blade.php
@@ -66,7 +66,7 @@
                     @foreach($pageNav as $navItem)
                         <li class="page-nav-item h{{ $navItem['level'] }}">
                             <a href="{{ $navItem['link'] }}" class="text-limit-lines-1 block">{{ $navItem['text'] }}</a>
-                            <div class="primary-background sidebar-page-nav-bullet"></div>
+                            <div class="link-background sidebar-page-nav-bullet"></div>
                         </li>
                     @endforeach
                 </div>
@@ -143,7 +143,7 @@
     <div class="actions mb-xl">
         <h5>{{ trans('common.actions') }}</h5>
 
-        <div class="icon-list text-primary">
+        <div class="icon-list text-link">
 
             {{--User Actions--}}
             @if(userCan('page-update', $page))
diff --git a/resources/views/settings/recycle-bin/index.blade.php b/resources/views/settings/recycle-bin/index.blade.php
index 9e82ba467..ff41cefcc 100644
--- a/resources/views/settings/recycle-bin/index.blade.php
+++ b/resources/views/settings/recycle-bin/index.blade.php
@@ -22,7 +22,7 @@
 
                             <form action="{{ url('/settings/recycle-bin/empty') }}" method="POST">
                                 {!! csrf_field() !!}
-                                <button type="submit" class="text-primary small delete text-item">{{ trans('common.confirm') }}</button>
+                                <button type="submit" class="text-link small delete text-item">{{ trans('common.confirm') }}</button>
                             </form>
                         </div>
                     </div>
diff --git a/resources/views/settings/roles/parts/asset-permissions-row.blade.php b/resources/views/settings/roles/parts/asset-permissions-row.blade.php
index df179a985..8bff570cb 100644
--- a/resources/views/settings/roles/parts/asset-permissions-row.blade.php
+++ b/resources/views/settings/roles/parts/asset-permissions-row.blade.php
@@ -1,7 +1,7 @@
 <div class="item-list-row flex-container-row items-center wrap">
     <div class="flex py-s px-m min-width-s">
         <strong>{{ $title }}</strong> <br>
-        <a href="#" refs="permissions-table@toggle-row" class="text-small text-primary">{{ trans('common.toggle_all') }}</a>
+        <a href="#" refs="permissions-table@toggle-row" class="text-small text-link">{{ trans('common.toggle_all') }}</a>
     </div>
     <div class="flex py-s px-m min-width-xxs">
         <small class="hide-over-m bold">{{ trans('common.create') }}<br></small>
diff --git a/resources/views/settings/roles/parts/form.blade.php b/resources/views/settings/roles/parts/form.blade.php
index 8534b7fdb..ac5c320d2 100644
--- a/resources/views/settings/roles/parts/form.blade.php
+++ b/resources/views/settings/roles/parts/form.blade.php
@@ -28,7 +28,7 @@
 
     <div component="permissions-table">
         <label class="setting-list-label">{{ trans('settings.role_system') }}</label>
-        <a href="#" refs="permissions-table@toggle-all" class="text-small text-primary">{{ trans('common.toggle_all') }}</a>
+        <a href="#" refs="permissions-table@toggle-all" class="text-small text-link">{{ trans('common.toggle_all') }}</a>
 
         <div class="toggle-switch-list grid half mt-m">
             <div>
@@ -62,7 +62,7 @@
              class="item-list toggle-switch-list">
             <div class="item-list-row flex-container-row items-center hide-under-m bold">
                 <div class="flex py-s px-m min-width-s">
-                    <a href="#" refs="permissions-table@toggle-all" class="text-small text-primary">{{ trans('common.toggle_all') }}</a>
+                    <a href="#" refs="permissions-table@toggle-all" class="text-small text-link">{{ trans('common.toggle_all') }}</a>
                 </div>
                 <div refs="permissions-table@toggle-column" class="flex py-s px-m min-width-xxs">{{ trans('common.create') }}</div>
                 <div refs="permissions-table@toggle-column" class="flex py-s px-m min-width-xxs">{{ trans('common.view') }}</div>
diff --git a/resources/views/settings/roles/parts/related-asset-permissions-row.blade.php b/resources/views/settings/roles/parts/related-asset-permissions-row.blade.php
index 1ec3d2257..62fdd6b74 100644
--- a/resources/views/settings/roles/parts/related-asset-permissions-row.blade.php
+++ b/resources/views/settings/roles/parts/related-asset-permissions-row.blade.php
@@ -1,7 +1,7 @@
 <div class="item-list-row flex-container-row items-center wrap">
     <div class="flex py-s px-m min-width-s">
         <strong>{{ $title }}</strong> <br>
-        <a href="#" refs="permissions-table@toggle-row" class="text-small text-primary">{{ trans('common.toggle_all') }}</a>
+        <a href="#" refs="permissions-table@toggle-row" class="text-small text-link">{{ trans('common.toggle_all') }}</a>
     </div>
     <div class="flex py-s px-m min-width-xxs">
         <small class="hide-over-m bold">{{ trans('common.create') }}<br></small>
diff --git a/resources/views/shelves/index.blade.php b/resources/views/shelves/index.blade.php
index df3ca83eb..eafc339ce 100644
--- a/resources/views/shelves/index.blade.php
+++ b/resources/views/shelves/index.blade.php
@@ -8,7 +8,7 @@
 
     <div class="actions mb-xl">
         <h5>{{ trans('common.actions') }}</h5>
-        <div class="icon-list text-primary">
+        <div class="icon-list text-link">
             @if(userCan('bookshelf-create-all'))
                 <a href="{{ url("/create-shelf") }}" data-shortcut="new" class="icon-list-item">
                     <span>@icon('add')</span>
diff --git a/resources/views/shelves/parts/form.blade.php b/resources/views/shelves/parts/form.blade.php
index a54452703..364f8e0be 100644
--- a/resources/views/shelves/parts/form.blade.php
+++ b/resources/views/shelves/parts/form.blade.php
@@ -44,7 +44,7 @@
 
 
 <div class="form-group collapsible" component="collapsible" id="logo-control">
-    <button refs="collapsible@trigger" type="button" class="collapse-title text-primary" aria-expanded="false">
+    <button refs="collapsible@trigger" type="button" class="collapse-title text-link" aria-expanded="false">
         <label>{{ trans('common.cover_image') }}</label>
     </button>
     <div refs="collapsible@content" class="collapse-content">
@@ -60,7 +60,7 @@
 </div>
 
 <div class="form-group collapsible" component="collapsible" id="tags-control">
-    <button refs="collapsible@trigger" type="button" class="collapse-title text-primary" aria-expanded="false">
+    <button refs="collapsible@trigger" type="button" class="collapse-title text-link" aria-expanded="false">
         <label for="tag-manager">{{ trans('entities.shelf_tags') }}</label>
     </button>
     <div refs="collapsible@content" class="collapse-content">
diff --git a/resources/views/shelves/show.blade.php b/resources/views/shelves/show.blade.php
index 1ea37992a..25b7a14fc 100644
--- a/resources/views/shelves/show.blade.php
+++ b/resources/views/shelves/show.blade.php
@@ -109,7 +109,7 @@
 @section('right')
     <div class="actions mb-xl">
         <h5>{{ trans('common.actions') }}</h5>
-        <div class="icon-list text-primary">
+        <div class="icon-list text-link">
 
             @if(userCan('book-create-all') && userCan('bookshelf-update', $shelf))
                 <a href="{{ $shelf->getUrl('/create-book') }}" data-shortcut="new" class="icon-list-item">

From 104621841be7e8a8ba2774a6d5925117cde2b9ec Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sat, 28 Jan 2023 17:11:15 +0000
Subject: [PATCH 5/8] Update JS to show live changes and set light color values

---
 app/Config/setting-defaults.php               |  2 +-
 resources/js/components/index.js              |  2 +-
 .../js/components/setting-app-color-picker.js | 50 -----------
 .../js/components/setting-app-color-scheme.js | 82 +++++++++++++++++++
 .../js/components/setting-color-picker.js     |  2 +-
 resources/js/components/tabs.js               |  2 +-
 resources/js/services/util.js                 |  2 +-
 .../views/common/custom-styles.blade.php      | 14 +---
 .../views/settings/customization.blade.php    | 12 ++-
 .../parts/setting-color-scheme.blade.php      |  6 +-
 10 files changed, 104 insertions(+), 70 deletions(-)
 delete mode 100644 resources/js/components/setting-app-color-picker.js
 create mode 100644 resources/js/components/setting-app-color-scheme.js

diff --git a/app/Config/setting-defaults.php b/app/Config/setting-defaults.php
index 005fddfd0..88c4612ca 100644
--- a/app/Config/setting-defaults.php
+++ b/app/Config/setting-defaults.php
@@ -24,7 +24,7 @@ return [
     'page-draft-color'     => '#7e50b1',
     'app-color-dark'       => '#195785',
     'app-color-light-dark' => 'rgba(32,110,167,0.15)',
-    'link-color-dark'      => '#195785',
+    'link-color-dark'      => '#429fe3',
     'bookshelf-color-dark' => '#ff5454',
     'book-color-dark'      => '#389f60',
     'chapter-color-dark'   => '#ee7a2d',
diff --git a/resources/js/components/index.js b/resources/js/components/index.js
index 27bce48db..82136184b 100644
--- a/resources/js/components/index.js
+++ b/resources/js/components/index.js
@@ -41,7 +41,7 @@ export {PagePicker} from "./page-picker.js"
 export {PermissionsTable} from "./permissions-table.js"
 export {Pointer} from "./pointer.js"
 export {Popup} from "./popup.js"
-export {SettingAppColorPicker} from "./setting-app-color-picker.js"
+export {SettingAppColorScheme} from "./setting-app-color-scheme.js"
 export {SettingColorPicker} from "./setting-color-picker.js"
 export {SettingHomepageControl} from "./setting-homepage-control.js"
 export {ShelfSort} from "./shelf-sort.js"
diff --git a/resources/js/components/setting-app-color-picker.js b/resources/js/components/setting-app-color-picker.js
deleted file mode 100644
index f167e5fc9..000000000
--- a/resources/js/components/setting-app-color-picker.js
+++ /dev/null
@@ -1,50 +0,0 @@
-import {Component} from "./component";
-
-export class SettingAppColorPicker extends Component {
-
-    setup() {
-        // TODO
-        this.colorInput = this.$refs.input;
-        this.lightColorInput = this.$refs.lightInput;
-
-        this.colorInput.addEventListener('change', this.updateColor.bind(this));
-        this.colorInput.addEventListener('input', this.updateColor.bind(this));
-    }
-
-    /**
-     * Update the app colors as a preview, and create a light version of the color.
-     */
-    updateColor() {
-        const hexVal = this.colorInput.value;
-        const rgb = this.hexToRgb(hexVal);
-        const rgbLightVal = 'rgba('+ [rgb.r, rgb.g, rgb.b, '0.15'].join(',') +')';
-
-        this.lightColorInput.value = rgbLightVal;
-
-        const customStyles = document.getElementById('custom-styles');
-        const oldColor = customStyles.getAttribute('data-color');
-        const oldColorLight = customStyles.getAttribute('data-color-light');
-
-        customStyles.innerHTML = customStyles.innerHTML.split(oldColor).join(hexVal);
-        customStyles.innerHTML = customStyles.innerHTML.split(oldColorLight).join(rgbLightVal);
-
-        customStyles.setAttribute('data-color', hexVal);
-        customStyles.setAttribute('data-color-light', rgbLightVal);
-    }
-
-    /**
-     * Covert a hex color code to rgb components.
-     * @attribution https://stackoverflow.com/a/5624139
-     * @param {String} hex
-     * @returns {{r: Number, g: Number, b: Number}}
-     */
-    hexToRgb(hex) {
-        const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
-        return {
-            r: result ? parseInt(result[1], 16) : 0,
-            g: result ? parseInt(result[2], 16) : 0,
-            b: result ? parseInt(result[3], 16) : 0
-        };
-    }
-
-}
diff --git a/resources/js/components/setting-app-color-scheme.js b/resources/js/components/setting-app-color-scheme.js
new file mode 100644
index 000000000..71b14badc
--- /dev/null
+++ b/resources/js/components/setting-app-color-scheme.js
@@ -0,0 +1,82 @@
+import {Component} from "./component";
+
+export class SettingAppColorScheme extends Component {
+
+    setup() {
+        this.container = this.$el;
+        this.mode = this.$opts.mode;
+        this.lightContainer = this.$refs.lightContainer;
+        this.darkContainer = this.$refs.darkContainer;
+
+        this.container.addEventListener('tabs-change', event => {
+            const panel = event.detail.showing;
+            const newMode = (panel === 'color-scheme-panel-light') ? 'light' : 'dark';
+            this.handleModeChange(newMode);
+        });
+
+        const onInputChange = (event) => {
+            this.updateAppColorsFromInputs();
+
+            if (event.target.name.startsWith('setting-app-color')) {
+                this.updateLightForInput(event.target);
+            }
+        };
+        this.container.addEventListener('change', onInputChange);
+        this.container.addEventListener('input', onInputChange);
+    }
+
+    handleModeChange(newMode) {
+        this.mode = newMode;
+        const isDark = (newMode === 'dark');
+
+        document.documentElement.classList.toggle('dark-mode', isDark);
+        this.updateAppColorsFromInputs();
+    }
+
+    updateAppColorsFromInputs() {
+        const inputContainer = this.mode === 'dark' ? this.darkContainer : this.lightContainer;
+        const inputs = inputContainer.querySelectorAll('input[type="color"]');
+        for (const input of inputs) {
+            const splitName = input.name.split('-');
+            const colorPos = splitName.indexOf('color');
+            let cssId = splitName.slice(1, colorPos).join('-');
+            if (cssId === 'app') {
+                cssId = 'primary';
+            }
+
+            const varName = '--color-' + cssId;
+            document.body.style.setProperty(varName, input.value);
+        }
+    }
+
+    /**
+     * Update the 'light' app color variant for the given input.
+     * @param {HTMLInputElement} input
+     */
+    updateLightForInput(input) {
+        const lightName = input.name.replace('-color', '-color-light');
+        const hexVal = input.value;
+        const rgb = this.hexToRgb(hexVal);
+        const rgbLightVal = 'rgba('+ [rgb.r, rgb.g, rgb.b, '0.15'].join(',') +')';
+
+        console.log(input.name, lightName, hexVal, rgbLightVal)
+        const lightColorInput = this.container.querySelector(`input[name="${lightName}"][type="hidden"]`);
+        lightColorInput.value = rgbLightVal;
+    }
+
+    /**
+     * Covert a hex color code to rgb components.
+     * @attribution https://stackoverflow.com/a/5624139
+     * @param {String} hex
+     * @returns {{r: Number, g: Number, b: Number}}
+     */
+    hexToRgb(hex) {
+        const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+        return {
+            r: result ? parseInt(result[1], 16) : 0,
+            g: result ? parseInt(result[2], 16) : 0,
+            b: result ? parseInt(result[3], 16) : 0
+        };
+    }
+
+}
diff --git a/resources/js/components/setting-color-picker.js b/resources/js/components/setting-color-picker.js
index 876e14f20..bfb2c93ce 100644
--- a/resources/js/components/setting-color-picker.js
+++ b/resources/js/components/setting-color-picker.js
@@ -15,6 +15,6 @@ export class SettingColorPicker extends Component {
 
     setValue(value) {
         this.colorInput.value = value;
-        this.colorInput.dispatchEvent(new Event('change'));
+        this.colorInput.dispatchEvent(new Event('change', {bubbles: true}));
     }
 }
\ No newline at end of file
diff --git a/resources/js/components/tabs.js b/resources/js/components/tabs.js
index 6d48048b5..8d22e3e9b 100644
--- a/resources/js/components/tabs.js
+++ b/resources/js/components/tabs.js
@@ -43,7 +43,7 @@ export class Tabs extends Component {
             tab.setAttribute('aria-selected', selected ? 'true' : 'false');
         }
 
-        this.$emit('change', {section: sectionId});
+        this.$emit('change', {showing: sectionId});
     }
 
 }
\ No newline at end of file
diff --git a/resources/js/services/util.js b/resources/js/services/util.js
index 1a56ebf6c..238f8b1d8 100644
--- a/resources/js/services/util.js
+++ b/resources/js/services/util.js
@@ -34,7 +34,7 @@ export function scrollAndHighlightElement(element) {
     if (!element) return;
     element.scrollIntoView({behavior: 'smooth'});
 
-    const color = document.getElementById('custom-styles').getAttribute('data-color-light');
+    const color = getComputedStyle(document.body).getPropertyValue('--color-primary-light');
     const initColor = window.getComputedStyle(element).getPropertyValue('background-color');
     element.style.backgroundColor = color;
     setTimeout(() => {
diff --git a/resources/views/common/custom-styles.blade.php b/resources/views/common/custom-styles.blade.php
index d261110c7..bfdcc8512 100644
--- a/resources/views/common/custom-styles.blade.php
+++ b/resources/views/common/custom-styles.blade.php
@@ -1,13 +1,7 @@
-<style id="custom-styles"
-       data-color="{{ setting('app-color') }}"
-       data-color-dark="{{ setting('app-color-dark') }}"
-       data-color-light="{{ setting('app-color-light') }}"
-       data-color-light-dark="{{ setting('app-color-light-dark') }}">
-
-    @php
-        $settingSuffix = setting()->getForCurrentUser('dark-mode-enabled') ? '-dark' : '';
-    @endphp
-
+@php
+    $settingSuffix = setting()->getForCurrentUser('dark-mode-enabled') ? '-dark' : '';
+@endphp
+<style>
     :root {
         --color-primary: {{ setting('app-color' . $settingSuffix) }};
         --color-primary-light: {{ setting('app-color-light' . $settingSuffix) }};
diff --git a/resources/views/settings/customization.blade.php b/resources/views/settings/customization.blade.php
index ac1f678a5..be99cc254 100644
--- a/resources/views/settings/customization.blade.php
+++ b/resources/views/settings/customization.blade.php
@@ -70,15 +70,17 @@
             </div>
 
             <!-- App Color Scheme -->
-            <div class="pb-l">
+            @php
+                $darkMode = boolval(setting()->getForCurrentUser('dark-mode-enabled'));
+            @endphp
+            <div component="setting-app-color-scheme"
+                 option:setting-app-color-scheme:mode="{{ $darkMode ? 'dark' : 'light' }}"
+                 class="pb-l">
                 <div class="mb-l">
                     <label class="setting-list-label">{{ trans('settings.color_scheme') }}</label>
                     <p class="small">{{ trans('settings.color_scheme_desc') }}</p>
                 </div>
 
-                @php
-                    $darkMode = boolval(setting()->getForCurrentUser('dark-mode-enabled'));
-                @endphp
                 <div component="tabs" class="tab-container">
                     <div role="tablist" class="controls-card">
                         <button type="button"
@@ -94,6 +96,7 @@
                     </div>
                     <div class="sub-card">
                         <div id="color-scheme-panel-light"
+                             refs="setting-app-color-scheme@lightContainer"
                              tabindex="0"
                              role="tabpanel"
                              aria-labelledby="color-scheme-tab-light"
@@ -102,6 +105,7 @@
                             @include('settings.parts.setting-color-scheme', ['mode' => 'light'])
                         </div>
                         <div id="color-scheme-panel-dark"
+                             refs="setting-app-color-scheme@darkContainer"
                              tabindex="0"
                              role="tabpanel"
                              aria-labelledby="color-scheme-tab-light"
diff --git a/resources/views/settings/parts/setting-color-scheme.blade.php b/resources/views/settings/parts/setting-color-scheme.blade.php
index 1b18a9a6a..7f8e55ac2 100644
--- a/resources/views/settings/parts/setting-color-scheme.blade.php
+++ b/resources/views/settings/parts/setting-color-scheme.blade.php
@@ -22,4 +22,8 @@
         @include('settings.parts.setting-color-picker', ['type' => 'page', 'mode' => $mode])
         @include('settings.parts.setting-color-picker', ['type' => 'page-draft', 'mode' => $mode])
     </div>
-</div>
\ No newline at end of file
+</div>
+
+<input type="hidden"
+       value="{{ setting('app-color-light' . ($mode === 'dark' ? '-dark' : '')) }}"
+       name="setting-app-color-light{{ $mode === 'dark' ? '-dark' : '' }}">
\ No newline at end of file

From f42ff59b4376493b8c4b30558f017d24a71dd75e Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sat, 28 Jan 2023 17:31:43 +0000
Subject: [PATCH 6/8] Added migration of color settings to dark mode

---
 ...1230_copy_color_settings_for_dark_mode.php | 60 +++++++++++++++++++
 1 file changed, 60 insertions(+)
 create mode 100644 database/migrations/2023_01_28_141230_copy_color_settings_for_dark_mode.php

diff --git a/database/migrations/2023_01_28_141230_copy_color_settings_for_dark_mode.php b/database/migrations/2023_01_28_141230_copy_color_settings_for_dark_mode.php
new file mode 100644
index 000000000..b72b16162
--- /dev/null
+++ b/database/migrations/2023_01_28_141230_copy_color_settings_for_dark_mode.php
@@ -0,0 +1,60 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Support\Facades\DB;
+
+class CopyColorSettingsForDarkMode extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        $colorSettings = [
+            'app-color',
+            'app-color-light',
+            'bookshelf-color',
+            'book-color',
+            'chapter-color',
+            'page-color',
+            'page-draft-color',
+        ];
+
+        $existing = DB::table('settings')
+            ->whereIn('setting_key', $colorSettings)
+            ->get()->toArray();
+
+        $newData = [];
+        foreach ($existing as $setting) {
+            $newSetting = (array) $setting;
+            $newSetting['setting_key'] .= '-dark';
+            $newData[] = $newSetting;
+        }
+
+        DB::table('settings')->insert($newData);
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        $colorSettings = [
+            'app-color-dark',
+            'app-color-light-dark',
+            'bookshelf-color-dark',
+            'book-color-dark',
+            'chapter-color-dark',
+            'page-color-dark',
+            'page-draft-color-dark',
+        ];
+
+        DB::table('settings')
+            ->whereIn('setting_key', $colorSettings)
+            ->delete();
+    }
+}

From 7751022c66134877eab405c6bb77ad92a4221523 Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sat, 28 Jan 2023 17:49:48 +0000
Subject: [PATCH 7/8] Updated migration to carry across more colors, updated
 export

Updated export to use link color for link.
Export will now copy primary color to link color options for stable
upgrades.
---
 ...23_01_28_141230_copy_color_settings_for_dark_mode.php | 9 +++++++++
 resources/views/exports/parts/styles.blade.php           | 2 +-
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/database/migrations/2023_01_28_141230_copy_color_settings_for_dark_mode.php b/database/migrations/2023_01_28_141230_copy_color_settings_for_dark_mode.php
index b72b16162..eb779fc7b 100644
--- a/database/migrations/2023_01_28_141230_copy_color_settings_for_dark_mode.php
+++ b/database/migrations/2023_01_28_141230_copy_color_settings_for_dark_mode.php
@@ -31,6 +31,13 @@ class CopyColorSettingsForDarkMode extends Migration
             $newSetting = (array) $setting;
             $newSetting['setting_key'] .= '-dark';
             $newData[] = $newSetting;
+
+            if ($newSetting['setting_key'] === 'app-color-dark') {
+                $newSetting['setting_key'] = 'link-color';
+                $newData[] = $newSetting;
+                $newSetting['setting_key'] = 'link-color-dark';
+                $newData[] = $newSetting;
+            }
         }
 
         DB::table('settings')->insert($newData);
@@ -45,6 +52,8 @@ class CopyColorSettingsForDarkMode extends Migration
     {
         $colorSettings = [
             'app-color-dark',
+            'link-color',
+            'link-color-dark',
             'app-color-light-dark',
             'bookshelf-color-dark',
             'book-color-dark',
diff --git a/resources/views/exports/parts/styles.blade.php b/resources/views/exports/parts/styles.blade.php
index 830b8e49a..f570ff512 100644
--- a/resources/views/exports/parts/styles.blade.php
+++ b/resources/views/exports/parts/styles.blade.php
@@ -10,7 +10,7 @@
     <style>
         /* Patches for CSS variable colors within PDF exports */
         a {
-            color: {{ setting('app-color') }};
+            color: {{ setting('app-link') }};
         }
 
         blockquote {

From 631546a68aa2e1d60f547919dc45345a7d1adfbe Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sat, 28 Jan 2023 17:57:43 +0000
Subject: [PATCH 8/8] Adjusted/improved some color setting wording

---
 resources/lang/en/settings.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/resources/lang/en/settings.php b/resources/lang/en/settings.php
index 90ad311a7..6f4376d42 100755
--- a/resources/lang/en/settings.php
+++ b/resources/lang/en/settings.php
@@ -50,8 +50,8 @@ return [
 
     // Color settings
     'color_scheme' => 'Application Color Scheme',
-    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light mode to best fit the theme and ensure legibility.',
-    'ui_colors_desc' => 'Set the primary and link colors used in BookStack. The primary color is mainly used for the header banner, buttons and UI decorations, in addition to a few other components.',
+    'color_scheme_desc' => 'Set the colors to use in the BookStack interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.',
+    'ui_colors_desc' => 'Set the primary color and default link color for BookStack. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the Bookstack interface.',
     'app_color' => 'Primary Color',
     'link_color' => 'Default Link Color',
     'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',