chunk-021-accessibility.js

I’ll build this as a safe, dependency-free accessibility helper for RX Theme: keyboard navigation, skip links, focus handling, reduced-motion support, ARIA helpers, forms, tables, menus, modals, and WordPress-friendly initialization.

Create this file:

assets/static-js/static-chunks-js/chunk-021-accessibility.js

/*!
 * RX Theme Accessibility Chunk
 * File: assets/static-js/static-chunks-js/chunk-021-accessibility.js
 * Purpose: Advanced accessibility helpers for RX Theme
 * Author: RxHarun
 *
 * Safe features:
 * - Skip link support
 * - Keyboard navigation
 * - Focus-visible handling
 * - Mobile menu accessibility
 * - Dropdown menu accessibility
 * - Modal accessibility
 * - Accordion accessibility
 * - Tabs accessibility
 * - Form accessibility
 * - Table accessibility
 * - External link labels
 * - Reduced motion support
 * - Live region announcements
 * - Lazy accessibility fixes
 * - WordPress admin-bar aware focus offset
 */

(function () {
  'use strict';

  const RXA11Y = {
    version: '1.0.0',

    selectors: {
      body: 'body',
      siteHeader: '.site-header, #masthead, header[role="banner"]',
      main: 'main, #main, .site-main, #primary',
      nav: 'nav, .main-navigation, .primary-navigation',
      menuToggle: '.menu-toggle, .nav-toggle, .mobile-menu-toggle, [data-rx-menu-toggle]',
      menuContainer: '.menu, .nav-menu, .primary-menu, [data-rx-menu]',
      dropdownParent: '.menu-item-has-children, .page_item_has_children, [data-rx-has-submenu]',
      submenu: '.sub-menu, .children, [data-rx-submenu]',
      searchToggle: '[data-rx-search-toggle], .search-toggle',
      searchForm: '.search-form, [role="search"], [data-rx-search-form]',
      modal: '[data-rx-modal], .rx-modal',
      modalOpen: '[data-rx-modal-open]',
      modalClose: '[data-rx-modal-close], .rx-modal-close',
      accordion: '[data-rx-accordion]',
      accordionButton: '[data-rx-accordion-button]',
      accordionPanel: '[data-rx-accordion-panel]',
      tabs: '[data-rx-tabs]',
      tabList: '[role="tablist"], [data-rx-tablist]',
      tab: '[role="tab"], [data-rx-tab]',
      tabPanel: '[role="tabpanel"], [data-rx-tabpanel]',
      table: 'table',
      iframe: 'iframe',
      image: 'img',
      form: 'form',
      input: 'input, textarea, select',
      invalidInput: 'input:invalid, textarea:invalid, select:invalid',
      externalLink: 'a[target="_blank"]',
      skipLink: '.skip-link, .screen-reader-text[href^="#"]'
    },

    classNames: {
      js: 'rx-js',
      noMotion: 'rx-reduced-motion',
      keyboardUser: 'rx-keyboard-user',
      mouseUser: 'rx-mouse-user',
      menuOpen: 'rx-menu-open',
      submenuOpen: 'rx-submenu-open',
      modalOpen: 'rx-modal-open',
      focusTrapActive: 'rx-focus-trap-active',
      screenReaderText: 'screen-reader-text',
      visuallyHidden: 'rx-visually-hidden',
      hasFocus: 'rx-has-focus',
      isReady: 'rx-a11y-ready'
    },

    keys: {
      tab: 'Tab',
      enter: 'Enter',
      escape: 'Escape',
      space: ' ',
      spaceLegacy: 'Spacebar',
      arrowUp: 'ArrowUp',
      arrowDown: 'ArrowDown',
      arrowLeft: 'ArrowLeft',
      arrowRight: 'ArrowRight',
      home: 'Home',
      end: 'End'
    },

    state: {
      lastFocusedElement: null,
      activeModal: null,
      activeFocusTrap: null,
      liveRegion: null,
      reducedMotion: false,
      initialized: false
    }
  };

  const doc = document;
  const win = window;
  const html = doc.documentElement;
  const body = doc.body;

  if (!body) {
    return;
  }

  function qs(selector, context = doc) {
    return context.querySelector(selector);
  }

  function qsa(selector, context = doc) {
    return Array.prototype.slice.call(context.querySelectorAll(selector));
  }

  function isElement(value) {
    return value instanceof Element || value instanceof HTMLDocument;
  }

  function isVisible(element) {
    if (!element || !isElement(element)) return false;

    const style = win.getComputedStyle(element);

    if (
      style.display === 'none' ||
      style.visibility === 'hidden' ||
      style.opacity === '0'
    ) {
      return false;
    }

    const rect = element.getBoundingClientRect();

    return !!(
      rect.width ||
      rect.height ||
      element.getClientRects().length
    );
  }

  function isFocusable(element) {
    if (!element || !isElement(element)) return false;

    if (element.disabled) return false;
    if (element.getAttribute('aria-hidden') === 'true') return false;
    if (element.hasAttribute('hidden')) return false;
    if (!isVisible(element)) return false;

    const focusableSelectors = [
      'a[href]',
      'area[href]',
      'button:not([disabled])',
      'input:not([disabled]):not([type="hidden"])',
      'select:not([disabled])',
      'textarea:not([disabled])',
      'iframe',
      'object',
      'embed',
      '[contenteditable="true"]',
      '[tabindex]:not([tabindex="-1"])'
    ];

    return element.matches(focusableSelectors.join(','));
  }

  function getFocusableElements(container = doc) {
    const focusableSelectors = [
      'a[href]',
      'area[href]',
      'button:not([disabled])',
      'input:not([disabled]):not([type="hidden"])',
      'select:not([disabled])',
      'textarea:not([disabled])',
      'iframe',
      'object',
      'embed',
      '[contenteditable="true"]',
      '[tabindex]:not([tabindex="-1"])'
    ];

    return qsa(focusableSelectors.join(','), container).filter(isFocusable);
  }

  function safeFocus(element, options = {}) {
    if (!element || !isElement(element)) return;

    try {
      element.focus({
        preventScroll: !!options.preventScroll
      });
    } catch (error) {
      try {
        element.focus();
      } catch (innerError) {
        return;
      }
    }

    if (options.scroll) {
      element.scrollIntoView({
        behavior: RXA11Y.state.reducedMotion ? 'auto' : 'smooth',
        block: 'center'
      });
    }
  }

  function uniqueId(prefix = 'rx-a11y') {
    return `${prefix}-${Math.random().toString(36).slice(2, 10)}`;
  }

  function ensureId(element, prefix = 'rx-a11y') {
    if (!element.id) {
      element.id = uniqueId(prefix);
    }

    return element.id;
  }

  function setExpanded(element, expanded) {
    if (!element) return;

    element.setAttribute('aria-expanded', expanded ? 'true' : 'false');
  }

  function setHidden(element, hidden) {
    if (!element) return;

    if (hidden) {
      element.setAttribute('hidden', '');
      element.setAttribute('aria-hidden', 'true');
    } else {
      element.removeAttribute('hidden');
      element.setAttribute('aria-hidden', 'false');
    }
  }

  function hasText(element) {
    if (!element) return false;

    return element.textContent && element.textContent.trim().length > 0;
  }

  function createScreenReaderText(text) {
    const span = doc.createElement('span');
    span.className = RXA11Y.classNames.screenReaderText;
    span.textContent = text;
    return span;
  }

  function announce(message, politeness = 'polite') {
    if (!message) return;

    if (!RXA11Y.state.liveRegion) {
      const region = doc.createElement('div');
      region.className = 'rx-a11y-live-region screen-reader-text';
      region.setAttribute('aria-live', politeness);
      region.setAttribute('aria-atomic', 'true');
      body.appendChild(region);
      RXA11Y.state.liveRegion = region;
    }

    RXA11Y.state.liveRegion.setAttribute('aria-live', politeness);
    RXA11Y.state.liveRegion.textContent = '';

    win.setTimeout(function () {
      RXA11Y.state.liveRegion.textContent = message;
    }, 50);
  }

  function initBaseClasses() {
    html.classList.add(RXA11Y.classNames.js);
    body.classList.add(RXA11Y.classNames.isReady);
  }

  function initReducedMotion() {
    const mediaQuery = win.matchMedia('(prefers-reduced-motion: reduce)');

    function updateReducedMotion() {
      RXA11Y.state.reducedMotion = mediaQuery.matches;

      if (mediaQuery.matches) {
        html.classList.add(RXA11Y.classNames.noMotion);
      } else {
        html.classList.remove(RXA11Y.classNames.noMotion);
      }
    }

    updateReducedMotion();

    if (typeof mediaQuery.addEventListener === 'function') {
      mediaQuery.addEventListener('change', updateReducedMotion);
    } else if (typeof mediaQuery.addListener === 'function') {
      mediaQuery.addListener(updateReducedMotion);
    }
  }

  function initInputModeDetection() {
    function setKeyboardMode() {
      body.classList.add(RXA11Y.classNames.keyboardUser);
      body.classList.remove(RXA11Y.classNames.mouseUser);
    }

    function setMouseMode() {
      body.classList.add(RXA11Y.classNames.mouseUser);
      body.classList.remove(RXA11Y.classNames.keyboardUser);
    }

    doc.addEventListener('keydown', function (event) {
      if (event.key === RXA11Y.keys.tab) {
        setKeyboardMode();
      }
    });

    doc.addEventListener('mousedown', setMouseMode);
    doc.addEventListener('pointerdown', setMouseMode);
    doc.addEventListener('touchstart', setMouseMode, { passive: true });
  }

  function initFocusWithinPolyfill() {
    doc.addEventListener('focusin', function (event) {
      const target = event.target;

      if (!target || !isElement(target)) return;

      target.classList.add(RXA11Y.classNames.hasFocus);

      let parent = target.parentElement;

      while (parent && parent !== body) {
        parent.classList.add(RXA11Y.classNames.hasFocus);
        parent = parent.parentElement;
      }
    });

    doc.addEventListener('focusout', function (event) {
      const target = event.target;

      if (!target || !isElement(target)) return;

      win.setTimeout(function () {
        qsa(`.${RXA11Y.classNames.hasFocus}`).forEach(function (element) {
          if (!element.contains(doc.activeElement)) {
            element.classList.remove(RXA11Y.classNames.hasFocus);
          }
        });
      }, 0);
    });
  }

  function initSkipLinks() {
    let main = qs(RXA11Y.selectors.main);

    if (!main) {
      main = doc.createElement('main');
      main.id = 'main';
      main.className = 'site-main';
      body.appendChild(main);
    }

    const mainId = ensureId(main, 'rx-main');

    let existingSkipLink = qs(`a[href="#${mainId}"]`);

    if (!existingSkipLink) {
      existingSkipLink = doc.createElement('a');
      existingSkipLink.className = 'skip-link screen-reader-text';
      existingSkipLink.href = `#${mainId}`;
      existingSkipLink.textContent = 'Skip to main content';
      body.insertBefore(existingSkipLink, body.firstChild);
    }

    existingSkipLink.addEventListener('click', function (event) {
      const target = qs(this.getAttribute('href'));

      if (!target) return;

      event.preventDefault();

      if (!target.hasAttribute('tabindex')) {
        target.setAttribute('tabindex', '-1');
      }

      safeFocus(target, {
        scroll: true
      });
    });
  }

  function initLandmarks() {
    const header = qs(RXA11Y.selectors.siteHeader);
    const main = qs(RXA11Y.selectors.main);
    const navs = qsa(RXA11Y.selectors.nav);

    if (header && !header.getAttribute('role')) {
      header.setAttribute('role', 'banner');
    }

    if (main && main.tagName.toLowerCase() !== 'main') {
      main.setAttribute('role', 'main');
    }

    navs.forEach(function (nav, index) {
      if (!nav.getAttribute('role')) {
        nav.setAttribute('role', 'navigation');
      }

      if (!nav.getAttribute('aria-label') && !nav.getAttribute('aria-labelledby')) {
        nav.setAttribute('aria-label', index === 0 ? 'Primary navigation' : `Navigation ${index + 1}`);
      }
    });
  }

  function initMobileMenu() {
    const toggles = qsa(RXA11Y.selectors.menuToggle);

    toggles.forEach(function (toggle) {
      const targetSelector =
        toggle.getAttribute('data-rx-menu-target') ||
        toggle.getAttribute('aria-controls');

      let menu = null;

      if (targetSelector) {
        menu = qs(targetSelector.startsWith('#') ? targetSelector : `#${targetSelector}`);
      }

      if (!menu) {
        const nav = toggle.closest(RXA11Y.selectors.nav);
        menu = nav ? qs(RXA11Y.selectors.menuContainer, nav) : qs(RXA11Y.selectors.menuContainer);
      }

      if (!menu) return;

      const menuId = ensureId(menu, 'rx-menu');

      toggle.setAttribute('aria-controls', menuId);
      setExpanded(toggle, false);

      if (!toggle.getAttribute('aria-label') && !hasText(toggle)) {
        toggle.setAttribute('aria-label', 'Open navigation menu');
      }

      function openMenu() {
        body.classList.add(RXA11Y.classNames.menuOpen);
        menu.classList.add(RXA11Y.classNames.menuOpen);
        setExpanded(toggle, true);
        toggle.setAttribute('aria-label', 'Close navigation menu');
        announce('Navigation menu opened');
      }

      function closeMenu(options = {}) {
        body.classList.remove(RXA11Y.classNames.menuOpen);
        menu.classList.remove(RXA11Y.classNames.menuOpen);
        setExpanded(toggle, false);
        toggle.setAttribute('aria-label', 'Open navigation menu');

        if (options.focusToggle) {
          safeFocus(toggle);
        }

        announce('Navigation menu closed');
      }

      function isOpen() {
        return toggle.getAttribute('aria-expanded') === 'true';
      }

      toggle.addEventListener('click', function () {
        if (isOpen()) {
          closeMenu();
        } else {
          openMenu();
        }
      });

      toggle.addEventListener('keydown', function (event) {
        if (event.key === RXA11Y.keys.enter || event.key === RXA11Y.keys.space || event.key === RXA11Y.keys.spaceLegacy) {
          event.preventDefault();

          if (isOpen()) {
            closeMenu();
          } else {
            openMenu();
          }
        }

        if (event.key === RXA11Y.keys.escape && isOpen()) {
          closeMenu({
            focusToggle: true
          });
        }
      });

      doc.addEventListener('keydown', function (event) {
        if (event.key === RXA11Y.keys.escape && isOpen()) {
          closeMenu({
            focusToggle: true
          });
        }
      });

      doc.addEventListener('click', function (event) {
        if (!isOpen()) return;
        if (menu.contains(event.target) || toggle.contains(event.target)) return;

        closeMenu();
      });
    });
  }

  function initDropdownMenus() {
    const parents = qsa(RXA11Y.selectors.dropdownParent);

    parents.forEach(function (parent) {
      const submenu = qs(RXA11Y.selectors.submenu, parent);
      const directLink = qs(':scope > a, :scope > button', parent);

      if (!submenu || !directLink) return;

      const submenuId = ensureId(submenu, 'rx-submenu');

      directLink.setAttribute('aria-haspopup', 'true');
      directLink.setAttribute('aria-controls', submenuId);
      setExpanded(directLink, false);

      function openSubmenu() {
        parent.classList.add(RXA11Y.classNames.submenuOpen);
        setExpanded(directLink, true);
      }

      function closeSubmenu() {
        parent.classList.remove(RXA11Y.classNames.submenuOpen);
        setExpanded(directLink, false);
      }

      function toggleSubmenu(event) {
        const expanded = directLink.getAttribute('aria-expanded') === 'true';

        if (expanded) {
          closeSubmenu();
        } else {
          openSubmenu();
        }

        if (event) {
          event.preventDefault();
        }
      }

      directLink.addEventListener('keydown', function (event) {
        const submenuLinks = getFocusableElements(submenu);

        if (event.key === RXA11Y.keys.arrowDown) {
          event.preventDefault();
          openSubmenu();

          if (submenuLinks.length) {
            safeFocus(submenuLinks[0]);
          }
        }

        if (event.key === RXA11Y.keys.escape) {
          event.preventDefault();
          closeSubmenu();
          safeFocus(directLink);
        }

        if (event.key === RXA11Y.keys.space || event.key === RXA11Y.keys.spaceLegacy) {
          toggleSubmenu(event);
        }
      });

      submenu.addEventListener('keydown', function (event) {
        const links = getFocusableElements(submenu);
        const currentIndex = links.indexOf(doc.activeElement);

        if (event.key === RXA11Y.keys.escape) {
          event.preventDefault();
          closeSubmenu();
          safeFocus(directLink);
        }

        if (event.key === RXA11Y.keys.arrowDown && links.length) {
          event.preventDefault();
          safeFocus(links[(currentIndex + 1) % links.length]);
        }

        if (event.key === RXA11Y.keys.arrowUp && links.length) {
          event.preventDefault();
          safeFocus(links[(currentIndex - 1 + links.length) % links.length]);
        }

        if (event.key === RXA11Y.keys.home && links.length) {
          event.preventDefault();
          safeFocus(links[0]);
        }

        if (event.key === RXA11Y.keys.end && links.length) {
          event.preventDefault();
          safeFocus(links[links.length - 1]);
        }
      });

      parent.addEventListener('mouseenter', openSubmenu);
      parent.addEventListener('mouseleave', closeSubmenu);

      parent.addEventListener('focusout', function () {
        win.setTimeout(function () {
          if (!parent.contains(doc.activeElement)) {
            closeSubmenu();
          }
        }, 0);
      });
    });
  }

  function trapFocus(container, event) {
    const focusable = getFocusableElements(container);

    if (!focusable.length) {
      event.preventDefault();
      safeFocus(container);
      return;
    }

    const first = focusable[0];
    const last = focusable[focusable.length - 1];

    if (event.shiftKey && doc.activeElement === first) {
      event.preventDefault();
      safeFocus(last);
    } else if (!event.shiftKey && doc.activeElement === last) {
      event.preventDefault();
      safeFocus(first);
    }
  }

  function initModals() {
    const openers = qsa(RXA11Y.selectors.modalOpen);
    const modals = qsa(RXA11Y.selectors.modal);

    modals.forEach(function (modal) {
      if (!modal.getAttribute('role')) {
        modal.setAttribute('role', 'dialog');
      }

      modal.setAttribute('aria-modal', 'true');

      if (!modal.getAttribute('aria-label') && !modal.getAttribute('aria-labelledby')) {
        const heading = qs('h1, h2, h3, h4, h5, h6', modal);

        if (heading) {
          modal.setAttribute('aria-labelledby', ensureId(heading, 'rx-modal-title'));
        } else {
          modal.setAttribute('aria-label', 'Dialog');
        }
      }

      setHidden(modal, true);
    });

    function openModal(modal, opener) {
      if (!modal) return;

      RXA11Y.state.lastFocusedElement = opener || doc.activeElement;
      RXA11Y.state.activeModal = modal;

      setHidden(modal, false);
      body.classList.add(RXA11Y.classNames.modalOpen);

      const focusTarget =
        qs('[data-rx-modal-focus]', modal) ||
        getFocusableElements(modal)[0] ||
        modal;

      if (!modal.hasAttribute('tabindex')) {
        modal.setAttribute('tabindex', '-1');
      }

      safeFocus(focusTarget);
      announce('Dialog opened');
    }

    function closeModal(modal, options = {}) {
      if (!modal) return;

      setHidden(modal, true);
      body.classList.remove(RXA11Y.classNames.modalOpen);

      RXA11Y.state.activeModal = null;

      if (options.restoreFocus !== false && RXA11Y.state.lastFocusedElement) {
        safeFocus(RXA11Y.state.lastFocusedElement);
      }

      announce('Dialog closed');
    }

    openers.forEach(function (opener) {
      const targetSelector =
        opener.getAttribute('data-rx-modal-open') ||
        opener.getAttribute('href') ||
        opener.getAttribute('aria-controls');

      const modal = targetSelector ? qs(targetSelector) : null;

      if (!modal) return;

      opener.setAttribute('aria-haspopup', 'dialog');
      opener.setAttribute('aria-controls', ensureId(modal, 'rx-modal'));

      opener.addEventListener('click', function (event) {
        event.preventDefault();
        openModal(modal, opener);
      });
    });

    doc.addEventListener('click', function (event) {
      const closeButton = event.target.closest(RXA11Y.selectors.modalClose);

      if (closeButton) {
        event.preventDefault();
        closeModal(closeButton.closest(RXA11Y.selectors.modal));
        return;
      }

      const activeModal = RXA11Y.state.activeModal;

      if (
        activeModal &&
        event.target === activeModal &&
        activeModal.getAttribute('data-rx-modal-static') !== 'true'
      ) {
        closeModal(activeModal);
      }
    });

    doc.addEventListener('keydown', function (event) {
      const activeModal = RXA11Y.state.activeModal;

      if (!activeModal) return;

      if (event.key === RXA11Y.keys.escape && activeModal.getAttribute('data-rx-modal-esc') !== 'false') {
        closeModal(activeModal);
      }

      if (event.key === RXA11Y.keys.tab) {
        trapFocus(activeModal, event);
      }
    });
  }

  function initAccordions() {
    const accordions = qsa(RXA11Y.selectors.accordion);

    accordions.forEach(function (accordion) {
      const buttons = qsa(RXA11Y.selectors.accordionButton, accordion);

      buttons.forEach(function (button, index) {
        let panel = null;

        const controls = button.getAttribute('aria-controls') || button.getAttribute('data-rx-controls');

        if (controls) {
          panel = qs(`#${controls}`);
        }

        if (!panel) {
          panel = qsa(RXA11Y.selectors.accordionPanel, accordion)[index];
        }

        if (!panel) return;

        const buttonId = ensureId(button, 'rx-accordion-button');
        const panelId = ensureId(panel, 'rx-accordion-panel');

        button.setAttribute('aria-controls', panelId);
        panel.setAttribute('aria-labelledby', buttonId);

        if (!button.hasAttribute('aria-expanded')) {
          setExpanded(button, false);
          setHidden(panel, true);
        }

        button.addEventListener('click', function () {
          const expanded = button.getAttribute('aria-expanded') === 'true';
          const allowMultiple = accordion.getAttribute('data-rx-accordion-multiple') === 'true';

          if (!allowMultiple) {
            buttons.forEach(function (otherButton) {
              if (otherButton === button) return;

              const otherPanel = qs(`#${otherButton.getAttribute('aria-controls')}`);

              setExpanded(otherButton, false);
              setHidden(otherPanel, true);
            });
          }

          setExpanded(button, !expanded);
          setHidden(panel, expanded);
        });

        button.addEventListener('keydown', function (event) {
          const currentIndex = buttons.indexOf(button);

          if (event.key === RXA11Y.keys.arrowDown) {
            event.preventDefault();
            safeFocus(buttons[(currentIndex + 1) % buttons.length]);
          }

          if (event.key === RXA11Y.keys.arrowUp) {
            event.preventDefault();
            safeFocus(buttons[(currentIndex - 1 + buttons.length) % buttons.length]);
          }

          if (event.key === RXA11Y.keys.home) {
            event.preventDefault();
            safeFocus(buttons[0]);
          }

          if (event.key === RXA11Y.keys.end) {
            event.preventDefault();
            safeFocus(buttons[buttons.length - 1]);
          }
        });
      });
    });
  }

  function initTabs() {
    const tabGroups = qsa(RXA11Y.selectors.tabs);

    tabGroups.forEach(function (group) {
      const tabList = qs(RXA11Y.selectors.tabList, group) || group;
      const tabs = qsa(RXA11Y.selectors.tab, group);
      const panels = qsa(RXA11Y.selectors.tabPanel, group);

      if (!tabs.length || !panels.length) return;

      tabList.setAttribute('role', 'tablist');

      tabs.forEach(function (tab, index) {
        const panel = panels[index];

        if (!panel) return;

        const tabId = ensureId(tab, 'rx-tab');
        const panelId = ensureId(panel, 'rx-tab-panel');

        tab.setAttribute('role', 'tab');
        tab.setAttribute('aria-controls', panelId);

        panel.setAttribute('role', 'tabpanel');
        panel.setAttribute('aria-labelledby', tabId);

        const selected = index === 0;

        tab.setAttribute('aria-selected', selected ? 'true' : 'false');
        tab.setAttribute('tabindex', selected ? '0' : '-1');

        if (!selected) {
          setHidden(panel, true);
        } else {
          setHidden(panel, false);
        }

        function activateTab(focus = true) {
          tabs.forEach(function (otherTab, otherIndex) {
            const otherPanel = panels[otherIndex];
            const isCurrent = otherTab === tab;

            otherTab.setAttribute('aria-selected', isCurrent ? 'true' : 'false');
            otherTab.setAttribute('tabindex', isCurrent ? '0' : '-1');

            if (otherPanel) {
              setHidden(otherPanel, !isCurrent);
            }
          });

          if (focus) {
            safeFocus(tab);
          }
        }

        tab.addEventListener('click', function (event) {
          event.preventDefault();
          activateTab(false);
        });

        tab.addEventListener('keydown', function (event) {
          const currentIndex = tabs.indexOf(tab);
          let nextIndex = null;

          if (event.key === RXA11Y.keys.arrowRight) {
            nextIndex = (currentIndex + 1) % tabs.length;
          }

          if (event.key === RXA11Y.keys.arrowLeft) {
            nextIndex = (currentIndex - 1 + tabs.length) % tabs.length;
          }

          if (event.key === RXA11Y.keys.home) {
            nextIndex = 0;
          }

          if (event.key === RXA11Y.keys.end) {
            nextIndex = tabs.length - 1;
          }

          if (nextIndex !== null) {
            event.preventDefault();
            tabs[nextIndex].click();
            safeFocus(tabs[nextIndex]);
          }
        });
      });
    });
  }

  function initSearchForms() {
    const forms = qsa(RXA11Y.selectors.searchForm);

    forms.forEach(function (form, index) {
      if (!form.getAttribute('role')) {
        form.setAttribute('role', 'search');
      }

      if (!form.getAttribute('aria-label') && !form.getAttribute('aria-labelledby')) {
        form.setAttribute('aria-label', index === 0 ? 'Site search' : `Site search ${index + 1}`);
      }

      const input = qs('input[type="search"], input[name="s"], input[type="text"]', form);

      if (input) {
        if (!input.getAttribute('aria-label') && !input.getAttribute('aria-labelledby')) {
          input.setAttribute('aria-label', 'Search keywords');
        }

        if (!input.getAttribute('autocomplete')) {
          input.setAttribute('autocomplete', 'search');
        }
      }
    });
  }

  function initFormAccessibility() {
    const forms = qsa(RXA11Y.selectors.form);

    forms.forEach(function (form) {
      const inputs = qsa(RXA11Y.selectors.input, form);

      inputs.forEach(function (input) {
        const id = ensureId(input, 'rx-field');

        const hasLabel =
          qs(`label[for="${CSS.escape(id)}"]`, form) ||
          input.closest('label') ||
          input.getAttribute('aria-label') ||
          input.getAttribute('aria-labelledby');

        if (!hasLabel) {
          const placeholder = input.getAttribute('placeholder');
          const name = input.getAttribute('name');

          if (placeholder) {
            input.setAttribute('aria-label', placeholder);
          } else if (name) {
            input.setAttribute('aria-label', name.replace(/[-_]/g, ' '));
          }
        }

        input.addEventListener('invalid', function () {
          const message = input.validationMessage || 'Please check this field.';
          input.setAttribute('aria-invalid', 'true');

          let error = input.nextElementSibling;

          if (!error || !error.classList.contains('rx-field-error')) {
            error = doc.createElement('span');
            error.className = 'rx-field-error screen-reader-text';
            error.id = `${id}-error`;
            input.insertAdjacentElement('afterend', error);
          }

          error.textContent = message;
          input.setAttribute('aria-describedby', `${input.getAttribute('aria-describedby') || ''} ${error.id}`.trim());

          announce(message, 'assertive');
        });

        input.addEventListener('input', function () {
          if (input.checkValidity()) {
            input.removeAttribute('aria-invalid');

            const error = qs(`#${CSS.escape(id)}-error`);

            if (error) {
              error.textContent = '';
            }
          }
        });
      });
    });
  }

  function initTables() {
    const tables = qsa(RXA11Y.selectors.table);

    tables.forEach(function (table, index) {
      if (!table.getAttribute('role')) {
        table.setAttribute('role', 'table');
      }

      if (!table.getAttribute('aria-label') && !table.getAttribute('aria-labelledby')) {
        const caption = qs('caption', table);

        if (caption && hasText(caption)) {
          table.setAttribute('aria-labelledby', ensureId(caption, 'rx-table-caption'));
        } else {
          table.setAttribute('aria-label', `Data table ${index + 1}`);
        }
      }

      const headers = qsa('th', table);

      headers.forEach(function (header) {
        if (!header.getAttribute('scope')) {
          const parentRow = header.parentElement;
          const isFirstCell = parentRow && parentRow.firstElementChild === header;

          header.setAttribute('scope', isFirstCell ? 'row' : 'col');
        }
      });

      const wrapper = table.parentElement;

      if (wrapper && !wrapper.classList.contains('rx-table-responsive')) {
        if (table.scrollWidth > wrapper.clientWidth) {
          wrapper.setAttribute('tabindex', '0');
          wrapper.setAttribute('role', 'region');
          wrapper.setAttribute('aria-label', 'Scrollable table');
        }
      }
    });
  }

  function initImages() {
    const images = qsa(RXA11Y.selectors.image);

    images.forEach(function (image) {
      if (image.hasAttribute('alt')) return;

      const role = image.getAttribute('role');
      const ariaHidden = image.getAttribute('aria-hidden');

      if (role === 'presentation' || ariaHidden === 'true') {
        image.setAttribute('alt', '');
        return;
      }

      const nearbyCaption =
        image.closest('figure') && qs('figcaption', image.closest('figure'));

      if (nearbyCaption && hasText(nearbyCaption)) {
        image.setAttribute('alt', nearbyCaption.textContent.trim());
      } else {
        image.setAttribute('alt', '');
      }
    });
  }

  function initIframes() {
    const iframes = qsa(RXA11Y.selectors.iframe);

    iframes.forEach(function (iframe, index) {
      if (!iframe.getAttribute('title')) {
        const src = iframe.getAttribute('src') || '';

        if (src.includes('youtube') || src.includes('youtu.be')) {
          iframe.setAttribute('title', 'Embedded YouTube video');
        } else if (src.includes('maps')) {
          iframe.setAttribute('title', 'Embedded map');
        } else {
          iframe.setAttribute('title', `Embedded content ${index + 1}`);
        }
      }
    });
  }

  function initExternalLinks() {
    const links = qsa(RXA11Y.selectors.externalLink);

    links.forEach(function (link) {
      const rel = link.getAttribute('rel') || '';
      const relParts = rel.split(/\s+/).filter(Boolean);

      ['noopener', 'noreferrer'].forEach(function (value) {
        if (!relParts.includes(value)) {
          relParts.push(value);
        }
      });

      link.setAttribute('rel', relParts.join(' '));

      const hasWarning =
        link.textContent.includes('opens in a new tab') ||
        link.getAttribute('aria-label')?.includes('opens in a new tab');

      if (!hasWarning) {
        if (link.getAttribute('aria-label')) {
          link.setAttribute(
            'aria-label',
            `${link.getAttribute('aria-label')} opens in a new tab`
          );
        } else {
          link.appendChild(createScreenReaderText(' opens in a new tab'));
        }
      }
    });
  }

  function initSmoothAnchorFocus() {
    doc.addEventListener('click', function (event) {
      const link = event.target.closest('a[href^="#"]');

      if (!link) return;

      const href = link.getAttribute('href');

      if (!href || href === '#') return;

      const target = qs(href);

      if (!target) return;

      event.preventDefault();

      if (!target.hasAttribute('tabindex')) {
        target.setAttribute('tabindex', '-1');
      }

      target.scrollIntoView({
        behavior: RXA11Y.state.reducedMotion ? 'auto' : 'smooth',
        block: 'start'
      });

      safeFocus(target, {
        preventScroll: true
      });

      if (history.pushState) {
        history.pushState(null, '', href);
      }
    });
  }

  function initKeyboardShortcuts() {
    doc.addEventListener('keydown', function (event) {
      const tagName = event.target && event.target.tagName
        ? event.target.tagName.toLowerCase()
        : '';

      const isTyping =
        tagName === 'input' ||
        tagName === 'textarea' ||
        tagName === 'select' ||
        event.target.isContentEditable;

      if (isTyping) return;

      if (event.key === '/') {
        const searchInput = qs('input[type="search"], input[name="s"]');

        if (searchInput) {
          event.preventDefault();
          safeFocus(searchInput, {
            scroll: true
          });
          announce('Search field focused');
        }
      }

      if (event.key === RXA11Y.keys.escape) {
        qsa(`.${RXA11Y.classNames.submenuOpen}`).forEach(function (item) {
          const trigger = qs('[aria-expanded="true"]', item);
          item.classList.remove(RXA11Y.classNames.submenuOpen);

          if (trigger) {
            setExpanded(trigger, false);
          }
        });
      }
    });
  }

  function initAriaCurrentForLinks() {
    const currentUrl = win.location.href.split('#')[0];
    const links = qsa('a[href]');

    links.forEach(function (link) {
      const linkUrl = link.href.split('#')[0];

      if (linkUrl === currentUrl && !link.getAttribute('aria-current')) {
        link.setAttribute('aria-current', 'page');
      }
    });
  }

  function initBackToTopButton() {
    const button = qs('[data-rx-back-to-top], .back-to-top');

    if (!button) return;

    if (!button.getAttribute('aria-label') && !hasText(button)) {
      button.setAttribute('aria-label', 'Back to top');
    }

    button.addEventListener('click', function (event) {
      event.preventDefault();

      win.scrollTo({
        top: 0,
        behavior: RXA11Y.state.reducedMotion ? 'auto' : 'smooth'
      });

      safeFocus(body, {
        preventScroll: true
      });

      announce('Back to top');
    });
  }

  function initDisclosureWidgets() {
    const triggers = qsa('[data-rx-disclosure]');

    triggers.forEach(function (trigger) {
      const targetSelector =
        trigger.getAttribute('data-rx-disclosure') ||
        trigger.getAttribute('aria-controls');

      const target = targetSelector ? qs(targetSelector) : null;

      if (!target) return;

      const targetId = ensureId(target, 'rx-disclosure');

      trigger.setAttribute('aria-controls', targetId);

      if (!trigger.hasAttribute('aria-expanded')) {
        setExpanded(trigger, false);
      }

      if (trigger.getAttribute('aria-expanded') !== 'true') {
        setHidden(target, true);
      }

      trigger.addEventListener('click', function () {
        const expanded = trigger.getAttribute('aria-expanded') === 'true';

        setExpanded(trigger, !expanded);
        setHidden(target, expanded);
      });

      trigger.addEventListener('keydown', function (event) {
        if (event.key === RXA11Y.keys.escape) {
          setExpanded(trigger, false);
          setHidden(target, true);
          safeFocus(trigger);
        }
      });
    });
  }

  function initDetailsSummary() {
    const detailsElements = qsa('details');

    detailsElements.forEach(function (details) {
      const summary = qs('summary', details);

      if (!summary) return;

      if (!summary.getAttribute('role')) {
        summary.setAttribute('role', 'button');
      }

      summary.setAttribute('aria-expanded', details.open ? 'true' : 'false');

      details.addEventListener('toggle', function () {
        summary.setAttribute('aria-expanded', details.open ? 'true' : 'false');
      });
    });
  }

  function initCommentReplyAccessibility() {
    const replyLinks = qsa('.comment-reply-link');

    replyLinks.forEach(function (link) {
      if (!link.getAttribute('aria-label')) {
        const comment = link.closest('.comment');
        const author = comment ? qs('.comment-author, .fn', comment) : null;

        if (author && hasText(author)) {
          link.setAttribute('aria-label', `Reply to ${author.textContent.trim()}`);
        } else {
          link.setAttribute('aria-label', 'Reply to comment');
        }
      }
    });

    const cancelReply = qs('#cancel-comment-reply-link');

    if (cancelReply && !cancelReply.getAttribute('aria-label')) {
      cancelReply.setAttribute('aria-label', 'Cancel comment reply');
    }
  }

  function initPaginationAccessibility() {
    const paginations = qsa('.pagination, .nav-links, .page-numbers');

    paginations.forEach(function (pagination, index) {
      const nav = pagination.closest('nav') || pagination;

      if (!nav.getAttribute('aria-label')) {
        nav.setAttribute('aria-label', index === 0 ? 'Pagination' : `Pagination ${index + 1}`);
      }

      qsa('a, span', pagination).forEach(function (item) {
        if (item.classList.contains('current')) {
          item.setAttribute('aria-current', 'page');
        }
      });
    });
  }

  function initBreadcrumbAccessibility() {
    const breadcrumbs = qsa('.breadcrumb, .breadcrumbs, [data-rx-breadcrumb]');

    breadcrumbs.forEach(function (breadcrumb) {
      if (!breadcrumb.getAttribute('aria-label')) {
        breadcrumb.setAttribute('aria-label', 'Breadcrumb');
      }

      const nav = breadcrumb.closest('nav');

      if (nav && !nav.getAttribute('aria-label')) {
        nav.setAttribute('aria-label', 'Breadcrumb');
      }

      const lastLink = qsa('a', breadcrumb).pop();

      if (lastLink && !lastLink.getAttribute('aria-current')) {
        lastLink.setAttribute('aria-current', 'page');
      }
    });
  }

  function initCardLinks() {
    const cards = qsa('[data-rx-card-link], .post-card, .article-card');

    cards.forEach(function (card) {
      const mainLink = qs('a[href]', card);

      if (!mainLink) return;

      if (!card.getAttribute('tabindex')) {
        card.setAttribute('tabindex', '0');
      }

      if (!card.getAttribute('role')) {
        card.setAttribute('role', 'link');
      }

      if (!card.getAttribute('aria-label')) {
        card.setAttribute('aria-label', mainLink.textContent.trim() || 'Read article');
      }

      card.addEventListener('keydown', function (event) {
        if (event.key === RXA11Y.keys.enter || event.key === RXA11Y.keys.space || event.key === RXA11Y.keys.spaceLegacy) {
          event.preventDefault();
          mainLink.click();
        }
      });
    });
  }

  function initVideoControlsAccessibility() {
    const videos = qsa('video');

    videos.forEach(function (video, index) {
      if (!video.getAttribute('aria-label') && !video.getAttribute('aria-labelledby')) {
        video.setAttribute('aria-label', `Video content ${index + 1}`);
      }

      if (video.autoplay && !video.muted) {
        video.muted = true;
      }

      if (RXA11Y.state.reducedMotion && video.autoplay) {
        video.pause();
        video.removeAttribute('autoplay');
      }
    });
  }

  function initLazyLoadedContentObserver() {
    if (!('MutationObserver' in win)) return;

    const observer = new MutationObserver(function (mutations) {
      let shouldRefresh = false;

      mutations.forEach(function (mutation) {
        if (mutation.addedNodes && mutation.addedNodes.length) {
          shouldRefresh = true;
        }
      });

      if (!shouldRefresh) return;

      win.clearTimeout(initLazyLoadedContentObserver.timer);

      initLazyLoadedContentObserver.timer = win.setTimeout(function () {
        initImages();
        initIframes();
        initExternalLinks();
        initFormAccessibility();
        initTables();
        initCommentReplyAccessibility();
        initPaginationAccessibility();
      }, 250);
    });

    observer.observe(body, {
      childList: true,
      subtree: true
    });
  }

  function initPrintAccessibility() {
    win.addEventListener('beforeprint', function () {
      qsa('[hidden][data-rx-print-show]').forEach(function (element) {
        element.dataset.rxWasHidden = 'true';
        element.removeAttribute('hidden');
      });
    });

    win.addEventListener('afterprint', function () {
      qsa('[data-rx-was-hidden="true"]').forEach(function (element) {
        element.setAttribute('hidden', '');
        delete element.dataset.rxWasHidden;
      });
    });
  }

  function initHashFocusOnLoad() {
    if (!win.location.hash) return;

    win.setTimeout(function () {
      const target = qs(win.location.hash);

      if (!target) return;

      if (!target.hasAttribute('tabindex')) {
        target.setAttribute('tabindex', '-1');
      }

      safeFocus(target, {
        scroll: true
      });
    }, 300);
  }

  function initWpAdminBarOffset() {
    const adminBar = qs('#wpadminbar');

    if (!adminBar) return;

    const height = adminBar.offsetHeight || 32;

    html.style.setProperty('--rx-admin-bar-height', `${height}px`);
  }

  function initAccessibleNamesForButtons() {
    const buttons = qsa('button, [role="button"]');

    buttons.forEach(function (button, index) {
      const hasName =
        hasText(button) ||
        button.getAttribute('aria-label') ||
        button.getAttribute('aria-labelledby') ||
        button.getAttribute('title');

      if (!hasName) {
        button.setAttribute('aria-label', `Button ${index + 1}`);
      }
    });
  }

  function initNoHrefLinks() {
    const links = qsa('a:not([href])');

    links.forEach(function (link) {
      if (!link.getAttribute('role')) {
        link.setAttribute('role', 'button');
      }

      if (!link.getAttribute('tabindex')) {
        link.setAttribute('tabindex', '0');
      }

      link.addEventListener('keydown', function (event) {
        if (event.key === RXA11Y.keys.enter || event.key === RXA11Y.keys.space || event.key === RXA11Y.keys.spaceLegacy) {
          event.preventDefault();
          link.click();
        }
      });
    });
  }

  function initRequiredFieldLabels() {
    const requiredFields = qsa('input[required], textarea[required], select[required]');

    requiredFields.forEach(function (field) {
      field.setAttribute('aria-required', 'true');

      const id = ensureId(field, 'rx-required-field');
      const label = qs(`label[for="${CSS.escape(id)}"]`);

      if (label && !label.querySelector('.rx-required-text')) {
        const requiredText = createScreenReaderText(' required');
        requiredText.classList.add('rx-required-text');
        label.appendChild(requiredText);
      }
    });
  }

  function initContentEditableAccessibility() {
    const editables = qsa('[contenteditable="true"]');

    editables.forEach(function (editable, index) {
      if (!editable.getAttribute('role')) {
        editable.setAttribute('role', 'textbox');
      }

      if (!editable.getAttribute('aria-label') && !editable.getAttribute('aria-labelledby')) {
        editable.setAttribute('aria-label', `Editable text area ${index + 1}`);
      }

      if (!editable.getAttribute('tabindex')) {
        editable.setAttribute('tabindex', '0');
      }
    });
  }

  function initProgressBars() {
    const progressBars = qsa('[data-rx-progress], .progress-bar');

    progressBars.forEach(function (bar) {
      if (!bar.getAttribute('role')) {
        bar.setAttribute('role', 'progressbar');
      }

      const value =
        bar.getAttribute('data-value') ||
        bar.style.width.replace('%', '') ||
        bar.getAttribute('aria-valuenow') ||
        '0';

      bar.setAttribute('aria-valuemin', bar.getAttribute('aria-valuemin') || '0');
      bar.setAttribute('aria-valuemax', bar.getAttribute('aria-valuemax') || '100');
      bar.setAttribute('aria-valuenow', value);
    });
  }

  function initStatusMessages() {
    const statuses = qsa('[data-rx-status], .rx-status, .notice, .alert');

    statuses.forEach(function (status) {
      if (!status.getAttribute('role')) {
        const isError =
          status.classList.contains('error') ||
          status.classList.contains('alert') ||
          status.classList.contains('notice-error');

        status.setAttribute('role', isError ? 'alert' : 'status');
      }

      if (!status.getAttribute('aria-live')) {
        status.setAttribute(
          'aria-live',
          status.getAttribute('role') === 'alert' ? 'assertive' : 'polite'
        );
      }
    });
  }

  function initReadableTimeElements() {
    const times = qsa('time');

    times.forEach(function (time) {
      if (!time.getAttribute('datetime')) {
        const text = time.textContent.trim();

        if (text) {
          time.setAttribute('datetime', text);
        }
      }
    });
  }

  function initCodeBlocks() {
    const codeBlocks = qsa('pre');

    codeBlocks.forEach(function (pre, index) {
      if (!pre.getAttribute('tabindex')) {
        pre.setAttribute('tabindex', '0');
      }

      if (!pre.getAttribute('role')) {
        pre.setAttribute('role', 'region');
      }

      if (!pre.getAttribute('aria-label')) {
        pre.setAttribute('aria-label', `Code block ${index + 1}`);
      }
    });
  }

  function initRxA11YApi() {
    win.RXThemeA11Y = {
      version: RXA11Y.version,
      announce,
      focus: safeFocus,
      getFocusableElements,
      refresh: function () {
        initLandmarks();
        initImages();
        initIframes();
        initExternalLinks();
        initFormAccessibility();
        initTables();
        initAccessibleNamesForButtons();
        initRequiredFieldLabels();
        initStatusMessages();
      },
      openModal: function (selector) {
        const modal = qs(selector);

        if (!modal) return;

        RXA11Y.state.lastFocusedElement = doc.activeElement;
        RXA11Y.state.activeModal = modal;

        setHidden(modal, false);
        body.classList.add(RXA11Y.classNames.modalOpen);

        const focusTarget = getFocusableElements(modal)[0] || modal;

        if (!modal.hasAttribute('tabindex')) {
          modal.setAttribute('tabindex', '-1');
        }

        safeFocus(focusTarget);
      },
      closeModal: function () {
        const modal = RXA11Y.state.activeModal;

        if (!modal) return;

        setHidden(modal, true);
        body.classList.remove(RXA11Y.classNames.modalOpen);

        if (RXA11Y.state.lastFocusedElement) {
          safeFocus(RXA11Y.state.lastFocusedElement);
        }

        RXA11Y.state.activeModal = null;
      }
    };
  }

  function init() {
    if (RXA11Y.state.initialized) return;

    RXA11Y.state.initialized = true;

    initBaseClasses();
    initReducedMotion();
    initInputModeDetection();
    initFocusWithinPolyfill();
    initSkipLinks();
    initLandmarks();
    initMobileMenu();
    initDropdownMenus();
    initModals();
    initAccordions();
    initTabs();
    initSearchForms();
    initFormAccessibility();
    initTables();
    initImages();
    initIframes();
    initExternalLinks();
    initSmoothAnchorFocus();
    initKeyboardShortcuts();
    initAriaCurrentForLinks();
    initBackToTopButton();
    initDisclosureWidgets();
    initDetailsSummary();
    initCommentReplyAccessibility();
    initPaginationAccessibility();
    initBreadcrumbAccessibility();
    initCardLinks();
    initVideoControlsAccessibility();
    initLazyLoadedContentObserver();
    initPrintAccessibility();
    initHashFocusOnLoad();
    initWpAdminBarOffset();
    initAccessibleNamesForButtons();
    initNoHrefLinks();
    initRequiredFieldLabels();
    initContentEditableAccessibility();
    initProgressBars();
    initStatusMessages();
    initReadableTimeElements();
    initCodeBlocks();
    initRxA11YApi();

    announce('Accessibility features loaded');
  }

  if (doc.readyState === 'loading') {
    doc.addEventListener('DOMContentLoaded', init);
  } else {
    init();
  }
})();

