From 6c6585e3405c8dead4fa5d905854764ad3f8c596 Mon Sep 17 00:00:00 2001 From: 0m364 <13804150+0m364@users.noreply.github.com> Date: Fri, 3 Jul 2026 11:55:11 +0000 Subject: [PATCH] feat: add semantic roles and spacebar support to sitrep anchor buttons --- .Jules/palette.md | 4 ++++ concepts.html | 2 +- contact.html | 3 ++- index.html | 1 + modal.js | 6 ++++++ products.html | 2 +- test_ui.py | 23 +++++++++++++++++++++++ 7 files changed, 38 insertions(+), 3 deletions(-) diff --git a/.Jules/palette.md b/.Jules/palette.md index 7c7c523..84eabc3 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -29,3 +29,7 @@ ## 2026-06-12 - Robust Skip-to-Content Links **Learning:** Adding a "Skip to main content" link is a critical accessibility requirement for keyboard users. To make it robust, it needs to be hidden visually but appear when focused using `transform: translateY(-100%)` instead of magic pixel numbers, and the target container (e.g., `
`) must have a matching `id` and `tabindex="-1"` so it can programmatically receive focus across all browsers. **Action:** Always include a visually hidden 'Skip to main content' link immediately after the opening `` tag. Set the main content container with an `id` and explicitly `tabindex="-1"`. + +## 2024-07-03 - Anchor Tags as Interactive Buttons +**Learning:** When using `` tags with `href="#"` as buttons (e.g., to open a modal), native HTML semantics are lost. Screen readers announce them as links, and native keyboard support only triggers on the `Enter` key, whereas standard buttons also respond to the `Spacebar`. +**Action:** When a link functions strictly as a UI trigger, explicitly add `role="button"` and context-specific ARIA attributes like `aria-haspopup="dialog"`. Additionally, you must manually bind a `keydown` event listener for the `Spacebar` key (and use `e.preventDefault()` to prevent scrolling) to fully replicate native button accessibility. diff --git a/concepts.html b/concepts.html index d4e1807..13b8873 100644 --- a/concepts.html +++ b/concepts.html @@ -19,7 +19,7 @@

CHARLES WILLIAMS

Products Concepts Contact - OSINT + OSINT
diff --git a/contact.html b/contact.html index b30b503..9451ede 100644 --- a/contact.html +++ b/contact.html @@ -19,6 +19,7 @@

CHARLES WILLIAMS

Products Concepts Contact + OSINT
@@ -49,7 +50,7 @@

Get in Touch

- + OSINT diff --git a/index.html b/index.html index d48f2f0..f3a0ac1 100644 --- a/index.html +++ b/index.html @@ -19,6 +19,7 @@

CHARLES WILLIAMS Consulting

Products Concepts Contact + OSINT
diff --git a/modal.js b/modal.js index 994d6bd..7ad7852 100644 --- a/modal.js +++ b/modal.js @@ -56,6 +56,12 @@ const openModal = (e) => { const btns = document.querySelectorAll('#sitrep-btn'); btns.forEach(btn => { btn.addEventListener('click', openModal); + btn.addEventListener('keydown', (e) => { + if (e.key === ' ' || e.key === 'Spacebar') { + e.preventDefault(); + openModal(e); + } + }); }); // Close modal via button diff --git a/products.html b/products.html index f5f0835..01f1a6c 100644 --- a/products.html +++ b/products.html @@ -20,7 +20,7 @@

CHARLES WILLIAMS

Products Concepts Contact - OSINT + OSINT
diff --git a/test_ui.py b/test_ui.py index 40be212..76e4a07 100644 --- a/test_ui.py +++ b/test_ui.py @@ -39,6 +39,29 @@ async def run(): await page.screenshot(path="/home/jules/verification/screenshots/error_state.png") await page.wait_for_timeout(1000) + print("Testing keyboard accessibility for sitrep-btn...") + await page.goto('http://127.0.0.1:8000/contact.html', wait_until='domcontentloaded') + + # Focus the button and press spacebar + btn = page.locator('#sitrep-btn').first + await btn.focus() + await btn.press(' ') + + # Verify the modal is open by checking if it is visible + modal = page.locator('#sitrep-modal') + try: + await modal.wait_for(state='visible', timeout=3000) + print("✅ Modal opened via spacebar.") + except Exception as e: + print("❌ Failed to open modal via spacebar.", e) + + # Verify focus is inside the modal + focused_element = await page.evaluate('document.activeElement.id') + if focused_element == 'sitrep-modal' or await page.evaluate('document.activeElement.classList.contains("sitrep-modal-content")') or focused_element == 'close-sitrep': + print("✅ Focus successfully moved into the modal.") + else: + print(f"❌ Focus did not move properly. Currently focused element ID: {focused_element}") + await context.close() await browser.close()