diff --git a/config.toml b/config.toml index 9a09282..90f0f39 100644 --- a/config.toml +++ b/config.toml @@ -96,6 +96,9 @@ show_remote_changes = true # Defaults to true. # Show a link to the repository of the site, right next to the "Powered by Zola & tabi" text. show_remote_source = true # Defaults to true. +# Load JavaScript to improve accessibility. +accessibility_javascript = true + # Add a "copy" button to codeblocks (loads ~700 bytes of JavaScript). # Can be set at page or section levels, following the hierarchy: page > section > config. See: https://welpo.github.io/tabi/blog/mastering-tabi-settings/#settings-hierarchy copy_button = true diff --git a/static/js/accessibility.min.js b/static/js/accessibility.min.js new file mode 100644 index 0000000..2248c2a --- /dev/null +++ b/static/js/accessibility.min.js @@ -0,0 +1,52 @@ +document.addEventListener("DOMContentLoaded", function () { + initDropdownMenu(); +}); + +function initDropdownMenu() { + const dropdown = document.querySelector('.dropdown'); + const menuItems = document.querySelectorAll('[role="menuitem"]'); + const menuButton = dropdown.querySelector('[role="button"]'); + + dropdown.addEventListener("toggle", handleToggle.bind(null, dropdown, menuItems, menuButton)); + document.addEventListener("keydown", handleKeydown.bind(null, dropdown, menuItems, menuButton)); +} + +function handleToggle(dropdown, menuItems, menuButton) { + if (dropdown.hasAttribute('open')) { + focusMenuItem(menuItems, 0); + setAriaExpanded(menuButton, true); + } else { + menuButton.focus(); + setAriaExpanded(menuButton, false); + } +} + +function handleKeydown(dropdown, menuItems, menuButton, event) { + if (!dropdown.hasAttribute('open')) return; + + let focusedItemIndex = Array.from(menuItems).indexOf(document.activeElement); + + switch (event.key) { + case "ArrowDown": + event.preventDefault(); + focusMenuItem(menuItems, (focusedItemIndex + 1) % menuItems.length); + break; + case "ArrowUp": + event.preventDefault(); + focusMenuItem(menuItems, (focusedItemIndex - 1 + menuItems.length) % menuItems.length); + break; + case "Escape": + dropdown.removeAttribute('open'); + setAriaExpanded(menuButton, false); + menuButton.focus(); + break; + } +} + +function focusMenuItem(menuItems, index) { + menuItems[index].focus(); +} + +function setAriaExpanded(element, state) { + element.setAttribute('aria-expanded', state ? 'true' : 'false'); +} diff --git a/templates/partials/footer.html b/templates/partials/footer.html index de74f91..ed6c754 100644 --- a/templates/partials/footer.html +++ b/templates/partials/footer.html @@ -118,4 +118,9 @@ {%- if email_needs_decoding -%} {%- endif -%} + + {# Load accessibility JavaScript #} + {%- if config.extra.accessibility_javascript -%} + + {%- endif -%} diff --git a/theme.toml b/theme.toml index ac90fa1..e34fe78 100644 --- a/theme.toml +++ b/theme.toml @@ -75,6 +75,9 @@ show_remote_changes = true # Defaults to true. # Show a link to the repository of the site, right next to the "Powered by Zola & tabi" text. show_remote_source = true # Defaults to true. +# Load JavaScript to improve accessibility. +accessibility_javascript = true + # Add a "copy" button to codeblocks (loads ~700 bytes of JavaScript). # Can be set at page or section levels, following the hierarchy: page > section > config. See: https://welpo.github.io/tabi/blog/mastering-tabi-settings/#settings-hierarchy copy_button = true