Skip to content

Commit

Permalink
A11y: optimize keyboard navigation for manager main nav modxcms#16612
Browse files Browse the repository at this point in the history
  • Loading branch information
jenswittmann authored and opengeek committed Sep 23, 2024
1 parent 34909bb commit d3b4f32
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 41 deletions.
8 changes: 6 additions & 2 deletions _build/templates/default/sass/_a11y.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.ext-webkit *:focus{
outline: auto !important;
.ext-webkit {
#modx-header {
*:focus-visible {
outline: auto !important;
}
}
}
9 changes: 7 additions & 2 deletions _build/templates/default/sass/_navbar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
background: $colorSplash;
max-width: $navbarWidth;
position: absolute;
z-index: 2;
z-index: 9999;
height: 100%;
}

Expand Down Expand Up @@ -184,6 +184,7 @@
}
}

#modx-header,
#modx-footer {
.modx-subnav {
border: 1px solid $navbarBorder;
Expand All @@ -194,7 +195,7 @@
box-sizing: border-box;
list-style: none;
position: absolute;
z-index: 10000;
z-index: 99999999999;
opacity: 0;
visibility: hidden;
transition: all .15s ease;
Expand Down Expand Up @@ -235,6 +236,7 @@
border-radius: $borderRadius;
background-color: $subnavBg;
color: $subnavTitleColor;
font-size: 13px;
font-weight: bold;
line-height: 1.5;
margin: 0;
Expand All @@ -244,6 +246,7 @@
display: block;
text-decoration: none;
cursor: pointer;
text-align: left;

.icon {
display: inline-block;
Expand Down Expand Up @@ -409,6 +412,7 @@
}
}

#modx-header,
#modx-footer {
.modx-subnav {
min-width: 300px;
Expand Down Expand Up @@ -458,6 +462,7 @@
}

