Skip to content

Commit

Permalink
misc: apply docs accessibility violation fixes (#1112)
Browse files Browse the repository at this point in the history
  • Loading branch information
lauzadis authored Jul 3, 2024
1 parent f22e4bd commit ea91fcd
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 1 deletion.
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ allprojects {
],
"customAssets": [
"${rootProject.file("docs/dokka-presets/assets/logo-icon.svg")}",
"${rootProject.file("docs/dokka-presets/assets/aws_logo_white_59x35.png")}"
"${rootProject.file("docs/dokka-presets/assets/aws_logo_white_59x35.png")}",
"${rootProject.file("docs/dokka-presets/scripts/accessibility.js")}"
],
"footerMessage": "© $year, Amazon Web Services, Inc. or its affiliates. All rights reserved.",
"separateInheritedMembers" : true,
Expand Down
29 changes: 29 additions & 0 deletions docs/dokka-presets/css/aws-styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,32 @@
div .compiler-info, .fold-button, .run-button {
display: none;
}

.skip-to-content {
width: 1px;
height: 1px;
overflow: hidden;
opacity: 0;
}

.skip-to-content:focus,
.skip-to-content:active {
width: auto;
height: auto;
opacity: 1;
z-index: 999; /* Ensure the skip link is on top of other content */
}

.aws-toggle-content-btn {
font-size: 24px;
background: none;
border: none;
cursor: pointer;
padding: 8px;
}

@media (max-width: 550px) {
.content[data-togglable] {
display: none;
}
}
144 changes: 144 additions & 0 deletions docs/dokka-presets/scripts/accessibility.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/**
* Apply "skip to main content" buttons after each active left sidebar `sideMenuPart`.
* These are invisible and only accessible via keyboard
* Fixes accessibility violation: "Provide a mechanism for skipping past repetitive content"
*/
function applySkipLinks() {
function insertSkipLink(element) {
if (element.querySelectorAll(".skip-to-content").length > 0) { return }

const skipLink = document.createElement('div');
// Create an anchor element with the href pointing to the main content
const anchor = document.createElement('a');
anchor.classList.add('skip-to-content');
anchor.href = '#content';
anchor.innerHTML = 'Skip to Main Content';
anchor.setAttribute("tabindex", "0");
skipLink.appendChild(anchor);
if (element.children.length > 1) {
element.insertBefore(skipLink, element.children[1]);
} else {
element.appendChild(skipLink);
}
}

function handleChanges(mutationsList) {
for (const mutation of mutationsList) {
if (mutation.type === 'attributes' && mutation.target.classList.contains('sideMenuPart') && !mutation.target.classList.contains('hidden')) {
insertSkipLink(mutation.target);
}
}

// Insert a skip link on all sideMenuParts with [data-active] property
document.querySelectorAll('.sideMenuPart[data-active]').forEach(function(sideMenuPart) {
insertSkipLink(sideMenuPart)
});
}

const observer = new MutationObserver(handleChanges);
const observerConfig = {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['class']
};
observer.observe(document.body, observerConfig);
}
document.addEventListener('DOMContentLoaded', applySkipLinks);
if (document.readyState === "interactive" || document.readyState === "complete" ) { applySkipLinks() }


/**
* Ensure `navButton` elements are interactable and have proper accessibility properties
* Fixes accessibilty violation: "Ensure all interactive functionality is operable with the keyboard"
*/
function ensureNavButtonInteractable() {
const navButtons = document.querySelectorAll('.navButton');

navButtons.forEach(function(navButton) {
// Make the navButton focusable, add accessibility information
navButton.setAttribute('tabindex', '0');
navButton.setAttribute('role', 'button');
navButton.setAttribute('aria-expanded', 'false');

// Grab the page ID, use it for aria-label and aria-controls
const sectionName = navButton.parentElement.parentElement.getAttribute('pageid')
// Remove the page ID suffix auto-generated by Dokka
const cleanedSectionName = sectionName.substring(0, sectionName.indexOf("////PointingToDeclaration"))
navButton.setAttribute('aria-label', cleanedSectionName);

const sectionID = navButton.parentElement.parentElement.id
navButton.setAttribute('aria-controls', sectionID);

// Add event listener for Enter and Space keys
navButton.addEventListener('keydown', function(event) {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault(); // Prevent the default action to avoid navigation
this.click(); // Trigger the onclick event
}
});

// Update aria-expanded attribute on click
navButton.addEventListener('click', function() {
const isExpanded = navButton.getAttribute('aria-expanded') === 'true';
navButton.setAttribute('aria-expanded', (!isExpanded).toString());
});
});
}

window.onload = function() {
ensureNavButtonInteractable()
}

//
/**
* Ensure that content (specifically, code blocks) reflows on small page sizes.
* Fixes accessibility violation: "Ensure pages reflow without requiring two-dimensional scrolling without loss of content or functionality"
*/
function ensureContentReflow() {
const MIN_WINDOW_SIZE = 550

// Function to insert 'toggle content' button
function insertToggleContentButton(element) {
const initiallyVisible = window.innerWidth >= MIN_WINDOW_SIZE

const toggleContent = document.createElement('button');
toggleContent.className = 'aws-toggle-content-btn';
toggleContent.textContent = initiallyVisible ? '▼' : '▶'
toggleContent.setAttribute('aria-expanded', initiallyVisible.toString());
toggleContent.setAttribute('aria-label', 'Toggle code block for' + element.getAttribute("data-togglable"));
toggleContent.setAttribute('aria-controls', element.id);

// Set initial visibility based on window size
element.style.display = initiallyVisible ? 'block' : 'none'

// Toggle visibility onclick
toggleContent.onclick = function() {
const isExpanded = toggleContent.getAttribute('aria-expanded') === 'true';
toggleContent.setAttribute('aria-expanded', (!isExpanded).toString());
element.style.display = isExpanded ? 'none' : 'block'
toggleContent.textContent = isExpanded ? '▶' : '▼'
};

element.parentNode.insertBefore(toggleContent, element);
}

document.querySelectorAll('.content[data-togglable]').forEach(insertToggleContentButton);

// Update content visibility on resize
window.addEventListener('resize', function() {
document.querySelectorAll('.content[data-togglable]').forEach(function(element) {
const toggleContent = element.previousSibling;
if (window.innerWidth < MIN_WINDOW_SIZE) {
element.style.display = 'none';
toggleContent.setAttribute('aria-expanded', 'false');
} else {
element.style.display = 'block';
toggleContent.setAttribute('aria-expanded', 'true');
}
});
});
}

document.addEventListener('DOMContentLoaded', ensureContentReflow)
if (document.readyState === "interactive" || document.readyState === "complete" ) { ensureContentReflow() }

0 comments on commit ea91fcd

Please sign in to comment.