Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .Jules/palette.md
Original file line number Diff line number Diff line change
Expand Up @@ -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., `<main>`) 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 `<body>` tag. Set the main content container with an `id` and explicitly `tabindex="-1"`.

## 2024-07-03 - Anchor Tags as Interactive Buttons
**Learning:** When using `<a>` 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.
2 changes: 1 addition & 1 deletion concepts.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ <h1>CHARLES WILLIAMS</h1>
<a href="products.html">Products</a>
<a href="concepts.html" class="active" aria-current="page">Concepts</a>
<a href="contact.html">Contact</a>
<a href="#" id="sitrep-btn">OSINT</a>
<a href="#" id="sitrep-btn" role="button" aria-haspopup="dialog">OSINT</a>
</nav>
<div id="local-info" class="local-info-widget"></div>
</div>
Expand Down
3 changes: 2 additions & 1 deletion contact.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ <h1>CHARLES WILLIAMS</h1>
<a href="products.html">Products</a>
<a href="concepts.html">Concepts</a>
<a href="contact.html" class="active" aria-current="page">Contact</a>
<a href="#" id="sitrep-btn" role="button" aria-haspopup="dialog">OSINT</a>

</nav>
<div id="local-info" class="local-info-widget"></div>
Expand Down Expand Up @@ -49,7 +50,7 @@ <h2>Get in Touch</h2>
</a>


<a href="#" id="sitrep-btn" class="button">
<a href="#" id="sitrep-btn" class="button" role="button" aria-haspopup="dialog">
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-activity"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline></svg>
OSINT
</a>
Expand Down
1 change: 1 addition & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ <h1>CHARLES WILLIAMS Consulting</h1>
<a href="products.html">Products</a>
<a href="concepts.html">Concepts</a>
<a href="contact.html">Contact</a>
<a href="#" id="sitrep-btn" role="button" aria-haspopup="dialog">OSINT</a>

</nav>
<div id="local-info" class="local-info-widget"></div>
Expand Down
6 changes: 6 additions & 0 deletions modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion products.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ <h1>CHARLES WILLIAMS</h1>
<a href="products.html" class="active" aria-current="page">Products</a>
<a href="concepts.html">Concepts</a>
<a href="contact.html">Contact</a>
<a href="#" id="sitrep-btn">OSINT</a>
<a href="#" id="sitrep-btn" role="button" aria-haspopup="dialog">OSINT</a>
</nav>
<div id="local-info" class="local-info-widget"></div>
</div>
Expand Down
23 changes: 23 additions & 0 deletions test_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down