Просмотр исходного кода

Merge pull request #5857 from seve12/main

Accessibility:
- Added product name to page titles, settings and global search.
- More accessible header and layout templates, modal dialogs, DOM structure, color contrast.
Lauri Ojansivu 1 неделя назад
Родитель
Сommit
4ce7ff7cef

+ 1 - 0
client/components/boards/boardBody.jade

@@ -16,6 +16,7 @@ template(name="boardBody")
   if notDisplayThisBoard
    | {{_ 'tableVisibilityMode-allowPrivateOnly'}}
   else
+
     .board-wrapper(class=currentBoard.colorClass)
       .board-canvas.js-swimlanes(
         class="{{#if hasSwimlanes}}dragscroll{{/if}}"

+ 27 - 7
client/components/boards/boardBody.js

@@ -340,12 +340,12 @@ BlazeComponent.extendComponent({
       selectable: true,
       timezone: 'local',
       weekNumbers: true,
-      header: {
-        left: 'title   today prev,next',
-        center:
-          'agendaDay,listDay,timelineDay agendaWeek,listWeek,timelineWeek month,listMonth',
-        right: '',
+      headerToolbar: {
+        left: 'prev,next today',
+        center: 'title',
+        right: 'month,agendaWeek,agendaDay'
       },
+      footerToolbar: false,
       // height: 'parent', nope, doesn't work as the parent might be small
       height: 'auto',
       /* TODO: lists as resources: https://fullcalendar.io/docs/vertical-resource-view */
@@ -473,9 +473,29 @@ BlazeComponent.extendComponent({
             closeModal();
           }
         });
-        document.body.appendChild(modalElement);
+        document.querySelector('.board-wrapper').appendChild(modalElement); // (optional: for better DOM order)
         const openModal = function() {
-          modalElement.style.display = 'flex';
+            modalElement.style.display = 'flex';
+            modalElement.setAttribute('role', 'dialog');
+            modalElement.setAttribute('aria-modal', 'true');
+            // Set ARIA attributes for accessibility
+            modalElement.setAttribute('role', 'dialog');
+            modalElement.setAttribute('aria-modal', 'true');
+            // Move focus to the first input or button in the modal
+            const firstInput = modalElement.querySelector('input, button');
+            if (firstInput) firstInput.focus();
+            // Set aria-labelledby and aria-describedby for accessibility
+            const title = modalElement.querySelector('.modal-title');
+            if (title) {
+              title.id = 'modal-title';
+              modalElement.setAttribute('aria-labelledby', 'modal-title');
+            }
+            const desc = modalElement.querySelector('.modal-body');
+            if (desc) {
+              desc.id = 'modal-desc';
+              modalElement.setAttribute('aria-describedby', 'modal-desc');
+            }
+
         };
         const closeModal = function() {
           modalElement.style.display = 'none';

+ 6 - 3
client/components/boards/boardHeader.jade

@@ -1,5 +1,5 @@
 template(name="boardHeaderBar")
-  h1.header-board-menu
+  h1.header-board-menu {{currentBoard.title}}
     with currentBoard
       if $eq title 'Templates'
         | {{_ 'templates'}}
@@ -137,8 +137,11 @@ template(name="boardHeaderBar")
           i.fa.fa-check-square-o
           span {{#if MultiSelection.isActive}}{{_ 'multi-selection-on'}}{{else}}{{_ 'multi-selection'}}{{/if}}
           if MultiSelection.isActive
-            a.board-header-btn-close.js-multiselection-reset(title="{{_ 'filter-clear'}}")
-              i.fa.fa-times-thin
+            a.board-header-btn-close.js-multiselection-reset(
+            title="{{_ 'deactivate-multi-selection'}}"
+            aria-label="{{_ 'deactivate-multi-selection'}}"
+          )
+            i.fa.fa-times-thin
 
       .separator
       a.board-header-btn.js-toggle-sidebar(title="{{_ 'sidebar-open'}} {{_ 'or'}} {{_ 'sidebar-close'}}")

+ 1 - 1
client/components/cards/cardDetails.jade

@@ -540,7 +540,7 @@ template(name="cardDetails")
       else if getDescription
         if currentBoard.allowsDescriptionTitle
           hr
-          h3.card-details-item-title {{_ 'description'}}
+          div.card-details-item-title {{_ 'description'}}
         if currentBoard.allowsDescriptionText
           +viewer
             = getDescription

+ 12 - 3
client/components/main/header.jade

@@ -39,12 +39,20 @@ template(name="header")
           if currentSetting.customTopLeftCornerLogoImageUrl
             if currentSetting.customTopLeftCornerLogoLinkUrl
               a(href="{{currentSetting.customTopLeftCornerLogoLinkUrl}}" alt="{{currentSetting.productName}}" title="{{currentSetting.productName}}")
-                img(src="{{currentSetting.customTopLeftCornerLogoImageUrl}}" height="{{#if currentSetting.customTopLeftCornerLogoHeight}}#{currentSetting.customTopLeftCornerLogoHeight}{{else}}27{{/if}}" width="auto" margin="0" padding="0")
+                img(
+                  src="{{currentSetting.customTopLeftCornerLogoImageUrl}}"
+                  alt="{{currentSetting.productName}} logo"
+                  title="{{currentSetting.productName}}"
+                )
             unless currentSetting.customTopLeftCornerLogoLinkUrl
               img(src="{{currentSetting.customTopLeftCornerLogoImageUrl}}" height="{{#if currentSetting.customTopLeftCornerLogoHeight}}#{currentSetting.customTopLeftCornerLogoHeight}{{else}}27{{/if}}" width="auto" margin="0" padding="0" alt="{{currentSetting.productName}}" title="{{currentSetting.productName}}")
           unless currentSetting.customTopLeftCornerLogoImageUrl
             div#headerIsSettingDatabaseCallDone
-              img(src="{{pathFor '/logo-header.png'}}" alt="{{currentSetting.productName}}" title="{{currentSetting.productName}}")
+              img(
+                src="{{pathFor '/logo-header.png'}}"
+                alt="{{currentSetting.productName}} logo"
+                title="{{currentSetting.productName}}"
+              )
         span.allBoards
           a(href="{{pathFor 'home'}}")
             span.fa.fa-home
@@ -73,7 +81,8 @@ template(name="header")
       //  Hide duplicate create board button,
       //  because it did not show board templates correctly.
       //a#header-new-board-icon.js-create-board
-      //  i.fa.fa-plus(title="Create a new board")
+      button.add-card(aria-label="Add card" title="Add card")
+        i.fa.fa-plus
 
       +notifications
 

+ 34 - 1
client/components/main/layouts.css

@@ -23,6 +23,39 @@ table, tbody, td, tfoot, th, thread, tr, tt, ul, var {
 .panel-heading.note-toolbar .note-color-palette div .note-color-btn {
   background: none;
 }
+input::placeholder, textarea::placeholder {
+  color: #555 !important;
+  opacity: 1;
+}
+.error-notification {
+  color: #fff !important; /* White text */
+  background: #d32f2f !important; /* Strong red background */
+}
+.menu-text, .sidebar-text {
+  color: #222 !important; /* Very dark grey or black */
+}
+.notification-link {
+  color: #0056b3 !important; /* Darker blue for better contrast */
+}
+.card-title, .header-title {
+  color: #fff !important; /* White text for dark backgrounds */
+}
+.checklist-item.finished {
+  color: #444 !important; /* Darker grey for better contrast */
+  text-decoration: line-through;
+}
+.button-dark {
+  color: #fff !important;
+}
+.heading {
+  color: #222 !important;
+}
+.menu-text {
+  color: #222 !important;
+}
+.calendar-event, .calendar-card {
+  color: #fff !important;
+}
 a:focus {
   outline: unset;
   outline-offset: unset;
@@ -194,7 +227,7 @@ strong {
 p {
   -webkit-user-select: text;
   user-select: text;
- 
+
 
 }
 p a {

+ 2 - 2
client/components/main/layouts.jade

@@ -21,11 +21,11 @@ head
 template(name="userFormsLayout")
   section.auth-layout
     if currentSetting.hideLogo
-      h1.at-form-landing-logo
+      h1.at-form-landing-logo Wekan
         br
         br
     unless currentSetting.hideLogo
-      h1.at-form-landing-logo
+      h1.at-form-landing-logo Wekan
       if currentSetting.customLoginLogoImageUrl
         if currentSetting.customLoginLogoLinkUrl
           a(href="{{currentSetting.customLoginLogoLinkUrl}}")

+ 1 - 1
client/components/settings/settingBody.js

@@ -288,7 +288,7 @@ BlazeComponent.extendComponent({
       this.setLoading(false);
     }
 
-    DocHead.setTitle(productName);
+    DocHead.setTitle(`Settings - ${productName}`);
   },
 
   sendSMTPTestEmail() {

+ 3 - 2
config/router.js

@@ -225,8 +225,9 @@ FlowRouter.route('/global-search', {
 
     Utils.manageCustomUI();
     Utils.manageMatomo();
-    DocHead.setTitle(TAPi18n.__('globalSearch-title'));
-
+    const currentSetting = ReactiveCache.getCurrentSetting && ReactiveCache.getCurrentSetting();
+    const productName = currentSetting && currentSetting.productName ? currentSetting.productName : 'Wekan';
+    DocHead.setTitle(`${TAPi18n.__('globalSearch-title')} - ${productName}`);
     if (FlowRouter.getQueryParam('q')) {
       Session.set(
         'globalQuery',