From 1f69965c1e61a6ef3d46eb46ef4290889ba34381 Mon Sep 17 00:00:00 2001 From: Dan Brown 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.
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') }} + type="button" class="tab-item" title="{{ trans('components.image_book_title') }}">@icon('book', ['class' => 'svg-icon']) {{ trans('entities.book') }} + type="button" class="tab-item" title="{{ trans('components.image_page_title') }}">@icon('page', ['class' => 'svg-icon']) {{ trans('entities.page') }}
- -
-
- -

{!! trans('settings.app_primary_color_desc') !!}

-
-
- - -
- - | - -
- -
-
- - +
-
- -

{!! trans('settings.content_colors_desc') !!}

+
+ +

{{ trans('settings.color_scheme_desc') }}

-
-
- @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 +
+ -
- @include('settings.parts.setting-entity-color-picker', ['type' => 'page']) - @include('settings.parts.setting-entity-color-picker', ['type' => 'page-draft']) +
+
+ @include('settings.parts.setting-color-scheme', ['mode' => 'light']) +
+
+ @include('settings.parts.setting-color-scheme', ['mode' => 'dark']) +
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
- + | @@ -14,10 +17,10 @@
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'. +--}} +

{{ trans('settings.ui_colors_desc') }}

+
+
+ @include('settings.parts.setting-color-picker', ['type' => 'app', 'mode' => $mode]) +
+
+ @include('settings.parts.setting-color-picker', ['type' => 'link', 'mode' => $mode]) +
+
+
+

{!! trans('settings.content_colors_desc') !!}

+
+
+ @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]) +
+
+ @include('settings.parts.setting-color-picker', ['type' => 'page', 'mode' => $mode]) + @include('settings.parts.setting-color-picker', ['type' => 'page-draft', 'mode' => $mode]) +
+
\ No newline at end of file From e708ce93baa19715b0951c86c111320d4af2dc82 Mon Sep 17 00:00:00 2001 From: Dan Brown 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' with 'tabContent' 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= + * On panels: + * - id + * - tabindex=0 + * - role=tabpanel + * - aria-labelledby= + * - 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 @@
-

{{ trans('entities.attachments_explain') }} {{ trans('entities.attachments_explain_instant_save') }}

+

{{ trans('entities.attachments_explain') }} {{ trans('entities.attachments_explain_instant_save') }}

-