keyboard users

Add keyboard support and documentation for iPad users
This commit is contained in:
2025-06-01 12:27:21 -04:00
parent 5ad6ef495a
commit 4a080096f3
2 changed files with 612 additions and 0 deletions
+165
View File
@@ -0,0 +1,165 @@
# iPad Keyboard Support Improvements
This document outlines the enhancements made to improve the sidebar display and overall user experience for iPad users with keyboards attached.
## Overview
The KJV Study website now provides enhanced support for iPad users with keyboards, offering a desktop-like experience while maintaining touch-friendly interactions. These improvements bridge the gap between mobile and desktop experiences for tablet users.
## Key Improvements
### 1. Enhanced Device Detection
- **Smart Input Method Detection**: The application now detects when users are on iPads or tablets with keyboards
- **Hybrid Touch/Keyboard Support**: Optimized for users who switch between touch and keyboard input
- **Responsive Breakpoints**: Specific breakpoints for different iPad models and orientations
### 2. Improved Sidebar Behavior
#### iPad-Specific Sidebar Features:
- **Always Visible**: Sidebar remains visible by default on iPad screens (768px+)
- **Proper Sizing**: Dynamic width adjustment based on screen size:
- iPad mini/standard: 260-280px
- iPad Pro: 300-320px
- **Smooth Transitions**: Hardware-accelerated animations for better performance
- **Orientation Support**: Automatic adjustments for portrait/landscape changes
#### Enhanced Layout:
```css
/* Example: iPad Pro landscape */
@media (min-width: 1024px) and (max-width: 1366px) and (orientation: landscape) {
.sidebar { width: 320px; }
.main-content { margin-left: 320px; width: calc(100% - 320px); }
}
```
### 3. Keyboard Navigation Enhancements
#### New Keyboard Shortcuts:
- **⌘/Ctrl + B**: Toggle sidebar (iPad/tablet only)
- **⌘/Ctrl + K**: Focus search input
- **⌘/Ctrl + 1-9**: Quick jump to navigation items
- **⌘/Ctrl + Home**: Scroll to top
- **⌘/Ctrl + End**: Scroll to bottom
- **Arrow Keys**: Navigate within sidebar (Up/Down)
- **Tab**: Enhanced focus management
- **Enter**: Activate focused links
- **Escape**: Close modals/remove focus
#### Focus Management:
- **Visible Focus Indicators**: Clear outline styles for keyboard users
- **Focus Trapping**: Proper tab order and focus management
- **Scroll Into View**: Focused elements automatically scroll into view
### 4. Touch Target Optimization
Enhanced touch targets that work well with both touch and keyboard:
- **Minimum 48px height** for all interactive elements
- **Increased padding** for better touch accuracy
- **Improved spacing** between clickable areas
- **Visual feedback** for both hover and focus states
### 5. CSS Media Query Strategy
The implementation uses a progressive enhancement approach:
```css
/* Base mobile styles */
@media (max-width: 767px) { /* Mobile phone specific */ }
/* iPad portrait */
@media (min-width: 768px) and (max-width: 834px) and (orientation: portrait) { }
/* iPad landscape / small tablets */
@media (min-width: 769px) and (max-width: 1024px) { }
/* iPad Pro landscape */
@media (min-width: 1024px) and (max-width: 1366px) and (orientation: landscape) { }
/* Desktop */
@media (min-width: 1367px) { }
```
## Technical Implementation
### JavaScript Features
1. **Device Detection Function**:
```javascript
function detectInputMethod() {
const isIPad = /iPad|iPhone|iPod/.test(navigator.userAgent) ||
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
const hasKeyboard = window.innerWidth >= 768 && window.innerHeight >= 600;
const isTablet = window.innerWidth >= 768 && window.innerWidth <= 1366;
return { isIPad, hasKeyboard, isTablet, isMobile, isDesktop };
}
```
2. **Enhanced Sidebar Toggle**: Intelligent behavior based on device type
3. **Keyboard Event Handling**: Comprehensive keyboard shortcut system
4. **Orientation Change Support**: Automatic layout adjustments
### CSS Classes
- `.ipad-optimized`: Applied to body for iPad-specific styles
- `.keyboard-available`: Added when keyboard input is detected
- `.keyboard-navigation`: Added during keyboard navigation for enhanced focus styles
## Browser Support
- **Safari on iPad**: Full support with hardware acceleration
- **Chrome on iPad**: Complete functionality
- **Firefox on iPad**: Full keyboard navigation support
- **Edge on iPad**: Complete feature set
## Performance Considerations
- **Hardware Acceleration**: Uses `transform3d` for smooth animations
- **Efficient Transitions**: Minimal repaints and reflows
- **Touch Scrolling**: Optimized `-webkit-overflow-scrolling: touch`
- **Memory Management**: Event listeners properly managed
## Accessibility Features
- **High Contrast Support**: Enhanced visibility in high contrast mode
- **Reduced Motion**: Respects `prefers-reduced-motion` setting
- **Screen Reader Friendly**: Proper ARIA attributes and semantic HTML
- **Keyboard Only Navigation**: Complete functionality without mouse/touch
## Testing Scenarios
### Recommended Test Cases:
1. **iPad with Magic Keyboard**: Test all keyboard shortcuts and sidebar behavior
2. **iPad with Smart Keyboard**: Verify layout and touch targets
3. **iPad Pro 12.9"**: Confirm optimal spacing and sidebar width
4. **Portrait/Landscape Transitions**: Test orientation change handling
5. **Touch + Keyboard Mixed Use**: Verify seamless switching between input methods
## Future Enhancements
Potential areas for future improvement:
- **Split View Support**: Better handling of iPad split-screen mode
- **Apple Pencil Integration**: Enhanced interaction for Pencil users
- **Voice Control**: Improved compatibility with iPad voice control
- **Shortcuts App**: Integration with iOS Shortcuts app
## Troubleshooting
### Common Issues:
1. **Sidebar Not Visible**: Check if device detection is working correctly
2. **Keyboard Shortcuts Not Working**: Verify focus is not trapped in iframe or input
3. **Layout Issues**: Check for conflicting CSS rules in custom themes
### Debug Tools:
Add this to console to check device detection:
```javascript
console.log(detectInputMethod());
```
## Conclusion
These improvements provide iPad users with a sophisticated, keyboard-friendly interface that maintains the intuitive touch experience while adding powerful keyboard navigation capabilities. The implementation ensures a smooth, responsive experience across all iPad models and orientations.
+447
View File
@@ -0,0 +1,447 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>iPad Keyboard Test - KJV Study</title>
<link href="/static/style.css" rel="stylesheet">
<style>
body {
font-family: 'Crimson Text', 'Times New Roman', serif;
margin: 0;
padding: 0;
background-color: var(--background-color, #f8f9fa);
}
.test-info {
position: fixed;
top: 10px;
right: 10px;
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 1rem;
border-radius: 8px;
font-size: 0.875rem;
z-index: 1000;
max-width: 300px;
}
.test-info h3 {
margin: 0 0 0.5rem 0;
color: #4CAF50;
}
.device-info {
margin-bottom: 1rem;
padding: 0.5rem;
background: rgba(255, 255, 255, 0.1);
border-radius: 4px;
}
.keyboard-shortcuts {
background: rgba(255, 255, 255, 0.1);
padding: 0.5rem;
border-radius: 4px;
margin-top: 0.5rem;
}
.keyboard-shortcuts h4 {
margin: 0 0 0.5rem 0;
color: #FFC107;
}
.shortcut {
display: flex;
justify-content: space-between;
margin-bottom: 0.25rem;
font-size: 0.8rem;
}
.main-test-content {
margin-left: 220px;
padding: 2rem;
min-height: 100vh;
}
.test-section {
background: white;
padding: 1.5rem;
margin-bottom: 1.5rem;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.test-button {
background: #007bff;
color: white;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 4px;
cursor: pointer;
margin-right: 1rem;
margin-bottom: 0.5rem;
font-size: 1rem;
}
.test-button:hover, .test-button:focus {
background: #0056b3;
outline: 2px solid #007bff;
outline-offset: 2px;
}
.test-input {
width: 100%;
padding: 0.75rem;
border: 2px solid #ddd;
border-radius: 4px;
font-size: 1rem;
margin-bottom: 1rem;
}
.test-input:focus {
border-color: #007bff;
outline: 2px solid rgba(0, 123, 255, 0.25);
outline-offset: 2px;
}
.status {
padding: 0.5rem;
border-radius: 4px;
margin-top: 1rem;
}
.status.success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.status.info {
background: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
@media (max-width: 767px) {
.test-info {
position: relative;
top: auto;
right: auto;
margin: 1rem;
max-width: none;
}
.main-test-content {
margin-left: 0;
padding: 1rem;
}
}
</style>
</head>
<body>
<!-- Sidebar (copied from base template structure) -->
<div class="layout">
<aside class="sidebar" id="sidebar">
<div class="sidebar-header">
<h1 class="sidebar-title">
<span>📖</span>
KJV Study Test
</h1>
<p class="sidebar-subtitle">iPad Keyboard Testing</p>
</div>
<nav class="sidebar-nav">
<h3>Navigation Test</h3>
<a href="#test1" id="nav1" tabindex="0">📚 Test Section 1</a>
<a href="#test2" id="nav2" tabindex="0">🔍 Test Section 2</a>
<a href="#test3" id="nav3" tabindex="0">📖 Test Section 3</a>
<a href="#test4" id="nav4" tabindex="0">✨ Test Section 4</a>
<a href="#test5" id="nav5" tabindex="0">🗺️ Test Section 5</a>
<h3>Keyboard Test Items</h3>
<a href="#keyboard1" tabindex="0">⌨️ Keyboard Test 1</a>
<a href="#keyboard2" tabindex="0">⌨️ Keyboard Test 2</a>
<a href="#keyboard3" tabindex="0">⌨️ Keyboard Test 3</a>
</nav>
</aside>
<main class="main-test-content">
<div class="test-info">
<h3>iPad Keyboard Test</h3>
<div class="device-info" id="deviceInfo">
<strong>Device Detection:</strong><br>
<span id="deviceDetails">Loading...</span>
</div>
<div class="keyboard-shortcuts">
<h4>Test These Shortcuts:</h4>
<div class="shortcut">
<span>⌘/Ctrl + B</span>
<span>Toggle Sidebar</span>
</div>
<div class="shortcut">
<span>⌘/Ctrl + K</span>
<span>Focus Search</span>
</div>
<div class="shortcut">
<span>Tab</span>
<span>Navigate Elements</span>
</div>
<div class="shortcut">
<span>↑/↓ in Sidebar</span>
<span>Navigate Links</span>
</div>
<div class="shortcut">
<span>⌘/Ctrl + 1-5</span>
<span>Quick Jump</span>
</div>
</div>
</div>
<div class="test-section" id="test1">
<h2>Test Section 1: Focus Management</h2>
<p>Test keyboard navigation and focus indicators:</p>
<button class="test-button" onclick="testFocus()" tabindex="0">Focus Test Button 1</button>
<button class="test-button" onclick="testFocus()" tabindex="0">Focus Test Button 2</button>
<button class="test-button" onclick="testFocus()" tabindex="0">Focus Test Button 3</button>
<input type="text" class="test-input" placeholder="Test input field - try Cmd+K" id="testSearch">
<div id="focusStatus" class="status info">Use Tab to navigate, observe focus indicators</div>
</div>
<div class="test-section" id="test2">
<h2>Test Section 2: Sidebar Integration</h2>
<p>Test sidebar behavior and keyboard shortcuts:</p>
<button class="test-button" onclick="testSidebar()" tabindex="0">Toggle Sidebar (Cmd+B)</button>
<button class="test-button" onclick="testSidebarNav()" tabindex="0">Focus First Sidebar Link</button>
<div id="sidebarStatus" class="status info">Try keyboard shortcuts and sidebar navigation</div>
</div>
<div class="test-section" id="test3">
<h2>Test Section 3: Touch vs Keyboard</h2>
<p>Test hybrid touch and keyboard interactions:</p>
<button class="test-button" onclick="simulateTouch()" tabindex="0">Simulate Touch Interaction</button>
<button class="test-button" onclick="simulateKeyboard()" tabindex="0">Simulate Keyboard Interaction</button>
<div id="interactionStatus" class="status info">Test both touch and keyboard on the same elements</div>
</div>
<div class="test-section" id="test4">
<h2>Test Section 4: Responsive Behavior</h2>
<p>Test responsive breakpoints and orientations:</p>
<button class="test-button" onclick="checkBreakpoint()" tabindex="0">Check Current Breakpoint</button>
<button class="test-button" onclick="simulateOrientation()" tabindex="0">Test Orientation Change</button>
<div id="responsiveStatus" class="status info">Rotate device or resize window to test</div>
</div>
<div class="test-section" id="test5">
<h2>Test Section 5: Accessibility</h2>
<p>Test accessibility features and high contrast:</p>
<button class="test-button" onclick="toggleHighContrast()" tabindex="0">Toggle High Contrast</button>
<button class="test-button" onclick="testScreenReader()" tabindex="0">Test Screen Reader</button>
<div id="accessibilityStatus" class="status info">Test with screen readers and accessibility tools</div>
</div>
</main>
</div>
<script>
// Device detection function (same as implemented)
function detectInputMethod() {
const isIPad = /iPad|iPhone|iPod/.test(navigator.userAgent) ||
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
const hasKeyboard = window.innerWidth >= 768 && window.innerHeight >= 600;
const isTablet = window.innerWidth >= 768 && window.innerWidth <= 1366;
return {
isIPad,
hasKeyboard,
isTablet,
isMobile: window.innerWidth <= 767,
isDesktop: window.innerWidth >= 1367
};
}
// Initialize device info
function updateDeviceInfo() {
const device = detectInputMethod();
const details = `
iPad: ${device.isIPad}<br>
Keyboard: ${device.hasKeyboard}<br>
Tablet: ${device.isTablet}<br>
Mobile: ${device.isMobile}<br>
Desktop: ${device.isDesktop}<br>
Screen: ${window.innerWidth}x${window.innerHeight}
`;
document.getElementById('deviceDetails').innerHTML = details;
}
// Test functions
function testFocus() {
document.getElementById('focusStatus').innerHTML = '<strong>Focus test activated!</strong> Button was clicked/focused.';
document.getElementById('focusStatus').className = 'status success';
}
function testSidebar() {
if (window.toggleSidebar) {
window.toggleSidebar();
document.getElementById('sidebarStatus').innerHTML = '<strong>Sidebar toggled!</strong> Function executed.';
} else {
document.getElementById('sidebarStatus').innerHTML = '<strong>Sidebar function not found.</strong> Check implementation.';
}
document.getElementById('sidebarStatus').className = 'status success';
}
function testSidebarNav() {
const firstLink = document.querySelector('.sidebar-nav a');
if (firstLink) {
firstLink.focus();
document.getElementById('sidebarStatus').innerHTML = '<strong>Focused first sidebar link!</strong> Use arrow keys to navigate.';
document.getElementById('sidebarStatus').className = 'status success';
}
}
function simulateTouch() {
document.body.classList.remove('keyboard-navigation');
document.getElementById('interactionStatus').innerHTML = '<strong>Touch mode activated!</strong> Focus indicators should be minimal.';
document.getElementById('interactionStatus').className = 'status success';
}
function simulateKeyboard() {
document.body.classList.add('keyboard-navigation');
document.getElementById('interactionStatus').innerHTML = '<strong>Keyboard mode activated!</strong> Focus indicators should be enhanced.';
document.getElementById('interactionStatus').className = 'status success';
}
function checkBreakpoint() {
const device = detectInputMethod();
let breakpoint = 'Unknown';
if (device.isMobile) breakpoint = 'Mobile';
else if (device.isTablet) breakpoint = 'Tablet';
else if (device.isDesktop) breakpoint = 'Desktop';
document.getElementById('responsiveStatus').innerHTML = `<strong>Current breakpoint: ${breakpoint}</strong><br>Window: ${window.innerWidth}x${window.innerHeight}`;
document.getElementById('responsiveStatus').className = 'status success';
}
function simulateOrientation() {
// Simulate orientation change by updating device info
setTimeout(() => {
updateDeviceInfo();
document.getElementById('responsiveStatus').innerHTML = '<strong>Orientation change simulated!</strong> Device info updated.';
document.getElementById('responsiveStatus').className = 'status success';
}, 100);
}
function toggleHighContrast() {
document.body.style.filter = document.body.style.filter ? '' : 'contrast(2) brightness(1.5)';
document.getElementById('accessibilityStatus').innerHTML = '<strong>High contrast toggled!</strong> Focus indicators should adapt.';
document.getElementById('accessibilityStatus').className = 'status success';
}
function testScreenReader() {
const announcement = document.createElement('div');
announcement.setAttribute('aria-live', 'polite');
announcement.style.position = 'absolute';
announcement.style.left = '-10000px';
announcement.textContent = 'Screen reader test: iPad keyboard improvements are active.';
document.body.appendChild(announcement);
document.getElementById('accessibilityStatus').innerHTML = '<strong>Screen reader test activated!</strong> Check for announcements.';
document.getElementById('accessibilityStatus').className = 'status success';
setTimeout(() => document.body.removeChild(announcement), 3000);
}
// Enhanced keyboard shortcuts for testing
document.addEventListener('keydown', (e) => {
const device = detectInputMethod();
// Cmd/Ctrl + K for search focus
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
e.preventDefault();
const searchInput = document.getElementById('testSearch');
if (searchInput) {
searchInput.focus();
searchInput.select();
document.getElementById('focusStatus').innerHTML = '<strong>Search focused via keyboard!</strong> Cmd+K shortcut worked.';
document.getElementById('focusStatus').className = 'status success';
}
}
// Cmd/Ctrl + B for sidebar toggle (tablet/iPad only)
if ((e.ctrlKey || e.metaKey) && e.key === 'b' && (device.isTablet || device.isIPad)) {
e.preventDefault();
testSidebar();
}
// Cmd/Ctrl + 1-5 for quick navigation
if ((e.ctrlKey || e.metaKey) && e.key >= '1' && e.key <= '5') {
e.preventDefault();
const index = parseInt(e.key) - 1;
const navLinks = document.querySelectorAll('.sidebar-nav a');
if (navLinks[index]) {
navLinks[index].focus();
document.getElementById('sidebarStatus').innerHTML = `<strong>Quick jump to nav item ${e.key}!</strong> Shortcut worked.`;
document.getElementById('sidebarStatus').className = 'status success';
}
}
// Tab navigation detection
if (e.key === 'Tab') {
document.body.classList.add('keyboard-navigation');
}
// Arrow key navigation in sidebar
if (e.target.closest('.sidebar-nav')) {
if (e.key === 'ArrowDown') {
e.preventDefault();
let next = e.target.nextElementSibling;
while (next && next.tagName !== 'A') {
next = next.nextElementSibling;
}
if (next) {
next.focus();
document.getElementById('sidebarStatus').innerHTML = '<strong>Arrow navigation worked!</strong> Down arrow moved focus.';
document.getElementById('sidebarStatus').className = 'status success';
}
}
if (e.key === 'ArrowUp') {
e.preventDefault();
let prev = e.target.previousElementSibling;
while (prev && prev.tagName !== 'A') {
prev = prev.previousElementSibling;
}
if (prev) {
prev.focus();
document.getElementById('sidebarStatus').innerHTML = '<strong>Arrow navigation worked!</strong> Up arrow moved focus.';
document.getElementById('sidebarStatus').className = 'status success';
}
}
}
});
// Remove keyboard navigation class on mouse use
document.addEventListener('mousedown', () => {
document.body.classList.remove('keyboard-navigation');
});
// Initialize
document.addEventListener('DOMContentLoaded', () => {
updateDeviceInfo();
// Add iPad optimization classes
const device = detectInputMethod();
if (device.isTablet || device.isIPad) {
document.body.classList.add('ipad-optimized');
if (device.hasKeyboard) {
document.body.classList.add('keyboard-available');
}
}
});
// Update on resize/orientation change
window.addEventListener('resize', updateDeviceInfo);
window.addEventListener('orientationchange', () => {
setTimeout(updateDeviceInfo, 100);
});
</script>
</body>
</html>