@media (max-height: 520px) {
#modx-header,
#modx-footer {
.modx-subnav {
.description {
Expand Down
92 changes: 62 additions & 30 deletions manager/assets/modext/core/modx.layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,16 @@ Ext.extend(MODx.Layout, Ext.Viewport, {
*/
,splitBarMargin: 8

/**
* @property {Array} focusRestoreEls - Set Focus back on the last Element in array on close
*/
,focusRestoreEls: []

/**
* @property {bool} subNavOpen - Check if Subnav is opened
*/
,subNavOpen: false

/**
* @property {Function} getSplitBarMargin - Utility getter for splitBarMargin
* @returns {Number}
Expand Down Expand Up @@ -141,7 +151,7 @@ Ext.extend(MODx.Layout, Ext.Viewport, {
,xtype: 'box'
,id: 'modx-header'
,applyTo: 'modx-header'
,autoScroll: true
//,autoScroll: true
,width: this.menuBarWidth
,listeners: {
afterrender: { fn: this.initPopper, scope: this }
Expand Down Expand Up @@ -425,7 +435,6 @@ Ext.extend(MODx.Layout, Ext.Viewport, {
,initPopper: function() {
var el = this;
var buttons = document.getElementById('modx-navbar').getElementsByClassName('top');
var focusRestore = false;
var position = window.innerWidth <= 960 ? 'bottom' : 'right';
for (var i = 0; i < buttons.length; i++) {
var submenu = document.getElementById(buttons[i].id + '-submenu');
Expand Down Expand Up @@ -466,23 +475,17 @@ Ext.extend(MODx.Layout, Ext.Viewport, {
}
}
});
buttons[i].onclick = function(e) {
var _this = this;
var openSubnav =
buttons[i].addEventListener('click', function(e) {
e.stopPropagation();
focusRestore = this;
_this.focusRestoreEls.push(this.querySelectorAll('a')[0]);
el.showMenu(this);
};
});
}
window.addEventListener('click', function() {
el.hideMenu();
});
document.addEventListener('keydown', function(e) {
if (e.key == 'Escape') {
el.hideMenu();
setTimeout(() => {
focusRestore?.querySelectorAll('a')[0].focus();
});
}
});
if (window.innerWidth > 960) {
this.initSubPopper();
}
Expand All @@ -497,7 +500,20 @@ Ext.extend(MODx.Layout, Ext.Viewport, {
submenu.classList.add('active');
setTimeout(() => {
submenu.querySelectorAll('a')[0].focus();
}, 250);
}, 50);
var focusRestore = (e) => {
setTimeout(() => {
if (this.subNavOpen) {
return;
}
if (!submenu.contains(document.activeElement)) {
this.focusRestoreEls.pop()?.focus();
this.hideMenu();
window.removeEventListener('focusout', focusRestore);
}
}, 1);
};
window.addEventListener('focusout', focusRestore);
}
this.hideSubMenu();
}
Expand All @@ -507,10 +523,10 @@ Ext.extend(MODx.Layout, Ext.Viewport, {
submenus[i].classList.remove('active');
}
}

,initSubPopper: function () {
var buttons = document.getElementById('modx-footer').querySelectorAll('.sub');
var buttons = document.querySelectorAll('#modx-header .sub, #modx-footer .sub');
var position = window.innerWidth <= 960 ? 'bottom' : 'right';
var _this = this;
for (var i = 0; i < buttons.length; i++) {
let popperInstance = null;

Expand Down Expand Up @@ -552,34 +568,50 @@ Ext.extend(MODx.Layout, Ext.Viewport, {
}
}

function show(button, menu) {
function show(button) {
var menu = button.getElementsByTagName('ul')[0];
button.classList.add('active');
menu.classList.add('active');
_this.focusRestoreEls.push(button.querySelectorAll('a')[0]);
_this.subNavOpen = true;
create(button, menu);
var focusRestore = (e) => {
requestAnimationFrame(() => {
if (!menu.contains(document.activeElement)) {
hide(button);
window.removeEventListener('focusout', focusRestore);
_this.focusRestoreEls.pop()?.parentNode?.nextSibling?.focus();
}
});
};
window.addEventListener('focusout', focusRestore);
}

function hide(menu) {
var buttons = menu.querySelectorAll('.sub');
function hide(button) {
var parentmenu = button.closest('ul');
button.classList.remove('active');
var buttons = parentmenu.querySelectorAll('.sub');
for (var i = 0; i < buttons.length; i++) {
var submenu = buttons[i].getElementsByTagName('ul')[0];
submenu.classList.remove('active');
submenu.removeAttribute('style');
buttons[i].classList.remove('active');
}
_this.subNavOpen = false;
destroy();
}

buttons[i].onmouseenter = function (e) {
buttons[i].addEventListener('mouseenter', function (e) {
e.stopPropagation();
var submenu = this.getElementsByTagName('ul')[0];
this.classList.add('active');
show(this, submenu);
};
buttons[i].onmouseleave = function (e) {
show(this);
});
buttons[i].querySelectorAll('a')[0].addEventListener('focus', function (e) {
e.stopPropagation();
var parentmenu = this.closest('ul');
this.classList.remove('active');
hide(parentmenu);
};
show(this.parentNode);
});
buttons[i].addEventListener('mouseleave', function (e) {
e.stopPropagation();
hide(this);
});
}
}

Expand Down
4 changes: 2 additions & 2 deletions manager/controllers/default/header.php
Original file line number Diff line number Diff line change
Expand Up @@ -329,10 +329,10 @@ public function processSubMenus(&$output, array $menus = [])
$attributes = ' href="?a='.$menu['action'].$menu['params'].'"';
}
if (!empty($menu['handler'])) {
$attributes .= ' tabindex="0" onclick="{literal} '.str_replace('"','\'',$menu['handler']).'{/literal} "';
$attributes .= ' onclick="{literal} '.str_replace('"','\'',$menu['handler']).'{/literal} "';
}
$menu['icon'] = $menu['icon'] ?? '';
$smTpl .= '<a'.$attributes.'>'.$menu['text'].$menu['icon'].$description.'</a>'."\n";
$smTpl .= '<a'.$attributes.' tabindex="0">'.$menu['text'].$menu['icon'].$description.'</a>'."\n";

if (!empty($menu['children'])) {
$smTpl .= '<ul class="modx-subsubnav">'."\n";
Expand Down
4 changes: 1 addition & 3 deletions manager/templates/default/footer.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@
<div id="modx-manager-search" role="search"></div>
</div>
{/if}
{eval var=$navb_submenus}
{eval var=$userNav_submenus}
</div>
</div>
<!-- #modx-container -->

</body>
</html>
</html>
6 changes: 4 additions & 2 deletions manager/templates/default/header.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@

<div id="modx-browser"></div>
<div id="modx-container">
<div id="modx-header">
<section id="modx-header" aria-label="Navigation">
<div id="modx-navbar">
<ul id="modx-headnav">
<li id="modx-home-dashboard">
Expand All @@ -106,12 +106,14 @@
<ul id="modx-topnav">
{eval var=$navb}
</ul>
{eval var=$navb_submenus}
<ul id="modx-user-menu">
{* eval is used here to support nested variables *}
{eval var=$userNav}
</ul>
{eval var=$userNav_submenus}
</div>
</div>
</section>
{*<div id="modAB"></div>*}
<div id="modx-leftbar"></div>
<div id="modx-action-buttons-container"></div>
Expand Down

0 comments on commit d3b4f32

Please sign in to comment.