Add this enqueue in your RX Theme functions.php or your enqueue file:

function rx_theme_enqueue_accessibility_chunk() {
    wp_enqueue_script(
        'rx-theme-accessibility-chunk',
        get_template_directory_uri() . '/assets/static-js/static-chunks-js/chunk-021-accessibility.js',
        array(),
        '1.0.0',
        true
    );
}
add_action( 'wp_enqueue_scripts', 'rx_theme_enqueue_accessibility_chunk' );

For best result, also add this small CSS:

.screen-reader-text,
.rx-visually-hidden {
  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;
}

.screen-reader-text:focus,
.skip-link:focus {
  position: fixed !important;
  top: calc(12px + var(--rx-admin-bar-height, 0px)) !important;
  left: 12px !important;
  z-index: 999999 !important;
  width: auto !important;
  height: auto !important;
  padding: 12px 18px !important;
  margin: 0 !important;
  overflow: visible !important;
  clip: auto !important;
  white-space: normal !important;
  background: #ffffff !important;
  color: #111111 !important;
  border: 2px solid currentColor !important;
  border-radius: 6px !important;
  text-decoration: none !important;
}

.rx-keyboard-user :focus {
  outline: 3px solid currentColor;
  outline-offset: 3px;
}

.rx-mouse-user :focus {
  outline: none;
}

.rx-reduced-motion *,
.rx-reduced-motion *::before,
.rx-reduced-motion *::after {
  scroll-behavior: auto !important;
  animation-duration: 0.001ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.001ms !important;
}

.rx-modal-open {
  overflow: hidden;
}

[hidden] {
  display: none !important;
}

.rx-table-responsive {
  overflow-x: auto;
}

Best filename is exactly:

chunk-021-accessibility.js

This is a good advanced accessibility chunk for RX Theme. It is large but still safe, clean, and performance-friendly.

Leave a Reply

Your email address will not be published. Required fields are marked *