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.
This commit is contained in:
parent
9ab54412ea
commit
61fad2786b
|
@ -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"<ul class=\"chapter\">")?;
|
||||
rc.writer.write_all(b"<ol class=\"chapter\">")?;
|
||||
|
||||
let mut current_level = 1;
|
||||
|
||||
|
@ -45,13 +45,13 @@ impl HelperDef for RenderToc {
|
|||
if level > current_level {
|
||||
while level > current_level {
|
||||
rc.writer.write_all(b"<li>")?;
|
||||
rc.writer.write_all(b"<ul class=\"section\">")?;
|
||||
rc.writer.write_all(b"<ol class=\"section\">")?;
|
||||
current_level += 1;
|
||||
}
|
||||
rc.writer.write_all(b"<li>")?;
|
||||
} else if level < current_level {
|
||||
while level < current_level {
|
||||
rc.writer.write_all(b"</ul>")?;
|
||||
rc.writer.write_all(b"</ol>")?;
|
||||
rc.writer.write_all(b"</li>")?;
|
||||
current_level -= 1;
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ impl HelperDef for RenderToc {
|
|||
if !self.no_section_label {
|
||||
// Section does not necessarily exist
|
||||
if let Some(section) = item.get("section") {
|
||||
rc.writer.write_all(b"<strong>")?;
|
||||
rc.writer.write_all(b"<strong aria-hidden=\"true\">")?;
|
||||
rc.writer.write_all(section.as_bytes())?;
|
||||
rc.writer.write_all(b"</strong> ")?;
|
||||
}
|
||||
|
@ -129,12 +129,12 @@ impl HelperDef for RenderToc {
|
|||
rc.writer.write_all(b"</li>")?;
|
||||
}
|
||||
while current_level > 1 {
|
||||
rc.writer.write_all(b"</ul>")?;
|
||||
rc.writer.write_all(b"</ol>")?;
|
||||
rc.writer.write_all(b"</li>")?;
|
||||
current_level -= 1;
|
||||
}
|
||||
|
||||
rc.writer.write_all(b"</ul>")?;
|
||||
rc.writer.write_all(b"</ol>")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 = $('<div class="theme-popup"></div>')
|
||||
.append($('<div class="theme" id="light">Light <span class="default">(default)</span><div>'))
|
||||
.append($('<div class="theme" id="rust">Rust</div>'))
|
||||
.append($('<div class="theme" id="coal">Coal</div>'))
|
||||
.append($('<div class="theme" id="navy">Navy</div>'))
|
||||
.append($('<div class="theme" id="ayu">Ayu</div>'));
|
||||
|
||||
|
||||
popup.insertAfter(this);
|
||||
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');
|
||||
}
|
||||
|
||||
|
|
|
@ -68,22 +68,29 @@
|
|||
$("html").addClass("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<div id="sidebar" class="sidebar">
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
{{#toc}}{{/toc}}
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page" tabindex="-1">
|
||||
<div class="page">
|
||||
{{> header}}
|
||||
<div id="menu-bar" class="menu-bar">
|
||||
<div class="left-buttons">
|
||||
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle sidebar">
|
||||
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</button>
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme">
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="submenu">
|
||||
<li><button class="theme" id="light">Light <span class="default">(default)</span></button></li>
|
||||
<li><button class="theme" id="rust">Rust</button></li>
|
||||
<li><button class="theme" id="coal">Coal</button></li>
|
||||
<li><button class="theme" id="navy">Navy</button></li>
|
||||
<li><button class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">{{ book_title }}</h1>
|
||||
|
@ -95,39 +102,50 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div id="content" class="content">
|
||||
{{{ content }}}
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script type="text/javascript">
|
||||
$('#sidebar-toggle').attr('aria-expanded', sidebar === 'visible');
|
||||
$('#sidebar').attr('aria-hidden', sidebar !== 'visible');
|
||||
$('#sidebar a').attr('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
</script>
|
||||
|
||||
<div class="nav-wrapper">
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
{{{ content }}}
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
{{#previous}}
|
||||
<a rel="prev" href="{{link}}" class="mobile-nav-chapters previous" title="Previous chapter">
|
||||
<a rel="prev" href="{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
{{/previous}}
|
||||
|
||||
{{#next}}
|
||||
<a rel="next" href="{{link}}" class="mobile-nav-chapters next" title="Next chapter">
|
||||
<a rel="next" href="{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
{{/next}}
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
{{#previous}}
|
||||
<a href="{{link}}" class="nav-chapters previous" title="You can navigate through the chapters using the arrow keys">
|
||||
<a href="{{link}}" class="nav-chapters previous" title="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
{{/previous}}
|
||||
|
||||
{{#next}}
|
||||
<a href="{{link}}" class="nav-chapters next" title="You can navigate through the chapters using the arrow keys">
|
||||
<a href="{{link}}" class="nav-chapters next" title="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
{{/next}}
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 }
|
||||
|
||||
|
|
Loading…
Reference in New Issue