From 61fad2786b9881350bf6f4209b0542b4c902caad Mon Sep 17 00:00:00 2001 From: Sorin Davidoi Date: Mon, 15 Jan 2018 14:26:53 +0100 Subject: [PATCH] Improve accessibility (#535) * fix(theme/index): Use nav element for Table of Content * fix(renderer/html_handlebars/helpers/toc): Use ol instead of ul Chapters and sections are ordered, so we should use the appropriate HTML tag. * fix(renderer/html_handlebars/helpers/toc): Hide section number from screen readers Screen readers have this functionality build-in, no need to present this. Ideally, this should not even be in the DOM tree, since the numbers can be shown by using CSS. * fix(theme/index): Remove tabIndex="-1" from .page Divs are not focusable by default * fix(theme): Make sidebar accessible Using aria-hidden (together with tabIndex) takes the links out of the tab order. http://heydonworks.com/practical_aria_examples/#progressive-collapsibles * fix(theme/index): Wrap content inside main tag The main tag helps users skip additional content on the page. * fix(theme/book): Don't focus .page on page load The main content is identified by the main tag, not by auto-focusing it on page load. * fix(theme/index): Make page controls accessible * fix: Make theme selector accessible - Use ul and li (since it is a list) - Add aria-expanded and aria-haspopup to the toggle button - Use button instead of div (buttons are accessible by default) - Handle Esc key (close popup) - Adjust CSS to keep same visual style * fix(theme/stylus/sidebar): Make link clickable area wider Links now expand to fill the entire row. * fix(theme): Wrap header buttons and improve animation performance Previously, the header had a fixed height, which meant that sometimes the print button was not visible. Animating the left property is expensive, which lead to laggy animations - transform is much cheaper and has the same effect. * fix(theme/stylus/theme-popup): Theme button inherits color Bug introduced while making the popup accessible * fix(theme/book): Handle edge case when toggling sidebar Bug introduced when switching from animating left to using transform. --- src/renderer/html_handlebars/helpers/toc.rs | 12 +-- src/theme/book.css | 85 +++++++++++++++------ src/theme/book.js | 55 +++++++------ src/theme/index.hbs | 60 ++++++++++----- src/theme/stylus/menu.styl | 15 ++-- src/theme/stylus/nav-icons.styl | 4 +- src/theme/stylus/page.styl | 3 - src/theme/stylus/sidebar.styl | 5 +- src/theme/stylus/theme-popup.styl | 7 ++ src/theme/stylus/themes/base.styl | 4 + 10 files changed, 162 insertions(+), 88 deletions(-) diff --git a/src/renderer/html_handlebars/helpers/toc.rs b/src/renderer/html_handlebars/helpers/toc.rs index 1cc971de..922a0db5 100644 --- a/src/renderer/html_handlebars/helpers/toc.rs +++ b/src/renderer/html_handlebars/helpers/toc.rs @@ -25,7 +25,7 @@ impl HelperDef for RenderToc { .ok_or_else(|| RenderError::new("Type error for `path`, string expected"))? .replace("\"", ""); - rc.writer.write_all(b"")?; + rc.writer.write_all(b"")?; rc.writer.write_all(b"")?; current_level -= 1; } - rc.writer.write_all(b"")?; + rc.writer.write_all(b"")?; Ok(()) } } diff --git a/src/theme/book.css b/src/theme/book.css index 607e6a12..755f7939 100644 --- a/src/theme/book.css +++ b/src/theme/book.css @@ -61,17 +61,21 @@ table thead td { -moz-box-sizing: border-box; box-sizing: border-box; -webkit-overflow-scrolling: touch; - -webkit-transition: left 0.5s; - -moz-transition: left 0.5s; - -o-transition: left 0.5s; - -ms-transition: left 0.5s; - transition: left 0.5s; + -webkit-transition: -webkit-transform 0.5s; + -moz-transition: -moz-transform 0.5s; + -o-transition: -o-transform 0.5s; + -ms-transition: -ms-transform 0.5s; + transition: transform 0.5s; } .sidebar code { line-height: 2em; } .sidebar-hidden .sidebar { - left: -300px; + -webkit-transform: translateX(-300px); + -moz-transform: translateX(-300px); + -o-transform: translateX(-300px); + -ms-transform: translateX(-300px); + transform: translateX(-300px); } .chapter { list-style: none outside none; @@ -79,6 +83,7 @@ table thead td { line-height: 2.2em; } .chapter li a { + display: block; padding: 5px 0; text-decoration: none; } @@ -105,8 +110,6 @@ table thead td { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; - min-height: 100%; - width: 100%; -webkit-transition: padding-left 0.5s, margin-left 0.5s; -moz-transition: padding-left 0.5s, margin-left 0.5s; -o-transition: padding-left 0.5s, margin-left 0.5s; @@ -143,7 +146,18 @@ table thead td { } .menu-bar { position: relative; - height: 50px; + display: -webkit-box; + display: -moz-box; + display: -webkit-flex; + display: -ms-flexbox; + display: box; + display: flex; + -webkit-box-lines: multiple; + -moz-box-lines: multiple; + -o-box-lines: multiple; + -webkit-flex-wrap: wrap; + -ms-flex-wrap: wrap; + flex-wrap: wrap; } .menu-bar i, .menu-bar .icon-button { @@ -161,24 +175,24 @@ table thead td { .menu-bar .icon-button:hover { cursor: pointer; } -.menu-bar .left-buttons { - float: left; -} -.menu-bar .right-buttons { - float: right; -} .menu-title { display: inline-block; font-weight: 200; font-size: 20px; line-height: 50px; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; text-align: center; margin: 0; + -webkit-box-flex: 1; + -moz-box-flex: 1; + -o-box-flex: 1; + box-flex: 1; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + white-space: nowrap; + overflow: hidden; + -o-text-overflow: ellipsis; + text-overflow: ellipsis; opacity: 0; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter: alpha(opacity=0); @@ -252,7 +266,7 @@ table thead td { right: 15px; } @media only screen and (max-width: 1080px) { - .nav-chapters { + .nav-wide-wrapper { display: none; } .nav-wrapper { @@ -260,7 +274,7 @@ table thead td { } } @media only screen and (max-width: 1380px) { - .sidebar-visible .nav-chapters { + .sidebar-visible .nav-wide-wrapper { display: none; } .sidebar-visible .nav-wrapper { @@ -275,11 +289,18 @@ table thead td { font-size: 0.7em; } .theme-popup .theme { + display: inline; + border: 0; margin: 0; padding: 2px 10px; line-height: 25px; + width: 100%; white-space: nowrap; + text-align: left; cursor: pointer; + color: inherit; + background: inherit; + font-size: inherit; } .theme-popup .theme:hover:first-child, .theme-popup .theme:hover:last-child { @@ -349,6 +370,10 @@ table thead td { color: #333; background: #fafafa; border: 1px solid #ccc; + margin: 0; + padding: 0; + list-style: none; + display: none; } .light .theme-popup .theme:hover { background-color: #e6e6e6; @@ -481,6 +506,10 @@ table thead td { color: #98a3ad; background: #141617; border: 1px solid #43484d; + margin: 0; + padding: 0; + list-style: none; + display: none; } .coal .theme-popup .theme:hover { background-color: #1f2124; @@ -613,6 +642,10 @@ table thead td { color: #bcbdd0; background: #161923; border: 1px solid #737480; + margin: 0; + padding: 0; + list-style: none; + display: none; } .navy .theme-popup .theme:hover { background-color: #282e40; @@ -745,6 +778,10 @@ table thead td { color: #262625; background: #e1e1db; border: 1px solid #b38f6b; + margin: 0; + padding: 0; + list-style: none; + display: none; } .rust .theme-popup .theme:hover { background-color: #99908a; @@ -877,6 +914,10 @@ table thead td { color: #c5c5c5; background: #14191f; border: 1px solid #5c6773; + margin: 0; + padding: 0; + list-style: none; + display: none; } .ayu .theme-popup .theme:hover { background-color: #191f26; diff --git a/src/theme/book.js b/src/theme/book.js index 10a42756..01cccb2a 100644 --- a/src/theme/book.js +++ b/src/theme/book.js @@ -38,7 +38,8 @@ $( document ).ready(function() { var KEY_CODES = { PREVIOUS_KEY: 37, - NEXT_KEY: 39 + NEXT_KEY: 39, + ESCAPE_KEY: 27, }; $(document).on('keydown', function (e) { @@ -56,15 +57,16 @@ $( document ).ready(function() { window.location.href = $('.nav-chapters.previous').attr('href'); } break; + case KEY_CODES.ESCAPE_KEY: + e.preventDefault(); + hideThemes(); + break; } }); // Interesting DOM Elements var sidebar = $("#sidebar"); - // Help keyboard navigation by always focusing on page content - $(".page").focus(); - // Toggle sidebar $("#sidebar-toggle").click(sidebarToggle); @@ -101,36 +103,37 @@ $( document ).ready(function() { } }); + function showThemes() { + $('.theme-popup').css('display', 'block'); + $('#theme-toggle').attr('aria-expanded', true); + } + + function hideThemes() { + $('.theme-popup').css('display', 'none'); + $('#theme-toggle').attr('aria-expanded', false); + } // Theme button $("#theme-toggle").click(function(){ - if($('.theme-popup').length) { - $('.theme-popup').remove(); + if ($('.theme-popup').css('display') === 'block') { + hideThemes(); } else { - var popup = $('
') - .append($('
Light (default)
')) - .append($('
Rust
')) - .append($('
Coal
')) - .append($('')) - .append($('
Ayu
')); - - - popup.insertAfter(this); - - $('.theme').click(function(){ - var theme = $(this).attr('id'); - set_theme(theme); - }); + showThemes(); } }); + $('.theme').click(function(){ + var theme = $(this).attr('id'); + set_theme(theme); + }); + // Hide theme selector popup when clicking outside of it $(document).click(function(event){ var popup = $('.theme-popup'); - if(popup.length) { + if(popup.css('display') === 'block') { var target = $(event.target); if(!target.closest('.theme').length && !target.closest('#theme-toggle').length) { - popup.remove(); + hideThemes(); } } }); @@ -375,7 +378,7 @@ function sidebarToggle() { } else if (html.hasClass("sidebar-visible")) { hideSidebar(); } else { - if ($("#sidebar").position().left === 0){ + if (getComputedStyle($('#sidebar')[0])['transform'] === 'none'){ hideSidebar(); } else { showSidebar(); @@ -385,11 +388,17 @@ function sidebarToggle() { function showSidebar() { $('html').removeClass('sidebar-hidden').addClass('sidebar-visible'); + $('#sidebar a').attr('tabIndex', 0); + $('#sidebar-toggle').attr('aria-expanded', true); + $('#sidebar').attr('aria-hidden', false); store.set('mdbook-sidebar', 'visible'); } function hideSidebar() { $('html').removeClass('sidebar-visible').addClass('sidebar-hidden'); + $('#sidebar a').attr('tabIndex', -1); + $('#sidebar-toggle').attr('aria-expanded', false); + $('#sidebar').attr('aria-hidden', true); store.set('mdbook-sidebar', 'hidden'); } diff --git a/src/theme/index.hbs b/src/theme/index.hbs index a44bd10c..634bf9a0 100644 --- a/src/theme/index.hbs +++ b/src/theme/index.hbs @@ -68,22 +68,29 @@ $("html").addClass("sidebar-" + sidebar); - +
-
+
{{> header}}
-
- {{{ content }}} + + -
- {{#previous}} - - {{/previous}} +
diff --git a/src/theme/stylus/menu.styl b/src/theme/stylus/menu.styl index ee4bfcb2..65ec4fba 100644 --- a/src/theme/stylus/menu.styl +++ b/src/theme/stylus/menu.styl @@ -1,6 +1,7 @@ .menu-bar { position: relative - height: 50px + display: flex + flex-wrap: wrap i, .icon-button { position: relative @@ -12,9 +13,6 @@ &:hover { cursor: pointer } } - - .left-buttons { float: left } - .right-buttons { float: right } } .menu-title { @@ -22,13 +20,12 @@ font-weight: 200 font-size: 20px line-height: 50px - position: absolute - top: 0 - left: 0 - right: 0 - bottom: 0 text-align: center margin: 0 + flex: 1 + white-space: nowrap + overflow: hidden + text-overflow: ellipsis opacity: 0 transition: opacity 0.5s ease-in-out diff --git a/src/theme/stylus/nav-icons.styl b/src/theme/stylus/nav-icons.styl index d756a8f8..5d967a54 100644 --- a/src/theme/stylus/nav-icons.styl +++ b/src/theme/stylus/nav-icons.styl @@ -43,13 +43,13 @@ } @media only screen and (max-width: $page-plus-sidebar-width) { - .nav-chapters { display: none } + .nav-wide-wrapper { display: none } .nav-wrapper { display: block } } @media only screen and (max-width: $page-plus-sidebar-width + $sidebar-width) { .sidebar-visible { - .nav-chapters { display: none } + .nav-wide-wrapper { display: none } .nav-wrapper { display: block } } } diff --git a/src/theme/stylus/page.styl b/src/theme/stylus/page.styl index 8ba72245..c95989c2 100644 --- a/src/theme/stylus/page.styl +++ b/src/theme/stylus/page.styl @@ -3,9 +3,6 @@ .page-wrapper { box-sizing: border-box - min-height: 100% - width: 100% - // Animation: slide away transition: padding-left 0.5s, margin-left 0.5s } diff --git a/src/theme/stylus/sidebar.styl b/src/theme/stylus/sidebar.styl index a3a15b37..2cacd0e2 100644 --- a/src/theme/stylus/sidebar.styl +++ b/src/theme/stylus/sidebar.styl @@ -13,7 +13,7 @@ -webkit-overflow-scrolling: touch // Animation: slide away - transition: left 0.5s + transition: transform 0.5s code { line-height: 2em; @@ -21,7 +21,7 @@ } .sidebar-hidden .sidebar { - left: - $sidebar-width + transform: translateX(- $sidebar-width) } .chapter { @@ -30,6 +30,7 @@ line-height: 2.2em li a { + display: block; padding: 5px 0 text-decoration: none diff --git a/src/theme/stylus/theme-popup.styl b/src/theme/stylus/theme-popup.styl index fc51bc35..b0c956e6 100644 --- a/src/theme/stylus/theme-popup.styl +++ b/src/theme/stylus/theme-popup.styl @@ -8,11 +8,18 @@ font-size: 0.7em .theme { + display: inline + border: 0 margin: 0 padding: 2px 10px line-height: 25px + width: 100% white-space: nowrap + text-align: left cursor: pointer + color inherit + background: inherit; + font-size: inherit; &:hover:first-child, &:hover:last-child { diff --git a/src/theme/stylus/themes/base.styl b/src/theme/stylus/themes/base.styl index 8f308d5e..727a9abd 100644 --- a/src/theme/stylus/themes/base.styl +++ b/src/theme/stylus/themes/base.styl @@ -67,6 +67,10 @@ color: $fg background: $theme-popup-bg border: 1px solid $theme-popup-border + margin: 0; + padding: 0; + list-style: none; + display: none; .theme:hover { background-color: $theme-hover }