# Real Keyboard Simulation for Focus Indicator Testing **Date:** 2025-11-20 **Updated:** 2025-12-09 **Status:** ✅ **IMPLEMENTED** (Now the only method - legacy programmatic method removed) --- ## Overview Cremote **always uses real Tab key simulation** for keyboard navigation testing. The legacy programmatic `.focus()` method has been removed because it produces false negatives. ### The Problem **Programmatic `.focus()` cannot trigger `:focus-within` or `:focus-visible` on elements**, causing false negatives for: - Dropdown menus that rely on CSS `:focus-within` pseudo-class - Modern focus indicators using `:focus-visible` pseudo-class - Accessibility plugins that inject universal focus styles ### The Solution **Always use real Tab key presses via Chrome DevTools Protocol** to simulate actual user keyboard navigation, which properly triggers all CSS pseudo-classes including `:focus-within` and `:focus-visible`. --- ## Implementation Details ### New Function: `testKeyboardNavigationWithRealKeys()` Located in `daemon/daemon.go` (lines 10849-11148), this function: 1. **Scans all interactive elements** using JavaScript to identify focusable elements 2. **Focuses the body** to start from a known state 3. **Presses Tab key repeatedly** using `d.performSpecialKey(tabID, "Tab")` 4. **Checks the focused element** after each Tab press 5. **Detects focus indicators** by examining computed styles (outline, box-shadow, border) 6. **Builds tab order** based on actual keyboard navigation flow 7. **Stops when cycling back** to body/document or after finding all expected elements ### Key Advantages ✅ **Triggers `:focus-within`** - Parent elements receive the pseudo-class ✅ **Opens dropdowns automatically** - CSS rules like `.menu-item-has-children:focus-within > .sub-menu` work ✅ **Tests real user experience** - Simulates actual keyboard navigation ✅ **Accurate focus indicators** - Detects indicators on elements inside hidden dropdowns ✅ **Proper tab order** - Follows browser's natural tab navigation flow --- ## API Changes ### Client Function: `TestKeyboardNavigation()` **Current signature:** ```go func (c *Client) TestKeyboardNavigation(tabID string, useRealKeys bool, timeout int) (*KeyboardTestResult, error) ``` **Parameters:** - `tabID` - Tab ID (empty string uses current tab) - `useRealKeys` - **Ignored** (always uses real Tab simulation for accuracy) - `timeout` - Timeout in seconds **Note:** The `useRealKeys` parameter is maintained for backward compatibility but is ignored. All keyboard testing now uses real Tab key simulation. ### Client Function: `GetKeyboardAudit()` **Current signature:** ```go func (c *Client) GetKeyboardAudit(tabID string, checkFocusIndicators, checkTabOrder, checkKeyboardTraps, useRealKeys bool, timeout int) (*KeyboardAuditResult, error) ``` **Parameters:** - `useRealKeys` - **Ignored** (always uses real Tab simulation for accuracy) **Note:** The `useRealKeys` parameter is maintained for backward compatibility but is ignored. --- ## MCP Tools Updated ### `web_keyboard_test_cremotemcp` **Parameters:** - `tab` (string, optional) - Tab ID - `timeout` (integer, default: 15) - Timeout in seconds **Note:** The `use_real_keys` parameter has been removed. Real Tab key simulation is always used. **Example:** ```json { "tool": "web_keyboard_test_cremotemcp", "arguments": { "timeout": 15 } } ``` ### `web_keyboard_audit_cremotemcp` **Parameters:** - `tab` (string, optional) - Tab ID - `check_focus_indicators` (boolean, default: true) - `check_tab_order` (boolean, default: true) - `check_keyboard_traps` (boolean, default: true) - `timeout` (integer, default: 15) - Timeout in seconds **Note:** The `use_real_keys` parameter has been removed. Real Tab key simulation is always used. **Example:** ```json { "tool": "web_keyboard_audit_cremotemcp", "arguments": { "check_focus_indicators": true, "timeout": 15 } } ``` --- ## Backward Compatibility ⚠️ **Breaking Change (Simplified):** - The `use_real_keys` parameter has been removed from MCP tools - Client functions still accept the parameter for backward compatibility but ignore it - **All keyboard testing now uses real Tab key simulation** for accurate results - Legacy programmatic `.focus()` method has been removed **Rationale:** The programmatic method produced false negatives for `:focus-visible` and `:focus-within`, making it unreliable for accessibility testing. --- ## Testing Recommendations ### For Dropdown Menus ```json { "tool": "web_keyboard_audit_cremotemcp", "arguments": { "check_focus_indicators": true } } ``` ### For Standard Pages ```json { "tool": "web_keyboard_test_cremotemcp", "arguments": { "timeout": 15 } } ``` ### Legacy Testing (if needed) ```json { "tool": "web_keyboard_test_cremotemcp", "arguments": { "use_real_keys": false } } ``` --- ## Expected Results ### Before (Programmatic Focus) - ❌ 29.2% pass rate on pages with dropdown menus - ❌ False negatives for elements in hidden dropdowns - ❌ `:focus-within` not triggered ### After (Real Tab Simulation) - ✅ ~49%+ pass rate on pages with dropdown menus - ✅ Accurate detection of focus indicators - ✅ `:focus-within` properly triggered - ✅ Dropdowns open automatically during testing --- ## Performance Considerations - **Slightly slower** than programmatic focus (adds ~50ms per element for Tab press + style check) - **More accurate** results justify the small performance trade-off - **Timeout increased** to 15 seconds by default to accommodate the additional time - **Safety limits** in place (max 200 Tab presses to prevent infinite loops) --- ## Next Steps 1. ✅ Implementation complete 2. ⏳ Test on pages with dropdown menus 3. ⏳ Update documentation 4. ⏳ Deploy to production --- ## Related Documents - `docs/FOCUS_INDICATORS_VALIDATION_SUCCESS.md` - Original issue identification - `mcp/LLM_USAGE_GUIDE.md` - MCP tool usage guide - `docs/ADA_TESTING_GUIDE.md` - Accessibility testing guide