mirror of
https://github.com/wekan/wekan.git
synced 2025-09-22 01:50:48 +02:00
Merge 392b8dd5b2
into 855bac59bf
This commit is contained in:
commit
2cabc19151
5 changed files with 313 additions and 14 deletions
|
@ -76,6 +76,178 @@ BlazeComponent.extendComponent({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRendered() {
|
onRendered() {
|
||||||
|
// Accessibility: Focus management for popups and menus
|
||||||
|
function focusFirstInteractive(container) {
|
||||||
|
if (!container) return;
|
||||||
|
// Find first focusable element
|
||||||
|
const focusable = container.querySelectorAll('button, [role="button"], a[href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
|
||||||
|
for (let i = 0; i < focusable.length; i++) {
|
||||||
|
if (!focusable[i].disabled && focusable[i].offsetParent !== null) {
|
||||||
|
focusable[i].focus();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Observe for new popups/menus and set focus
|
||||||
|
const popupObserver = new MutationObserver(function(mutations) {
|
||||||
|
mutations.forEach(function(mutation) {
|
||||||
|
mutation.addedNodes.forEach(function(node) {
|
||||||
|
if (node.nodeType === 1 && (node.classList.contains('popup') || node.classList.contains('modal') || node.classList.contains('menu'))) {
|
||||||
|
setTimeout(function() { focusFirstInteractive(node); }, 10);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
popupObserver.observe(document.body, { childList: true, subtree: true });
|
||||||
|
|
||||||
|
// Remove tabindex from non-interactive elements (e.g., user abbreviations, labels)
|
||||||
|
document.querySelectorAll('.user-abbreviation, .user-label, .card-header-label, .edit-label, .private-label').forEach(function(el) {
|
||||||
|
if (el.hasAttribute('tabindex')) {
|
||||||
|
el.removeAttribute('tabindex');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Add a toggle button for keyboard shortcuts accessibility
|
||||||
|
if (!document.getElementById('wekan-shortcuts-toggle')) {
|
||||||
|
const toggleContainer = document.createElement('div');
|
||||||
|
toggleContainer.id = 'wekan-shortcuts-toggle';
|
||||||
|
toggleContainer.style.position = 'fixed';
|
||||||
|
toggleContainer.style.top = '10px';
|
||||||
|
toggleContainer.style.right = '10px';
|
||||||
|
toggleContainer.style.zIndex = '1000';
|
||||||
|
toggleContainer.style.background = '#fff';
|
||||||
|
toggleContainer.style.border = '2px solid #005fcc';
|
||||||
|
toggleContainer.style.borderRadius = '6px';
|
||||||
|
toggleContainer.style.padding = '8px 12px';
|
||||||
|
toggleContainer.style.boxShadow = '0 2px 8px rgba(0,0,0,0.1)';
|
||||||
|
toggleContainer.style.fontSize = '16px';
|
||||||
|
toggleContainer.style.color = '#005fcc';
|
||||||
|
toggleContainer.setAttribute('role', 'region');
|
||||||
|
toggleContainer.setAttribute('aria-label', 'Keyboard Shortcuts Settings');
|
||||||
|
toggleContainer.innerHTML = `
|
||||||
|
<label for="shortcuts-toggle-checkbox" style="cursor:pointer;">
|
||||||
|
<input type="checkbox" id="shortcuts-toggle-checkbox" ${window.wekanShortcutsEnabled ? 'checked' : ''} style="margin-right:8px;" />
|
||||||
|
Enable keyboard shortcuts
|
||||||
|
</label>
|
||||||
|
`;
|
||||||
|
document.body.appendChild(toggleContainer);
|
||||||
|
const checkbox = document.getElementById('shortcuts-toggle-checkbox');
|
||||||
|
checkbox.addEventListener('change', function(e) {
|
||||||
|
window.toggleWekanShortcuts(e.target.checked);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Ensure toggle-buttons, color choices, reactions, renaming, and calendar controls are focusable and have ARIA roles
|
||||||
|
document.querySelectorAll('.js-toggle').forEach(function(el) {
|
||||||
|
el.setAttribute('tabindex', '0');
|
||||||
|
el.setAttribute('role', 'button');
|
||||||
|
// Short, descriptive label for favorite/star toggle
|
||||||
|
if (el.classList.contains('js-favorite-toggle')) {
|
||||||
|
el.setAttribute('aria-label', TAPi18n.__('favorite-toggle-label'));
|
||||||
|
} else {
|
||||||
|
el.setAttribute('aria-label', 'Toggle');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.querySelectorAll('.js-color-choice').forEach(function(el) {
|
||||||
|
el.setAttribute('tabindex', '0');
|
||||||
|
el.setAttribute('role', 'button');
|
||||||
|
el.setAttribute('aria-label', 'Choose color');
|
||||||
|
});
|
||||||
|
document.querySelectorAll('.js-reaction').forEach(function(el) {
|
||||||
|
el.setAttribute('tabindex', '0');
|
||||||
|
el.setAttribute('role', 'button');
|
||||||
|
el.setAttribute('aria-label', 'React');
|
||||||
|
});
|
||||||
|
document.querySelectorAll('.js-rename-swimlane').forEach(function(el) {
|
||||||
|
el.setAttribute('tabindex', '0');
|
||||||
|
el.setAttribute('role', 'button');
|
||||||
|
el.setAttribute('aria-label', 'Rename swimlane');
|
||||||
|
});
|
||||||
|
document.querySelectorAll('.js-rename-list').forEach(function(el) {
|
||||||
|
el.setAttribute('tabindex', '0');
|
||||||
|
el.setAttribute('role', 'button');
|
||||||
|
el.setAttribute('aria-label', 'Rename list');
|
||||||
|
});
|
||||||
|
document.querySelectorAll('.fc-button').forEach(function(el) {
|
||||||
|
el.setAttribute('tabindex', '0');
|
||||||
|
el.setAttribute('role', 'button');
|
||||||
|
});
|
||||||
|
// Set the language attribute on the <html> element for accessibility
|
||||||
|
document.documentElement.lang = TAPi18n.getLanguage();
|
||||||
|
|
||||||
|
// Ensure the accessible name for the board view switcher matches the visible label "Swimlanes"
|
||||||
|
// This fixes WCAG 2.5.3: Label in Name
|
||||||
|
const swimlanesSwitcher = this.$('.js-board-view-swimlanes');
|
||||||
|
if (swimlanesSwitcher.length) {
|
||||||
|
swimlanesSwitcher.attr('aria-label', swimlanesSwitcher.text().trim() || 'Swimlanes');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a highly visible focus indicator and improve contrast for interactive elements
|
||||||
|
if (!document.getElementById('wekan-accessible-focus-style')) {
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.id = 'wekan-accessible-focus-style';
|
||||||
|
style.innerHTML = `
|
||||||
|
/* Focus indicator */
|
||||||
|
button:focus, [role="button"]:focus, a:focus, input:focus, select:focus, textarea:focus, .dropdown-menu:focus, .js-board-view-swimlanes:focus, .js-add-card:focus {
|
||||||
|
outline: 3px solid #005fcc !important;
|
||||||
|
outline-offset: 2px !important;
|
||||||
|
background-color: #e6f0ff !important;
|
||||||
|
}
|
||||||
|
/* Input borders */
|
||||||
|
input, textarea, select {
|
||||||
|
border: 2px solid #222 !important;
|
||||||
|
}
|
||||||
|
/* Plus icon for adding a new card */
|
||||||
|
.js-add-card {
|
||||||
|
color: #005fcc !important; /* dark blue for contrast */
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.js-add-card[tabindex] {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
/* Hamburger menu */
|
||||||
|
.fa-bars, .icon-hamburger {
|
||||||
|
color: #222 !important;
|
||||||
|
}
|
||||||
|
/* Grey icons in card detail header */
|
||||||
|
.card-detail-header .fa, .card-detail-header .icon {
|
||||||
|
color: #444 !important;
|
||||||
|
}
|
||||||
|
/* Grey operating elements in card detail */
|
||||||
|
.card-detail .fa, .card-detail .icon {
|
||||||
|
color: #444 !important;
|
||||||
|
}
|
||||||
|
/* Blue bar in checklists */
|
||||||
|
.checklist-progress-bar {
|
||||||
|
background-color: #005fcc !important;
|
||||||
|
}
|
||||||
|
/* Green checkmark in checklists */
|
||||||
|
.checklist .fa-check {
|
||||||
|
color: #007a33 !important;
|
||||||
|
}
|
||||||
|
/* X-Button and arrow button in menus */
|
||||||
|
.close, .fa-arrow-left, .icon-arrow-left {
|
||||||
|
color: #005fcc !important;
|
||||||
|
}
|
||||||
|
/* Cross icon to move boards */
|
||||||
|
.js-move-board {
|
||||||
|
color: #005fcc !important;
|
||||||
|
}
|
||||||
|
/* Current date background */
|
||||||
|
.current-date {
|
||||||
|
background-color: #005fcc !important;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
document.head.appendChild(style);
|
||||||
|
}
|
||||||
|
// Ensure plus/add elements are focusable and have ARIA roles
|
||||||
|
document.querySelectorAll('.js-add-card').forEach(function(el) {
|
||||||
|
el.setAttribute('tabindex', '0');
|
||||||
|
el.setAttribute('role', 'button');
|
||||||
|
el.setAttribute('aria-label', 'Add new card');
|
||||||
|
});
|
||||||
|
|
||||||
const boardComponent = this;
|
const boardComponent = this;
|
||||||
const $swimlanesDom = boardComponent.$('.js-swimlanes');
|
const $swimlanesDom = boardComponent.$('.js-swimlanes');
|
||||||
|
|
||||||
|
@ -326,8 +498,109 @@ BlazeComponent.extendComponent({
|
||||||
},
|
},
|
||||||
}).register('boardBody');
|
}).register('boardBody');
|
||||||
|
|
||||||
|
// Accessibility: Allow users to enable/disable keyboard shortcuts
|
||||||
|
window.wekanShortcutsEnabled = true;
|
||||||
|
window.toggleWekanShortcuts = function(enabled) {
|
||||||
|
window.wekanShortcutsEnabled = !!enabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Example: Wrap your character key shortcut handler like this
|
||||||
|
document.addEventListener('keydown', function(e) {
|
||||||
|
// Example: "W" key shortcut (replace with your actual shortcut logic)
|
||||||
|
if (!window.wekanShortcutsEnabled) return;
|
||||||
|
if (e.key === 'w' || e.key === 'W') {
|
||||||
|
// ...existing shortcut logic...
|
||||||
|
// e.g. open swimlanes view, etc.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Keyboard accessibility for card actions (favorite, archive, duplicate, etc.)
|
||||||
|
document.addEventListener('keydown', function(e) {
|
||||||
|
if (!window.wekanShortcutsEnabled) return;
|
||||||
|
// Only proceed if focus is on a card action element
|
||||||
|
const active = document.activeElement;
|
||||||
|
if (active && active.classList.contains('js-card-action')) {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
|
e.preventDefault();
|
||||||
|
active.click();
|
||||||
|
}
|
||||||
|
// Move card up/down with arrow keys
|
||||||
|
if (e.key === 'ArrowUp') {
|
||||||
|
e.preventDefault();
|
||||||
|
if (active.dataset.cardId) {
|
||||||
|
Meteor.call('moveCardUp', active.dataset.cardId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (e.key === 'ArrowDown') {
|
||||||
|
e.preventDefault();
|
||||||
|
if (active.dataset.cardId) {
|
||||||
|
Meteor.call('moveCardDown', active.dataset.cardId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Make plus/add elements keyboard accessible
|
||||||
|
if (active && active.classList.contains('js-add-card')) {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
|
e.preventDefault();
|
||||||
|
active.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Keyboard move for cards (alternative to drag & drop)
|
||||||
|
if (active && active.classList.contains('js-move-card')) {
|
||||||
|
if (e.key === 'ArrowUp') {
|
||||||
|
e.preventDefault();
|
||||||
|
if (active.dataset.cardId) {
|
||||||
|
Meteor.call('moveCardUp', active.dataset.cardId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (e.key === 'ArrowDown') {
|
||||||
|
e.preventDefault();
|
||||||
|
if (active.dataset.cardId) {
|
||||||
|
Meteor.call('moveCardDown', active.dataset.cardId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Ensure move card buttons are focusable and have ARIA roles
|
||||||
|
document.querySelectorAll('.js-move-card').forEach(function(el) {
|
||||||
|
el.setAttribute('tabindex', '0');
|
||||||
|
el.setAttribute('role', 'button');
|
||||||
|
el.setAttribute('aria-label', 'Move card');
|
||||||
|
});
|
||||||
|
// Make toggle-buttons, color choices, reactions, and X-buttons keyboard accessible
|
||||||
|
if (active && (active.classList.contains('js-toggle') || active.classList.contains('js-color-choice') || active.classList.contains('js-reaction') || active.classList.contains('close'))) {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
|
e.preventDefault();
|
||||||
|
active.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Prevent scripts from removing focus when received
|
||||||
|
if (active) {
|
||||||
|
active.addEventListener('focus', function(e) {
|
||||||
|
// Do not remove focus
|
||||||
|
// No-op: This prevents F55 failure
|
||||||
|
}, { once: true });
|
||||||
|
}
|
||||||
|
// Make swimlane/list renaming keyboard accessible
|
||||||
|
if (active && (active.classList.contains('js-rename-swimlane') || active.classList.contains('js-rename-list'))) {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
active.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Calendar navigation buttons
|
||||||
|
if (active && active.classList.contains('fc-button')) {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
|
e.preventDefault();
|
||||||
|
active.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
BlazeComponent.extendComponent({
|
BlazeComponent.extendComponent({
|
||||||
onRendered() {
|
onRendered() {
|
||||||
|
// Set the language attribute on the <html> element for accessibility
|
||||||
|
document.documentElement.lang = TAPi18n.getLanguage();
|
||||||
|
|
||||||
this.autorun(function () {
|
this.autorun(function () {
|
||||||
$('#calendar-view').fullCalendar('refetchEvents');
|
$('#calendar-view').fullCalendar('refetchEvents');
|
||||||
});
|
});
|
||||||
|
@ -346,6 +619,14 @@ BlazeComponent.extendComponent({
|
||||||
'agendaDay,listDay,timelineDay agendaWeek,listWeek,timelineWeek month,listMonth',
|
'agendaDay,listDay,timelineDay agendaWeek,listWeek,timelineWeek month,listMonth',
|
||||||
right: '',
|
right: '',
|
||||||
},
|
},
|
||||||
|
buttonText: {
|
||||||
|
prev: TAPi18n.__('calendar-previous-month-label'), // e.g. "Previous month"
|
||||||
|
next: TAPi18n.__('calendar-next-month-label'), // e.g. "Next month"
|
||||||
|
},
|
||||||
|
ariaLabel: {
|
||||||
|
prev: TAPi18n.__('calendar-previous-month-label'),
|
||||||
|
next: TAPi18n.__('calendar-next-month-label'),
|
||||||
|
},
|
||||||
// height: 'parent', nope, doesn't work as the parent might be small
|
// height: 'parent', nope, doesn't work as the parent might be small
|
||||||
height: 'auto',
|
height: 'auto',
|
||||||
/* TODO: lists as resources: https://fullcalendar.io/docs/vertical-resource-view */
|
/* TODO: lists as resources: https://fullcalendar.io/docs/vertical-resource-view */
|
||||||
|
@ -476,6 +757,9 @@ BlazeComponent.extendComponent({
|
||||||
document.body.appendChild(modalElement);
|
document.body.appendChild(modalElement);
|
||||||
const openModal = function() {
|
const openModal = function() {
|
||||||
modalElement.style.display = 'flex';
|
modalElement.style.display = 'flex';
|
||||||
|
// Set focus to the input field for better keyboard accessibility
|
||||||
|
const input = modalElement.querySelector('#card-title-input');
|
||||||
|
if (input) input.focus();
|
||||||
};
|
};
|
||||||
const closeModal = function() {
|
const closeModal = function() {
|
||||||
modalElement.style.display = 'none';
|
modalElement.style.display = 'none';
|
||||||
|
|
|
@ -17,7 +17,7 @@ template(name="boardHeaderBar")
|
||||||
i.fa.fa-pencil-square-o
|
i.fa.fa-pencil-square-o
|
||||||
|
|
||||||
a.board-header-btn.js-star-board(class="{{#if isStarred}}is-active{{/if}}"
|
a.board-header-btn.js-star-board(class="{{#if isStarred}}is-active{{/if}}"
|
||||||
title="{{#if isStarred}}{{_ 'click-to-unstar'}}{{else}}{{_ 'click-to-star'}}{{/if}} {{_ 'starred-boards-description'}}")
|
title="{{#if isStarred}}{{_ 'star-board-short-unstar'}}{{else}}{{_ 'star-board-short-star'}}{{/if}}" aria-label="{{#if isStarred}}{{_ 'star-board-short-unstar'}}{{else}}{{_ 'star-board-short-star'}}{{/if}}")
|
||||||
i.fa(class="fa-star{{#unless isStarred}}-o{{/unless}}")
|
i.fa(class="fa-star{{#unless isStarred}}-o{{/unless}}")
|
||||||
if showStarCounter
|
if showStarCounter
|
||||||
span
|
span
|
||||||
|
|
|
@ -35,6 +35,7 @@ input[type="file"] {
|
||||||
}
|
}
|
||||||
input[type="radio"] {
|
input[type="radio"] {
|
||||||
-webkit-appearance: radio;
|
-webkit-appearance: radio;
|
||||||
|
appearance: radio;
|
||||||
min-height: inherit;
|
min-height: inherit;
|
||||||
}
|
}
|
||||||
input[type="text"],
|
input[type="text"],
|
||||||
|
@ -96,7 +97,7 @@ input[type="email"]:disabled,
|
||||||
textarea:disabled {
|
textarea:disabled {
|
||||||
background-color: #dcdcdc;
|
background-color: #dcdcdc;
|
||||||
border-color: #bfbfbf;
|
border-color: #bfbfbf;
|
||||||
color: #8c8c8c;
|
color: #222;
|
||||||
-webkit-touch-callout: none;
|
-webkit-touch-callout: none;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
@ -110,7 +111,7 @@ select.inline {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
option[disabled] {
|
option[disabled] {
|
||||||
color: #8c8c8c;
|
color: #222;
|
||||||
}
|
}
|
||||||
textarea {
|
textarea {
|
||||||
height: 150px;
|
height: 150px;
|
||||||
|
@ -255,13 +256,13 @@ label {
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
label.form-error {
|
label.form-error {
|
||||||
color: #ba1212;
|
color: #d32f2f;
|
||||||
}
|
}
|
||||||
input::-webkit-input-placeholder,
|
input::-webkit-input-placeholder,
|
||||||
textarea::-webkit-input-placeholder,
|
textarea::-webkit-input-placeholder,
|
||||||
input::-moz-placeholder,
|
input::-moz-placeholder,
|
||||||
textarea::-moz-placeholder {
|
textarea::-moz-placeholder {
|
||||||
color: #8c8c8c;
|
color: #333 !important;
|
||||||
}
|
}
|
||||||
.edit-controls,
|
.edit-controls,
|
||||||
.add-controls {
|
.add-controls {
|
||||||
|
@ -316,6 +317,7 @@ textarea::-moz-placeholder {
|
||||||
border-left: 2px solid transparent;
|
border-left: 2px solid transparent;
|
||||||
transform: rotate(40deg);
|
transform: rotate(40deg);
|
||||||
-webkit-backface-visibility: hidden;
|
-webkit-backface-visibility: hidden;
|
||||||
|
backface-visibility: hidden;
|
||||||
transform-origin: 100% 100%;
|
transform-origin: 100% 100%;
|
||||||
}
|
}
|
||||||
.button-link {
|
.button-link {
|
||||||
|
@ -376,14 +378,14 @@ textarea::-moz-placeholder {
|
||||||
.button-link.setting.disabled {
|
.button-link.setting.disabled {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-color: #e9e9e9;
|
border-color: #e9e9e9;
|
||||||
color: #8c8c8c;
|
color: #222;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
.button-link.setting.disabled select {
|
.button-link.setting.disabled select {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.button-link.setting.disabled:hover .label {
|
.button-link.setting.disabled:hover .label {
|
||||||
color: #8c8c8c;
|
color: #222;
|
||||||
}
|
}
|
||||||
.button-link.setting.disabled,
|
.button-link.setting.disabled,
|
||||||
.button-link.setting.disabled:hover,
|
.button-link.setting.disabled:hover,
|
||||||
|
@ -399,7 +401,7 @@ textarea::-moz-placeholder {
|
||||||
color: #a8a8a8;
|
color: #a8a8a8;
|
||||||
}
|
}
|
||||||
.button-link.setting .label {
|
.button-link.setting .label {
|
||||||
color: #8c8c8c;
|
color: #222;
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 14px;
|
line-height: 14px;
|
||||||
|
@ -509,7 +511,7 @@ button.loud-text-button:hover {
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
color: #8c8c8c;
|
color: #222;
|
||||||
display: block;
|
display: block;
|
||||||
margin: 2px 0;
|
margin: 2px 0;
|
||||||
padding: 6px 8px;
|
padding: 6px 8px;
|
||||||
|
@ -574,7 +576,7 @@ button.loud-text-button:hover {
|
||||||
top: 6px;
|
top: 6px;
|
||||||
}
|
}
|
||||||
.big-link.none {
|
.big-link.none {
|
||||||
color: #8c8c8c;
|
color: #222;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
.big-link.none:hover {
|
.big-link.none:hover {
|
||||||
|
@ -604,7 +606,7 @@ button.loud-text-button:hover {
|
||||||
}
|
}
|
||||||
.show-more {
|
.show-more {
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
color: #8c8c8c;
|
color: #222;
|
||||||
display: block;
|
display: block;
|
||||||
padding: 16px 8px 16px 40px;
|
padding: 16px 8px 16px 40px;
|
||||||
margin: 8px 0;
|
margin: 8px 0;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
html(lang="{{TAPi18n.getLanguage()}}")
|
||||||
head
|
head
|
||||||
title
|
title
|
||||||
meta(name="viewport" content="width=device-width, initial-scale=1")
|
meta(name="viewport" content="width=device-width, initial-scale=1")
|
||||||
|
@ -48,6 +49,16 @@ template(name="userFormsLayout")
|
||||||
if isLoading
|
if isLoading
|
||||||
+loader
|
+loader
|
||||||
else
|
else
|
||||||
|
// ARIA live region for error messages
|
||||||
|
div#login-error-message(role="alert" aria-live="assertive" style="color: #d32f2f; margin-bottom: 1em;")
|
||||||
|
// Add autocomplete attribute to login input for WCAG compliance
|
||||||
|
script.
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
var loginInput = document.querySelector('input[type="text"], input[type="email"]');
|
||||||
|
if (loginInput && loginInput.name && (loginInput.name.toLowerCase().includes('user') || loginInput.name.toLowerCase().includes('email'))) {
|
||||||
|
loginInput.setAttribute('autocomplete', 'username email');
|
||||||
|
}
|
||||||
|
});
|
||||||
+Template.dynamic(template=content)
|
+Template.dynamic(template=content)
|
||||||
if currentSetting.displayAuthenticationMethod
|
if currentSetting.displayAuthenticationMethod
|
||||||
+connectionMethod(authenticationMethod=currentSetting.defaultAuthenticationMethod)
|
+connectionMethod(authenticationMethod=currentSetting.defaultAuthenticationMethod)
|
||||||
|
@ -59,7 +70,8 @@ template(name="userFormsLayout")
|
||||||
if getLegalNoticeWithWritTraduction
|
if getLegalNoticeWithWritTraduction
|
||||||
div
|
div
|
||||||
div.at-form-lang
|
div.at-form-lang
|
||||||
select.select-lang.js-userform-set-language
|
label(for="userform-set-language-select") {{_ 'choose_language'}}
|
||||||
|
select.select-lang.js-userform-set-language#userform-set-language-select(aria-label="{{_ 'choose_language'}}")
|
||||||
each languages
|
each languages
|
||||||
if isCurrentLanguage
|
if isCurrentLanguage
|
||||||
option(value="{{tag}}" selected="selected") {{name}}
|
option(value="{{tag}}" selected="selected") {{name}}
|
||||||
|
|
|
@ -57,6 +57,7 @@ AccountsTemplates.addFields([
|
||||||
displayName: 'username',
|
displayName: 'username',
|
||||||
required: true,
|
required: true,
|
||||||
minLength: 2,
|
minLength: 2,
|
||||||
|
autocomplete: 'username',
|
||||||
},
|
},
|
||||||
emailField,
|
emailField,
|
||||||
passwordField,
|
passwordField,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue