Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Apply accessibility violation fixes #1344

Merged
merged 11 commits into from
Jul 3, 2024
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,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 @@ -62,4 +62,33 @@
*/
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: 320px) {
lauzadis marked this conversation as resolved.
Show resolved Hide resolved
.content[data-togglable] {
display: none;
}
}
145 changes: 145 additions & 0 deletions docs/dokka-presets/scripts/accessibility.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/**
* Apply "skip to main content" buttons after each active left sidebar `sideMenuPart`.
* These are invisible and only accessible via keyboard
* Fix for accessibility violation: "Provide a mechanism for skipping past repetitive content"
*/
function applySkipLinks() {
console.log("DOMContentLoaded")
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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: do we have elements with 0 children?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the leaf nodes in our documentation have 1 child which is what this condition matches.

The structure for this condition is like this:

<div sideMenuPart>
	<div overview>
	<div skip-to-main-content> -- added via appendChild

The other condition is for structures like like this:

<div sideMenuPart>
	<div overview>
	<div skip-to-main-content> -- added via insertBefore element.children[1]
	<div child sideMenuPart>
		<div overview>
	<div child sideMenuPart>
		<div overview>
	<div child sideMenuPart>
		<div overview>

}
}

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
* Fix for 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()
}

//
lauzadis marked this conversation as resolved.
Show resolved Hide resolved
/**
* Ensure that content (specifically, code blocks) reflows on small page sizes.
* Fix for accessibility violation: "Ensure pages reflow without requiring two-dimensional scrolling without loss of content or functionality"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: it's a bit easier to read if we say Fixes accessibility violation: ...

*/
function ensureContentReflow() {
const MIN_WINDOW_SIZE = 550

// Function to insert 'toggle content' button
function insertToggleContentButton(element) {
const toggleContent = document.createElement('button');
toggleContent.className = 'aws-toggle-content-btn';
toggleContent.textContent = '☰';
lauzadis marked this conversation as resolved.
Show resolved Hide resolved

const initiallyVisible = window.innerWidth >= MIN_WINDOW_SIZE

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'
};

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" ) { applySkipLinks() }
lauzadis marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion docs/dokka-presets/templates/base.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@
</div>
</div>
</body>
</html>
</html>
Loading