diff --git a/resources/assets/v2/src/js/accessibility.js b/resources/assets/v2/src/js/accessibility.js new file mode 100644 index 00000000000..08f9b9b1891 --- /dev/null +++ b/resources/assets/v2/src/js/accessibility.js @@ -0,0 +1,72 @@ +document.addEventListener('DOMContentLoaded', function() { + const sidebarToggle = document.querySelector('.sidebar-toggle'); + if (sidebarToggle) { + sidebarToggle.addEventListener('click', function() { + const isExpanded = this.getAttribute('aria-expanded') === 'true'; + this.setAttribute('aria-expanded', !isExpanded); + }); + } + + const treeviewLinks = document.querySelectorAll('.treeview > a'); + treeviewLinks.forEach(function(link) { + link.addEventListener('click', function(e) { + e.preventDefault(); + const isExpanded = this.getAttribute('aria-expanded') === 'true'; + this.setAttribute('aria-expanded', !isExpanded); + + const submenu = this.nextElementSibling; + if (submenu && submenu.classList.contains('treeview-menu')) { + submenu.style.display = isExpanded ? 'none' : 'block'; + } + }); + }); + + const modals = document.querySelectorAll('.modal'); + modals.forEach(function(modal) { + modal.addEventListener('shown.bs.modal', function() { + const firstFocusable = modal.querySelector('input, button, select, textarea, [tabindex]:not([tabindex="-1"])'); + if (firstFocusable) { + firstFocusable.focus(); + } + }); + + modal.addEventListener('hidden.bs.modal', function() { + const trigger = document.querySelector('[data-target="#' + modal.id + '"]'); + if (trigger) { + trigger.focus(); + } + }); + }); + + function announceToScreenReader(message) { + const announcement = document.createElement('div'); + announcement.setAttribute('aria-live', 'polite'); + announcement.setAttribute('aria-atomic', 'true'); + announcement.className = 'sr-only'; + announcement.textContent = message; + document.body.appendChild(announcement); + + setTimeout(function() { + document.body.removeChild(announcement); + }, 1000); + } + + const forms = document.querySelectorAll('form'); + forms.forEach(function(form) { + form.addEventListener('submit', function(e) { + const errors = form.querySelectorAll('.text-danger[role="alert"]'); + if (errors.length > 0) { + announceToScreenReader('Form has ' + errors.length + ' error' + (errors.length > 1 ? 's' : '') + '. Please review and correct.'); + } + }); + }); + + document.addEventListener('keydown', function(e) { + if (e.key === 'Escape') { + const openModals = document.querySelectorAll('.modal.in, .modal.show'); + openModals.forEach(function(modal) { + $(modal).modal('hide'); + }); + } + }); +}); diff --git a/resources/assets/v2/src/sass/app.scss b/resources/assets/v2/src/sass/app.scss index 872e5bda2d6..8f9809e0247 100644 --- a/resources/assets/v2/src/sass/app.scss +++ b/resources/assets/v2/src/sass/app.scss @@ -48,7 +48,20 @@ $success: #64B624 !default; // some local CSS -.skip-links {display: none;} +.skip-links { + position: absolute; + left: -9999px; + z-index: 999999; + padding: 8px 16px; + background: #000; + color: #fff; + text-decoration: none; + border-radius: 3px; +} +.skip-links:focus { + left: 6px; + top: 7px; +} /* Remove bottom margin from unstyled lists @@ -80,6 +93,59 @@ h3.hover-expand:hover { appearance: auto; } +.form-control:focus { + outline: 2px solid #1E6581; + outline-offset: 2px; +} + +.btn:focus { + outline: 2px solid #1E6581; + outline-offset: 2px; +} + +a:focus { + outline: 2px solid #1E6581; + outline-offset: 2px; +} + +.sidebar-toggle:focus { + outline: 2px solid #1E6581; + outline-offset: 2px; +} + +.treeview > a:focus { + outline: 2px solid #1E6581; + outline-offset: 2px; +} + +.modal:focus { + outline: none; +} + +.modal-content { + outline: 2px solid #1E6581; +} + +@media (prefers-reduced-motion: reduce) { + * { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +} + +.sr-only { + position: absolute !important; + width: 1px !important; + height: 1px !important; + padding: 0 !important; + margin: -1px !important; + overflow: hidden !important; + clip: rect(0, 0, 0, 0) !important; + white-space: nowrap !important; + border: 0 !important; +} + // edit buttons .hidden-edit-button { cursor: pointer; diff --git a/resources/views/accounts/create.twig b/resources/views/accounts/create.twig index c52e127687d..01472f2be16 100644 --- a/resources/views/accounts/create.twig +++ b/resources/views/accounts/create.twig @@ -3,6 +3,8 @@ {{ Breadcrumbs.render(Route.getCurrentRoute.getName, objectType) }} {% endblock %} {% block content %} +