Fix ARIA labels for all anchors with href="#"; adds aria-pressed information for toggles (#1262)

This follows up from #1259 and closes #1261. Basically, this PR accomplishes the two items discussed in the issue:

1. for all anchors that are *actually* buttons (i.e., have `href="#"`), I've replaced them with a semantic `<button>`
    - under the hood, I've made a `.btn-reset` class pulling out the reset from #1259, so there's no visual change
2. for anchors that are ["toggle buttons"](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/button_role#toggle_buttons) (the mobile menu nav, sidebar children/grandchildren toggles), I've added an `aria-pressed` property that is updated as the button is clicked

I've also slightly modified some of the `aria-label`s to make them more consistent. Observe that we *shouldn't* update these as the button is clicked; screen readers use the `aria-pressed` property to add an annotation to each button.

To test this,

- the sidebar children and grandchildren can be done on the deploy preview:
    - open an arbitrary page; observe that the sidebar children/grandchildren dropdown ticks now have a proper `aria-label` and `aria-pressed`, as well as otherwise work as intended
    - toggle one of the buttons; observe the `aria-pressed` role changing as this is done
    - open a grandchild page; observe that the `aria-pressed` has a correct default wrt whether or not the page is active
- the mobile menu can be done on the deploy preview; on a smaller viewport, observe the correct `aria-pressed`
- two features require local changes to test:
    - the `site.search.button` needs to be enabled in the `_config.yml`. To test this, locally clone the repo, change the flag, and observe that the button still works as intended + has no visual regressions.
    - the collections feature is a bit more complicated. To test this, locally clone the repo, add an arbitrary collection and changes to `_config.yml`, and observe the same behaviour for the sidebar children/grandchildren above
This commit is contained in:
Matt Wang 2023-06-06 19:34:42 -07:00 committed by GitHub
parent d0cc9a0ff4
commit 0ca69334b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 43 additions and 36 deletions

View File

@ -1,7 +1,7 @@
{% if site.search.button %} {% if site.search.button %}
<a href="#" id="search-button" class="search-button"> <button id="search-button" class="search-button btn-reset" aria-label="Focus on search">
<svg viewBox="0 0 24 24" class="icon"><use xlink:href="#svg-search"></use></svg> <svg viewBox="0 0 24 24" class="icon" aria-hidden="true"><use xlink:href="#svg-search"></use></svg>
</a> </button>
{% endif %} {% endif %}
<div class="search-overlay"></div> <div class="search-overlay"></div>

View File

@ -1,7 +1,7 @@
<div class="side-bar"> <div class="side-bar">
<div class="site-header" role="banner"> <div class="site-header" role="banner">
<a href="{{ '/' | relative_url }}" class="site-title lh-tight">{% include title.html %}</a> <a href="{{ '/' | relative_url }}" class="site-title lh-tight">{% include title.html %}</a>
<button id="menu-button" class="site-button" aria-label="Open menu"> <button id="menu-button" class="site-button btn-reset" aria-label="Toggle menu" aria-pressed="false">
<svg viewBox="0 0 24 24" class="icon" aria-hidden="true"><use xlink:href="#svg-menu"></use></svg> <svg viewBox="0 0 24 24" class="icon" aria-hidden="true"><use xlink:href="#svg-menu"></use></svg>
</a> </a>
</div> </div>
@ -38,7 +38,9 @@
<ul class="nav-list nav-category-list"> <ul class="nav-list nav-category-list">
<li class="nav-list-item{% if page.collection == collection_key %} active{% endif %}"> <li class="nav-list-item{% if page.collection == collection_key %} active{% endif %}">
{%- if collection.size > 0 -%} {%- if collection.size > 0 -%}
<a href="#" class="nav-list-expander"><svg viewBox="0 0 24 24"><use xlink:href="#svg-arrow-right"></use></svg></a> <button class="nav-list-expander btn-reset" aria-label="Toggle collection {{ collection_value.name }}" aria-pressed="{% if page.collection == collection_key %}true{% else %}false{% endif %}">
<svg viewBox="0 0 24 24" aria-hidden="true"><use xlink:href="#svg-arrow-right"></use></svg>
</button>
{%- endif -%} {%- endif -%}
<div class="nav-category">{{ collection_value.name }}</div> <div class="nav-category">{{ collection_value.name }}</div>
{% include nav.html pages=collection key=collection_key %} {% include nav.html pages=collection key=collection_key %}

View File

@ -4,7 +4,7 @@
Results in: HTML for the navigation panel. Results in: HTML for the navigation panel.
Includes: Includes:
sorted_pages.html sorted_pages.html
Overwrites: Overwrites:
nav_pages, first_level_pages, second_level_pages, third_level_pages, nav_pages, first_level_pages, second_level_pages, third_level_pages,
node, children_list, child, grand_children_list, grand_child. node, children_list, child, grand_children_list, grand_child.
{%- endcomment -%} {%- endcomment -%}
@ -26,29 +26,29 @@
{%- comment -%} {%- comment -%}
The order of sibling pages in `sorted_pages` determines the order of display of The order of sibling pages in `sorted_pages` determines the order of display of
links to them in lists of navigation links. links to them in lists of navigation links.
Note that Liquid evaluates conditions from right to left (and it does not allow Note that Liquid evaluates conditions from right to left (and it does not allow
the use of parentheses). Some conditions are not so easy to express clearly... the use of parentheses). Some conditions are not so easy to express clearly...
For example, consider the following condition: For example, consider the following condition:
C: page.collection = = include.key and C: page.collection = = include.key and
page.url = = node.url or page.url = = node.url or
page.grand_parent = = node.title or page.grand_parent = = node.title or
page.parent = = node.title and page.parent = = node.title and
page.grand_parent = = nil page.grand_parent = = nil
Here, `node` is a first-level page. The last part of the condition Here, `node` is a first-level page. The last part of the condition
-- namely: `page.parent = = node.title and page.grand_parent = = nil` -- -- namely: `page.parent = = node.title and page.grand_parent = = nil` --
is evaluated first; it holds if and only if `page` is a child of `node`. is evaluated first; it holds if and only if `page` is a child of `node`.
The condition `page.grand_parent = = node.title or ...` holds when The condition `page.grand_parent = = node.title or ...` holds when
`page` is a grandchild of node, OR `...` holds. `page` is a grandchild of node, OR `...` holds.
The condition `page.url = = node.url or ...` holds when The condition `page.url = = node.url or ...` holds when
`page` is `node`, OR `...` holds. `page` is `node`, OR `...` holds.
The condition C: `page.collection = = include.key and ...` holds when we are The condition C: `page.collection = = include.key and ...` holds when we are
generating the nav links for a collection that includes `page`, AND `...` holds. generating the nav links for a collection that includes `page`, AND `...` holds.
{%- endcomment -%} {%- endcomment -%}
@ -57,9 +57,9 @@
{%- unless node.nav_exclude -%} {%- unless node.nav_exclude -%}
<li class="nav-list-item{% if page.collection == include.key and page.url == node.url or page.grand_parent == node.title or page.parent == node.title and page.grand_parent == nil %} active{% endif %}"> <li class="nav-list-item{% if page.collection == include.key and page.url == node.url or page.grand_parent == node.title or page.parent == node.title and page.grand_parent == nil %} active{% endif %}">
{%- if node.has_children -%} {%- if node.has_children -%}
<a href="#" class="nav-list-expander" aria-label="toggle links in {{ node.title }} category"> <button class="nav-list-expander btn-reset" aria-label="toggle items in {{ node.title }} category" aria-pressed="{% if page.collection == include.key and page.url == node.url or page.grand_parent == node.title or page.parent == node.title and page.grand_parent == nil %}true{% else %}false{% endif %}">
<svg viewBox="0 0 24 24"><use xlink:href="#svg-arrow-right"></use></svg> <svg viewBox="0 0 24 24" aria-hidden="true"><use xlink:href="#svg-arrow-right"></use></svg>
</a> </button>
{%- endif -%} {%- endif -%}
<a href="{{ node.url | relative_url }}" class="nav-list-link{% if page.url == node.url %} active{% endif %}">{{ node.title }}</a> <a href="{{ node.url | relative_url }}" class="nav-list-link{% if page.url == node.url %} active{% endif %}">{{ node.title }}</a>
{%- if node.has_children -%} {%- if node.has_children -%}
@ -73,9 +73,9 @@
{%- unless child.nav_exclude -%} {%- unless child.nav_exclude -%}
<li class="nav-list-item {% if page.url == child.url or page.parent == child.title %} active{% endif %}"> <li class="nav-list-item {% if page.url == child.url or page.parent == child.title %} active{% endif %}">
{%- if child.has_children -%} {%- if child.has_children -%}
<a href="#" class="nav-list-expander" aria-label="toggle links in {{ child.title }} category"> <button class="nav-list-expander btn-reset" aria-label="toggle items in {{ child.title }} category" aria-pressed="{% if page.url == child.url or page.parent == child.title %}true{% else %}false{% endif %}">
<svg viewBox="0 0 24 24"><use xlink:href="#svg-arrow-right"></use></svg> <svg viewBox="0 0 24 24" aria-hidden="true"><use xlink:href="#svg-arrow-right"></use></svg>
</a> </button>
{%- endif -%} {%- endif -%}
<a href="{{ child.url | relative_url }}" class="nav-list-link{% if page.url == child.url %} active{% endif %}">{{ child.title }}</a> <a href="{{ child.url | relative_url }}" class="nav-list-link{% if page.url == child.url %} active{% endif %}">{{ child.title }}</a>
{%- if child.has_children -%} {%- if child.has_children -%}

View File

@ -111,3 +111,13 @@
.btn-green { .btn-green {
@include btn-color($white, $green-100); @include btn-color($white, $green-100);
} }
.btn-reset {
background: none;
border: none;
margin: 0;
text-align: inherit;
font: inherit;
border-radius: 0;
appearance: none;
}

View File

@ -145,13 +145,6 @@
height: 100%; height: 100%;
padding: $gutter-spacing-sm; padding: $gutter-spacing-sm;
align-items: center; align-items: center;
background-color: transparent;
border: none;
margin: 0;
text-align: inherit;
font: inherit;
border-radius: 0;
appearance: none;
} }
@include mq(md) { @include mq(md) {

View File

@ -31,7 +31,7 @@ function initNav() {
} }
if (target) { if (target) {
e.preventDefault(); e.preventDefault();
target.parentNode.classList.toggle('active'); target.ariaPressed = target.parentNode.classList.toggle('active');
} }
}); });
@ -45,9 +45,11 @@ function initNav() {
if (menuButton.classList.toggle('nav-open')) { if (menuButton.classList.toggle('nav-open')) {
siteNav.classList.add('nav-open'); siteNav.classList.add('nav-open');
mainHeader.classList.add('nav-open'); mainHeader.classList.add('nav-open');
menuButton.ariaPressed = true;
} else { } else {
siteNav.classList.remove('nav-open'); siteNav.classList.remove('nav-open');
mainHeader.classList.remove('nav-open'); mainHeader.classList.remove('nav-open');
menuButton.ariaPressed = false;
} }
}); });
@ -491,8 +493,8 @@ jtd.onReady(function(){
if (!window.isSecureContext) { if (!window.isSecureContext) {
console.log('Window does not have a secure context, therefore code clipboard copy functionality will not be available. For more details see https://web.dev/async-clipboard/#security-and-permissions'); console.log('Window does not have a secure context, therefore code clipboard copy functionality will not be available. For more details see https://web.dev/async-clipboard/#security-and-permissions');
return; return;
} }
var codeBlocks = document.querySelectorAll('div.highlighter-rouge, div.listingblock > div.content, figure.highlight'); var codeBlocks = document.querySelectorAll('div.highlighter-rouge, div.listingblock > div.content, figure.highlight');
// note: the SVG svg-copied and svg-copy is only loaded as a Jekyll include if site.enable_copy_code_button is true; see _includes/icons/icons.html // note: the SVG svg-copied and svg-copy is only loaded as a Jekyll include if site.enable_copy_code_button is true; see _includes/icons/icons.html